Your friends at Viget present Extend, a Code & Technology Blog

Rails 3 Generators: The Old Faithful

I figured it would be best to start the tour of Rails 3 generators with those that are most commonly used. Assuming I'm representative of most Rails developers, that would include the following: migrations, models, controllers, and resources. (If your favorite isn't in the list, don't worry! I'll be covering the rest of them soon enough.)

Migration

[rails2] > ./script/generate migration AddNameToPerson
      exists  db/migrate
      create  db/migrate/20100210021747_add_name_to_person.rb

[rails3] > rails generate migration AddNameToPerson
      invoke  active_record
      create    db/migrate/20100210021747_add_name_to_person.rb

My dad always told me to start simple, and it doesn't get much simpler than the migration generator. In Rails 2.3.5, all it does is check that the migration folder exists and creates the file. In Rails 3, it's almost unchanged. The existence check is now done behind the scenes, and in its place in the output we have a new invoke line.

We'll see invoke a lot as we look at the generators. invoke is a flag that the generator is jumping into a new context, and as you can see here, the output generated while in that new context is indented. In this case specifically, the basic migration generator is handing off control to active_record:migration, which creates the actual migration file. invoke may look trivial, but this is what allows us to swap out generators when we replace components in Rails 3 (for instance, using DataMapper instead of ActiveRecord).

Model

[rails2] > ./script/generate model Person name:string
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/person.rb
      create  test/unit/person_test.rb
      create  test/fixtures/people.yml
      exists  db/migrate
      create  db/migrate/20100210021556_create_people.rb

[rails3] > rails generate model Person name:string
      invoke  active_record
      create    db/migrate/20100210021556_create_people.rb
      create    app/models/person.rb
      invoke    test_unit
      create      test/unit/person_test.rb
      create      test/fixtures/people.yml

The model generator is only slightly more complicated than the migration generator. Here, you can see that hiding the existence checks cleans up the output a good bit, and you can also see our first example of nested invokes when the basic model generator invokes active_record:model, which in turn invokes test_unit:model.

There are also a number of new options in the model generator:

TestUnit options:
  -r, [--fixture-replacement=NAME]  # Fixture replacement to be invoked
      [--fixture]                   # Indicates when to generate fixture
                                    # Default: true

ActiveRecord options:
      [--parent=PARENT]        # The parent class for the generated model
  -t, [--test-framework=NAME]  # Test framework to be invoked
                               # Default: test_unit
      [--migration]            # Indicates when to generate migration
                               # Default: true
      [--timestamps]           # Indicates when to generate timestamps
                               # Default: true

As you might expect, these options come from the TestUnit and ActiveRecord generators, respectively. Finally, we can replace fixtures with something else, or turn 'em off entirely! (Granted, we could already do the latter with --skip-fixture...)

The --parent option is completely new, and makes it easier to generate any STI models you might need – I can only hope this won't become too common, but that's only because of my well-known bias against single-table inheritance.

The --test-framework option is also new, and is another indicator of the flexibility of the Rails 3 generator system.

Controller

[rails2] > ./script/generate controller People index show
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/people
      exists  test/functional/
      exists  test/unit/helpers/
      create  app/controllers/people_controller.rb
      create  test/functional/people_controller_test.rb
      create  app/helpers/people_helper.rb
      create  test/unit/helpers/people_helper_test.rb
      create  app/views/people/index.html.erb
      create  app/views/people/show.html.erb

[rails3] > rails generate controller People index show
      create  app/controllers/people_controller.rb
      invoke  erb
      create    app/views/people
      create    app/views/people/index.html.erb
      create    app/views/people/show.html.erb
      invoke  test_unit
      create    test/functional/people_controller_test.rb
      invoke  helper
      create    app/helpers/people_helper.rb
      invoke    test_unit
      create      test/unit/helpers/people_helper_test.rb

With the controller generator, the changes become even more obvious. First, this is the only Rails 3 generator we've seen that doesn't start with invoke. That's because ActionController is a core piece of Rails, and you can't swap it out for an alternative like you can with an ORM or test framework.

After the controller itself is created, the Erb generator is pulled in to create the views. This gives you the freedom to replace Erb (which is technically Erubis in Rails 3) with a different template engine. That's followed by a straightforward invoke of test_unit:controller.

The next new piece is the invocation of the helper generator, which will pop back up in a later post. It's pretty simple, though, creating only a helper file and a unit test (the latter by passing control over to test_unit:helper).

Options:
      [--helper]                # Indicates when to generate helper
                                # Default: true
  -t, [--test-framework=NAME]   # Test framework to be invoked
                                # Default: test_unit
  -e, [--template-engine=NAME]  # Template engine to be invoked
                                # Default: erb

New options here include --test-framework and --template-engine, which are self-explanatory, and --helper, which allows you to turn off the helper generator if (like me) you don't like controller-linked helper classes.

Resource

[rails2] > ./script/generate resource Person name:string
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/people
      exists  test/functional/
      exists  test/unit/
      exists  test/unit/helpers/
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/person.rb
      create    test/unit/person_test.rb
      create    test/fixtures/people.yml
      exists    db/migrate
      create    db/migrate/20100210021723_create_people.rb
      create  app/controllers/people_controller.rb
      create  test/functional/people_controller_test.rb
      create  app/helpers/people_helper.rb
      create  test/unit/helpers/people_helper_test.rb
       route  map.resources :people

[rails3] > rails generate resource Person name:string
      invoke  active_record
      create    db/migrate/20100210021723_create_people.rb
      create    app/models/person.rb
      invoke    test_unit
      create      test/unit/person_test.rb
      create      test/fixtures/people.yml
      invoke  controller
      create    app/controllers/people_controller.rb
      invoke    erb
      create      app/views/people
      invoke    test_unit
      create      test/functional/people_controller_test.rb
      invoke    helper
      create      app/helpers/people_helper.rb
      invoke      test_unit
      create        test/unit/helpers/people_helper_test.rb
       route  resources :people

Having looked at the model and controller generators, the resource generator should be perfectly understandable. The most interesting thing here is actually in the 2.3.5 output – see that dependency model line? That's what invoke replaced. Who said the old way wasn't modular!

Options:
      [--singleton]               # Supply to create a singleton controller
  -c, --resource-controller=NAME  # Resource controller to be invoked
                                  # Default: controller
  -o, --orm=NAME                  # Orm to be invoked
                                  # Default: active_record
  -a, [--actions=ACTION ACTION]   # Actions for the resource controller
      [--force-plural]            # Forces the use of a plural ModelName

# ...

TestUnit options:
  -r, [--fixture-replacement=NAME]  # Fixture replacement to be invoked
      [--fixture]                   # Indicates when to generate fixture
                                    # Default: true

ActiveRecord options:
  [--parent=PARENT]  # The parent class for the generated model
  [--migration]      # Indicates when to generate migration
                     # Default: true
  [--timestamps]     # Indicates when to generate timestamps
                     # Default: true

Controller options:
      [--helper]                # Indicates when to generate helper
                                # Default: true
  -t, [--test-framework=NAME]   # Test framework to be invoked
                                # Default: test_unit
  -e, [--template-engine=NAME]  # Template engine to be invoked
                                # Default: erb

Most of the options here showed up in the model and controller generators, but some are new. The --singleton option, for instance, changes the routing line to represent a singleton resource. I'm not convinced of the utility of the --force-plural option, though, which prevents the generator from singularizing the model name before calling active_record:model.

The other new options here are --actions and --resource-controller. The --actions option allows you to specify a starting set of actions for the controller, which is otherwise bare – and as you might expect, adding actions invokes the appropriate template generator, as well. The --resource-controller lets you override the controller class that is created for your resource. The most obvious use of this would be replacing the basic ApplicationController with a custom class (ResourceController or something similar, for instance), but you can also swap in a Metal controller here, if you like.

Next Up

That does it for the first stop on the tour. Check back soon for the next set of generators, which will include such old standbys as mailer, observer, session_migration, and more!


Get More From Viget

Subscribe to get our monthly newsletter and occasional special announcements.