Rails' batteries-included approach is one of its greatest assets. No other framework makes it so effortless to get your application off the ground quickly, at least partially due to Rails' generators.
If you've used Rails for any amount of time, you have come across generators. Need to create a new application? Run rails new
. Need to scaffold a bunch of new models and views? Run rails generate scaffold
. There are dozens more available to help you get started rapidly or streamline your workflow.
But sometimes, using generators is just not enough. You might want to customize the behavior of these commands or even create your own. In this article, we'll take a closer look at generators - in particular, how to create your own custom Rails application using templates.
Let's get started!
What Are Rails Generators?
Not to be confused with generator functions (which you might be familiar with from Python or Javascript), Rails generators are custom Thor commands that focus on, well, generating things.
There are lots of examples. You'll likely be familiar with the model generator (rails generate model
) for creating new ActiveRecord models or the migration generator (rails generate migration
) for generating new migrations. There is also rails generate generator
which — you guessed it — creates a new generator!
Generators can call each other — for example, rails scaffold
will call numerous other generators — and provide methods to create or modify files, install gems, run specific rake tasks, and much more. Let's create a simple model spec generator to understand how this works.
Creating Your Own Generator in Ruby on Rails
Run the following:
rails generate generator model_spec
This will create several new files in /lib/generators/model_spec
. We can modify model_spec_generator.rb
in folder lib/generators/model_spec/
to create a model spec file in the correct directory:
class ModelSpecGenerator < Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
def create_model_spec
template_file = File.join('spec/models', class_path, "#{file_name}_spec.rb")
template 'model_spec.rb.erb', template_file
end
end
The template
command will look for a template file in the lib/generators/model_spec/templates
directory and render it to the specified location — the spec/models
directory. The command will replace ERB-style variables found in the template file.
By setting the source_root
, we let our generator know where it can find referenced template files. Template model_spec.rb
in folder lib/generators/model_spec/templates/
could look like this:
require 'rails_helper'
RSpec.describe <%= class_name %>, :model
pending "add some examples to (or delete) #{__FILE__}"
end
Once you have created that file, you can run the generator to create a new spec file.
rails generate model_spec mymodel
Many gems ship with generators such as this one. In fact, we created a simplified version of the generator Rspec ships with. FactoryBot has a generator for factories. There are many more examples.
The generators in various gems are more sophisticated than the one we created. We could make our generator take arguments or even hook it into existing generators such as rails scaffold
. Refer to the Rails generators documentation if you want to learn more.
So generators have the potential to simplify your workflow in an existing application. But can we also use generators to customize setting up a new application?
Enter templates!
Templates in Ruby on Rails
As the name suggests, templates are files for customizing your application setup. Don't confuse these with the template files that we previously discussed!
Under the hood, they are just generators with a specific purpose, as evidenced by the template API. While not exactly identical to generators, they are very similar.
If you have an existing template file, you can use it like so:
rails new myapp -m mytemplate.rb
Rather than specifying a local file, you may also specify a URL. This is especially useful as it allows you to share application templates.
rails new myapp -m https://gist.github.com/appsignal/12345/raw/
You are not limited to using templates when running rails new
either. If you've already set up an app, you can apply templates afterward by executing:
rails app:template LOCATION=http://example.com/template.rb
Templates can be extremely useful. Who doesn't want to automate adding the same couple of gems and making the same configuration changes every time they create a new app? Creating your own application template is great fun — even if it doesn't save a lot of time in the long run.
Creating Your Own Template in Rails
We now know about generators and how to use templates. Let's create a simple application template to automate some setup steps.
How you set up your Rails app is very much down to personal preference, but here's an example:
- Install the dotenv gem.
- Create a
.env.development
file for the development environment. - Adapt the database configuration file to use environment variables.
- Optionally install and set up Rspec.
Let's create a local file — mytemplate.rb
— and add dotenv
using the gem
command.
gem 'dotenv-rails', groups: [:development, :test]
As we've just added the dotenv gem, let's also create a .env.development
file to contain our database configuration.
You can create a new file with specific content by using create_file
. You won't find this in the template or generator documentation, as the method is supplied by Thor. You might also come across the alias file
. Application templates are evaluated in the context of Rails::Generators::AppGenerator
, and that's exactly where the file
alias is defined.
create_file '.env.development', <<~TXT
DATABASE_HOST=localhost
DATABASE_USERNAME=#{app_name}
DATABASE_PASSWORD=#{app_name}
TXT
The app_name
variable contains the first argument of rails new
. Using this variable, we can ensure our config file matches the generated application.
Next, let's use our environment variables to connect to the database. We could overwrite the entire config/database.yml
using the create_file
command, but let's modify it instead using inject_into_file
.
inject_into_file 'config/database.yml', after: /database: #{app_name}_development\n/ do <<-RUBY
host: <%= ENV['DATABASE_HOST'] %>
username: <%= ENV['DATABASE_USERNAME'] %>
password: <%= ENV['DATABASE_PASSWORD'] %>
RUBY
end
We can use both strings or regex with the after
argument to specify where to inject content.
Of course, using this kind of configuration only makes sense if a user isn't creating an application with SQLite. You can check for the presence of certain arguments by using the options
variable. It's best you read the source code of Rails' app generator to see which options are available.
if options[:database] != 'sqlite3'
# Set up env vars and db configuration
end
Last but not least, let's also allow users to install Rspec if they want to. There are various methods for taking user input and creating interactive templates. The yes?
method asks a user for confirmation:
if yes?('Would you like to install Rspec?')
gem 'rspec-rails', group: :test
after_bundle { generate 'rspec:install' }
end
We already know the gem
method, but generate
and after_bundle
are new.
As mentioned before, Rspec adds its own generators, and you can call these generators (or any other ones, for that matter) directly from your template. But there is a catch — gems specified with the gem
method are only installed at the end of the template. Calling generate
with a generator supplied by such a gem would fail — which is why you should register the command as a callback with after_bundle
.
Note: Before we wrap up, a quick word about creating or modifying files. We used create_file
and inject_into_file
, but there are many other options. You may come across copy_file
or template
when reading different templates. I did not mention them here to keep things simple. If you want to create more advanced templates, you should know that these other methods for dealing with files exist.
The Result: The Final Template in Rails
The final template should look like this:
# Install dotenv
gem 'dotenv-rails', groups: [:development, :test]
if options[:database] != 'sqlite3'
# Set up .env.development
create_file '.env.development', <<~TXT
DATABASE_HOST=localhost
DATABASE_USERNAME=#{app_name}
DATABASE_PASSWORD=#{app_name}
TXT
# Modify database.yml
inject_into_file 'config/database.yml', after: /database: #{app_name}_development\n/ do <<-RUBY
host: <%= ENV['DATABASE_HOST'] %>
username: <%= ENV['DATABASE_USERNAME'] %>
password: <%= ENV['DATABASE_PASSWORD'] %>
RUBY
end
end
# Optionally install Rspec
if yes?('Would you like to install Rspec?')
gem 'rspec-rails', group: :test
after_bundle { generate 'rspec:install' }
end
You can test this particular template by running:
rails new myapp -m mytemplate.rb
Or:
rails new myapp --database=postgresql mytemplate.rb
To take advantage of the custom database configuration.
As mentioned, this file is perfectly suited to share as a GitHub gist. Adapt it to suit your needs, upload it, and then share it with your colleagues, friends, and anyone else interested in your custom app template 😉.
Learn More About Rails Generators and Templates
Needless to say, we've just scratched the surface here.
Everyone has different preferences for writing and developing Rails apps, so there are numerous generators and application templates out there. You can learn a lot about doing specific customizations by reading about them. I recommend Chris Oliver's Jumpstart and RailsBytes, the latter of which is a community-curated collection of templates.
There is also Thoughtbot's Suspenders, which inspired me to dig deeper into Rails generators and templates. I even wrote my own application template — Schienenzeppelin — which, while not up-to-date, might still provide some inspiration.
Wrap Up: Get Started with Ruby on Rails Generators and Templates
In this post, we looked into the basics of Rails generators and how they are used. We created our own generators to simplify writing new model specs.
We then delved into templates and learned how you could create a simple template to customize your application setup. This can be a bit of work, but also quite rewarding. If writing your own templates is not for you, there are many existing ones online to choose from!
Happy templating!
P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!
Top comments (0)