Bootstrapping with Ruby on Rails Generators and Templates
Tags: ruby, rails
Published: 2022-05-11 12:00:46

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](https://github.com/rails/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:

```ruby
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:

```ruby
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:

```ruby
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.

```bash
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](https://relishapp.com/rspec/rspec-rails/docs/generators). [FactoryBot has a generator](https://github.com/thoughtbot/factory_bot_rails#generators) 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](https://guides.rubyonrails.org/generators.html#customizing-your-workflow) 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](https://guides.rubyonrails.org/rails_application_templates.html#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:

```bash
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.

```bash
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:

```bash
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](https://github.com/bkeepers/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](https://github.com/rspec/rspec-rails).

Let's create a local file — `mytemplate.rb` — and add `dotenv` using the `gem` command.

```ruby
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](https://github.com/rails/rails/blob/8f39fbe18a57ae74513edc8561c00a369fe10f08/railties/lib/rails/generators/rails/app/app_generator.rb#L522).

```ruby
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`](https://guides.rubyonrails.org/generators.html#inject-into-file).

```ruby
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](https://github.com/rails/rails/blob/8f39fbe18a57ae74513edc8561c00a369fe10f08/railties/lib/rails/generators/database.rb#L14) to see which options are available.

```ruby
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:

```ruby
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:

```ruby
# 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:

```bash
rails new myapp -m mytemplate.rb
```

Or:

```bash
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](https://github.com/excid3/jumpstart) and [RailsBytes](https://railsbytes.com/), the latter of which is a community-curated collection of templates.

There is also [Thoughtbot's Suspenders](https://github.com/thoughtbot/suspenders), which inspired me to dig deeper into Rails generators and templates. I even wrote my own application template — [Schienenzeppelin](https://github.com/hschne/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](https://blog.appsignal.com/ruby-magic)!**