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!