Blog article

Time has come for me to write a Rails generator, and as you’re guessing right now, my first step was taking a look at the Guides. They give you a pretty good idea on what you can do (despite of being for Rails 3.0), but as my friend Santiago always say, there’s no better documentation than the source code itself. So, my second step was to dive into the code. You should definitely read the code, great stuff there.

After some time reading, I decided it was time for me to start playing around with that, so here it comes:

First thing you should know, is that all Rails generators are derived from a class called “Rails::Generator::Base.” But, if we derive our generator class from NamedBase instead of Base, then we’ll get the ability to take a name parameter from the script/generate command line. With that in mind, you can start writing the generator skeleton:

class WidgetGenerator < Rails::Generator::NamedBase
  def manifest
    record do |m|
      # Do something
    end
  end
end

In order to make the generator work on your Rails 2.3 application, you should place this file under ‘lib/generators/widget/widget_generator.rb’

Is on the manifest method where the magic occurs. Depending on what exactly we want our generator to do, is what we’re going to code inside it. In my case, I wanted to behave very similar to the scaffold, so I wanted a controller class, a model, views, migration, etc… You can look at the templates of the scaffold generator, they will give you a very clear idea on how to use them. Then, you should place your templates under ‘lib/generators/widget/templates/’

In most cases, you will want your generator to receive several arguments. Best way is to have an initialize method to take care of that, just like this:

class WidgetGenerator < Rails::Generator::NamedBase
  attr_reader   :controller_class_name,
                :class_name
  attr_accessor :attributes
  
def initialize(runtime_args, runtime_options = {})
    super
    @name = runtime_args.first
    @controller_class_name = @name.pluralize.capitalize
    @attributes = []

    runtime_args[1..-1].each do |arg|
      if arg.include? ':'
        @attributes << Rails::Generator::GeneratedAttribute.new(*arg.split(":"))
      end
    end
  end

  def manifest
    record do |m|
      # Do something
    end
  end
end

Until now, we’re just initializing the generator, but it’s not doing anything yet. Let’s add some action on our manifest method:

def manifest
    record do |m|
      m.template('controller.rb', "app/controllers/#{name.pluralize}_controller.rb")
      m.template('model.rb', "app/models/#{name}.rb")
      m.migration_template("migration.rb", "db/migrate", :migration_file_name => "create_#{name.underscore.pluralize.camelize}")

      m.directory(File.join('app/views', name.pluralize))
      for action in %w[ new edit show ]
        m.template(
          "view_#{action}.html.erb",
          File.join('app/views', controller_file_name, "#{action}.html.erb")
        )
      end
    end
  end

Cool, now we are generating a controller from our template, a model, a migration, among others…Nice!

You can also add a protected banner method, to display the usage of the generator right on the console:

protected
    def banner
      "Usage: #{$0} widget WidgetName [field:type, field:type]"
    end

And that’s all !! You can now generate all the widgets you want. The command to run this would be:

$ ./script/generate widget MyWidget title:string viewing:integer

Have fun generating!!