The Flatiron Papers: Rails Generators and the Generators that Generate Them

Note: This blog post is intended as a resource for current/future software development bootcamp students. If you’re already a developer, it will probably not teach you anything new. But if you’re deep in the weeds with your first week of Ruby on Rails, it might help you out.

Rails g migration. Rails g resource. Rails g scaffold. Whatever it is you’re in need of, odds are Rails can generate it for you at the drop of a syntactically valid hat. Like much of Rails, generators can seem like magic — one, two, three, hey presto, ten files just appeared in your text editor and you barely had to do anything. But how do generators work under the hood, and what more can we learn about them?

God of Thunder (and Command Line Utilities)

In Rails versions above 3.0, generators are built on Thor, a toolkit for building CLIs. Thor is somewhat analogous to Rake, although Rake is more for individual projects whereas Thor can port functionality across apps. In a sentence, Thor allows Rails to build out powerful command line utilities, including, as we’ll see, custom generators.

<iframe src=”https://giphy.com/embed/l4FGni1RBAR2OWsGk" width=”480" height=”272" frameBorder=”0" class=”giphy-embed” allowFullScreen></iframe><p><a href=”https://giphy.com/gifs/excited-yes-thor-l4FGni1RBAR2OWsGk">via GIPHY</a></p>

Have It Your Way

That’s right: you can build your own generator in Rails! We’re not just stuck with scaffolds, migrations, and the various other built-in generators Rails has out of the box. Let’s look a little deeper:

First off, custom generators go in the lib/generators folder and inherit from Rails::Generators::Base. Consider this custom generator from the Rails documentation, which creates a new initializer called, well, “initializer”:

class InitializerGenerator < Rails::Generators::Base
def create_initializer_file
create_file "config/initializers/initializer.rb", "# Add initialization content here"
end
end

Let’s break this down. At the top, we see our class declaration, which inherits as it must from Rails::Generators::Base. It has a single method, create_initializer_file, which uses create_file (a Thor method inherited from Thor::Actions) and an argument of a string containing a file path to create initializer.rb in the config/initializers folder. The second argument, a comment inside a string, is just the default text for the initializer that’s created.

If you’d like to add a little helper documentation for your generator, odds are good that Rails will be able to auto-create it if your namespacing falls within convention. If not (you can check by running rails generate [generator name] -- help) you can add your own either by calling desc inside your generator and following it with a string descriptor, as in desc “This generator creates a new migration in the migrate folder.”

Yo Dawg, I Heard You Liked Generators

The above method of creating a generator is a little tedious. What if you’re a loose cannon developer on the edge, with no time for manual generator generation? Lucky for you, Rails generators have their very own generator generator.

If we want to create our above initializer generator in no time flat, we can run the following code:

rails generate generator initializer

Which creates the following file:

class InitializerGenerator < Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
end

There are a couple of notable differences between these approaches. One is that creating a generator in the command line creates a generator that inherits from Rails::Generators::NamedBase as opposed to Rails::Generators::Base. This just means that the generator will expect at least one argument, which will be the name of whatever object you’re creating. This generator also comes with an auto-created source_root attribute, which is the place in your file structure that subsequent objects created with this generator will be stored. By default, it’s lib/generators/object_name/templates.

But what if you want your generator to accept custom command line arguments? Done and done. Thanks to Thor’s built-in functionality, all you have to do is add class_option like so:

class_option :scope, type: :string, default: 'read_products'

This adds a custom scope command line argument, with a type of string, that defaults to ‘read_products’. Easy enough!

Custom generators offer a whole new world of functionality for programmers, from streamlining repetitive object instantiation to customizing and modifying existing default generators. Hopefully, this short introduction has made your workflow a little bit faster.