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

Rails 3 Generators: Scaffolding

Scaffolding in Rails has always been a little controversial. Originally, it was responsible for a lot of Rails’s wow factor – DHH’s blog in fifteen minutes was built on scaffolding. Over time, however, it became less clear whether it was supposed to be used for production code, or if it was intended to educate new Rails developers on best practices.

After a few discussions through various channels, the core team eventually settled on the latter – scaffolding was supposed to be educational, illustrating the best practices around RESTful controllers and other Rails conventions. Even with that goal, however, there’s still been some difficulty around updating the scaffolded views to teach people how best to, for instance, split files into partials.

Before digging in to these deeper issues any further, let’s take a look at what the new scaffolding generators do:

Scaffold

[rails2] > ./script/generate scaffold Club name:string exclusive:boolean
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/clubs
      exists  app/views/layouts/
      exists  test/functional/
      exists  test/unit/
      create  test/unit/helpers/
      exists  public/stylesheets/
      create  app/views/clubs/index.html.erb
      create  app/views/clubs/show.html.erb
      create  app/views/clubs/new.html.erb
      create  app/views/clubs/edit.html.erb
      create  app/views/layouts/clubs.html.erb
      create  public/stylesheets/scaffold.css
      create  app/controllers/clubs_controller.rb
      create  test/functional/clubs_controller_test.rb
      create  app/helpers/clubs_helper.rb
      create  test/unit/helpers/clubs_helper_test.rb
       route  map.resources :clubs
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/club.rb
      create    test/unit/club_test.rb
      create    test/fixtures/clubs.yml
      exists    db/migrate
      create    db/migrate/20100301133032_create_clubs.rb

[rails3] > rails generate scaffold Club name:string exclusive:boolean
      invoke  active_record
      create    db/migrate/20100301132947_create_clubs.rb
      create    app/models/club.rb
      invoke    test_unit
      create      test/unit/club_test.rb
       route  resources :clubs
      invoke  scaffold_controller
      create    app/controllers/clubs_controller.rb
      invoke    erb
      create      app/views/clubs
      create      app/views/clubs/index.html.erb
      create      app/views/clubs/edit.html.erb
      create      app/views/clubs/show.html.erb
      create      app/views/clubs/new.html.erb
      create      app/views/clubs/_form.html.erb
      create      app/views/layouts/clubs.html.erb
      invoke    test_unit
      create      test/functional/clubs_controller_test.rb
      invoke  stylesheets
      create    public/stylesheets/scaffold.css

The readability improvements in Rails 3’s generators really come through here, don’t they?

We’ve already seen all the model-related changes that Rails 3 includes, and I covered the stylesheets generator last time, which leaves the scaffold_controller section in the middle as the only new piece. Rails 3 makes that generator available on its own (for modularity purposes, of course):

[rails3] > rails generate scaffold_controller Club name:string exclusive:boolean
      create  app/controllers/clubs_controller.rb
      invoke  erb
      create    app/views/clubs
      create    app/views/clubs/index.html.erb
      create    app/views/clubs/edit.html.erb
      create    app/views/clubs/show.html.erb
      create    app/views/clubs/new.html.erb
      create    app/views/clubs/_form.html.erb
      create    app/views/layouts/clubs.html.erb
      invoke  test_unit
      create    test/functional/clubs_controller_test.rb

If we dig into the controller and functional test, most things look pretty similar. In fact, the only difference in the controller is the newly-condensed redirect-and-set-flash pattern:

# Rails 2
flash[:notice] = 'Club was successfully created.'
format.html { redirect_to(@club) }
format.xml  { render :xml => @club, :status => :created, :location => @club }

# Rails 3
format.html { redirect_to(@club, :notice => 'Club was successfully created.') }
format.xml  { render :xml => @club, :status => :created, :location => @club }

And the only difference in the functional test is the use of fixtures for data:

# Rails 2
test “should create club” do
  assert_difference(‘Club.count’) do
    post :create, :club => { }
  end

  assert_redirected_to club_path(assigns(:club))
end

# Rails 3
test “should create club” do
  assert_difference(‘Club.count’) do
    post :create, :club => clubs(:one).attributes
  end

  assert_redirected_to club_path(assigns(:club))
end

Incidentally, this points to a difficulty in the new generator modularity. When you replace fixtures (with, say, Factory Girl), you eliminate test/fixtures/whatever.yml, but other generators may still expect them to be defined, like these functional tests. We’ll look at this again in a future post in the series.

In the views, there are a few more changes. First, notice that the scaffold has finally split out the new/edit form into a partial. If you look inside that partial, you’ll notice another slight difference:

<% form_for(@club) do |f| %>
  <%= f.error_messages %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :exclusive %><br />
    <%= f.check_box :exclusive %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Rails 3 has gone classy! The field and actions classes are used in the new scaffold stylesheet.

The other main difference is the new render sytnax:

<h1>New club</h1>

<%= render 'form' %>

<%= link_to 'Back', clubs_path %>

Partials are more directly renderable now than they were in the old syntax (render :partial => 'form'). Other than those changes, however, the scaffolding is pretty much just what it was… and that’s kind of the problem.

It’s all well and good to say that scaffolding is for education, but Rails 3 has two distinct audiences: newbies and Rails 2 switchers. The former may need the extra hand-holding that the scaffolding includes (the Rails 2-style respond_to blocks), but the latter would be better served if the scaffolding followed the new respond_with style.

Rails 3 has introduced significant changes into some common tasks, and until the everyday users are up to speed with those, scaffolding will again fall into a no-man’s-land where it’s not quite educational enough for anyone, whether they're new to Rails or experts just trying to update their knowledge.

But wait, there’s more

OK, that was kind of a downer. Here’s something positive to think about, instead: scaffold_controller is a modular generator, which means that we can replace it with something better. I anticipate a flurry of scaffold-replacement gems to start anytime now (I’d contribute to it myself if I weren’t already working on some other generator replacements), and hopefully one or a few of those will win out, and we’ll be able to have the multi-purpose scaffolding we’ve always dreamed of.

Want to get in on the ground floor? Watch for my next post, where I talk about how to replace the built-in generators!


Get More From Viget

Subscribe to get our monthly newsletter and occasional special announcements.