Adding a Custom Generator to Rails Scaffold


This took me wayyy too long to figure out, so I figured I’d share an end to end guide. Shoutout to this GitHub gist.

Background

If you’re like me, you hate typing boilerplate so you use rails g scaffold Thing. Oftentimes, we come up with design patterns that aren’t covered by the Rails default scaffold. To get around this, you can create custom generators.

Create a Generator

First, we’ll create our Something generator. Note that this is namespaced under the Rails module.

rails g generator rails/something

Now that you’ve generated it, fill out the resulting USAGE and something_generator.rb files like so.

Description:
    Generates something

Example:
    bin/rails generate something Thing

    This will create:
      app/something/thing.rb
class Rails::SomethingGenerator < Rails::Generators::NamedBase
  source_root File.expand_path("templates", __dir__)

  def create_something
    file_path = "app/something/#{file_name}.rb"
    create_file file_path, "# Oh hai there"
  end
end

If you want to be extra fancy, you can create a template and spec! But I don’t feel like it so here we go.

Initializer

Next we need to create an initializer to hook into the scaffold generator. I called mine config/initializers/custom_scaffold_generators.rb.

require "rails/railtie"
require "rails/generators"
require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"

module SomethingGenerator
  module ScaffoldControllerGenerator
    extend ActiveSupport::Concern

    included do
      hook_for :something, in: nil, default: true, type: :boolean
    end
  end
end

module ActiveModel
  class Railtie < Rails::Railtie
    generators do |app|
      Rails::Generators.configure! app.config.generators
      Rails::Generators::ScaffoldControllerGenerator.include SomethingGenerator::ScaffoldControllerGenerator
    end
  end
end

Note that we set it so default: true, so it’s automatically included in the scaffold. Voila, you’ve got a working custom scaffold generator.

rails g scaffold Thing
      invoke  active_record
      create    db/migrate/20220618203059_create_things.rb
      create    app/models/thing.rb
      ... a bunch of stuff we don't care about in this post
      invoke    something
      create      app/something/thing.rb