ruby

Bootstrapping with Ruby on Rails Generators and Templates

Bootstrapping with Ruby on Rails Generators and Templates

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:

  1. Install the dotenv gem.
  2. Create a .env.development file for the development environment.
  3. Adapt the database configuration file to use environment variables.
  4. 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!

Share this article

RSS
Hans-Jörg Schnedlitz

Hans-Jörg Schnedlitz

Our guest author Hans is a Rails engineer from Vienna, Austria. He spends most of his time coding or reading about coding, and sometimes even writes about it on his blog! When he's not sitting in front of a screen, you'll probably find him outside, climbing some mountain.

-> All articles by Hans-Jörg Schnedlitz-> Become an AppSignal author

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps