The Modest Rubyist

A blog about Ruby & Rails from beginner to advanced.

Rails 3 Plugins - Part 4 - More on Generators

ShareThis March 22nd 2010

So, you have written a basic generator for your Rails 3 plugin but it needs to do more. In this part I discuss the generators API in more detail.

Adding a Descritiption and Usage Info

People using your plugin will want to know what your generator does. You can add a simple description to your generator like so,

class MyGenerator < Rails::Generators::Base
  desc "run this generator to create something"
end

The ‘desc’ macro takes a string, which should describe the generators purpose.

In the same directory as the file which contains your generator class you can place a file named ‘USAGE’. This file should contain a more detailed description and optionally an example of how to use your generator. Here is the USAGE file from the rails core application generator

Description:
  The 'rails' command creates a new Rails application with a default
  directory structure and configuration at the path you specify.  

Example:
  rails ~/Code/Ruby/weblog

  This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
  See the README in the newly created application to get going.

Taking Command-Line Options and Arguments

If you need to provide control to the user upon invocation you can define some options for it. These options are exposed all the way to the command-line. Defining these options is not actually something specific to the Rails 3 Generators API. Instead, its inherited from Thor, which the API is based upon.

To define a single option we can use the ‘class_option’ macro. To define multiple we use ‘class_options’. Here is how we would define a boolean option for some generator

class_option :my_option, :type => :boolean, :desc => "some desc"

‘class_option’ takes a name and several options. The following are a list of options you can pass to class_option:

:desc => string, description of option
:required => true/false, whether or not the argument is required
:default => default value if argument is not given
:group => group this option belongs to. 
          Used for orginization of options at different levels
:type => :string/:hash/:array/:numeric/:boolean
:banner => string, shown in usage notes

Options are accessed from within a generator’s steps (or instance methods defined in the Generator class) using the ‘options’ hash. We can access our previously defined option like so

 options[:my_option] 

Since it is a boolean option we would most likely use it like

 def some_step
   do_something unless options[:my_option]
 end

There also exists the ‘method_option(s)’ and the ‘argument’ class macros. The former is more useful in Thor specific contexts and I will not discuss it here. The latter is a way to take command-line arguments instead of options. The difference between arguments and options is subtle with respect to nomenclature but great in practice. Whereas options are provided regardless of order using the

  --option=VALUE

syntax, arguments are taken off the command-line in order. Inside your code, options are accessed using self.options. Arguments are accessed using accessors, like self.some_argument, and cannot be of type :boolean. Also, any optional arguments, created with :required => false or :optional => true, must be specified after all the required arguments. Restated, all required arguments must precede any optional ones. This is of course due to the order sensitive nature of arguments. Here is how we could define a couple arguments for our generator

  argument :my_arg, :type => :numeric, :required => true, :desc => "required"
  argument :some_arg, :type => :string, :required => false, :desc => "optional"

We could then access these arguments in our generator class like in this rudimentiery and mostly useless example,

 def some_task
    if self.my_arg < some_value
      do_something_with self.some_arg
    end
 end

Note, unlike ‘class_option’ and ‘class_options’ , an ‘arguments’ macro does not exist for creating multiple arguments at once. Instead ‘arguments’ returns the arguments defined for a class, or if none exist, its superclass. You can read more on these in the Thor docs.

Rails Specific Actions

Thor provides several useful methods or actions that can be used within steps to perform common tasks. These are defined in Thor::Actions. Rails also defines some more actions, primarily, in Rails::Generators::Actions.

Here are some useful ones and how to use them:

# lib(filename, data=nil, &block)
lib("somefile.rb") do 
  "this string will be the content of the newly created lib/somefile.rb"
end  

lib("anotherfile.rb", "you can also pass a string to lib")

# rake(command, options={})
rake("db:migrate") #run any defined Rake command
rake("db:migrate", :env => :development)

# route(routing_code)
route("resources :accounts") # adds route to TOP of routes definition
                             # you must write valid routing code

# git(command = {})
git(:init) #run any git command
git(:add => "somefile.ext", :rm => "somedir/anotherfile.ext")

All of these are automatically available in any of your generators.

blog comments powered by Disqus