4.3. Generating Scaffolding CodeCode generation is the other major form of scaffolding. You generate scaffolding with the ruby script/generate scaffold command. Run it without parameters to see the parameters you can specify and a description of the generator: > ruby script/generate scaffold Usage: script/generate scaffold ModelName [ControllerName] [action, ...] General Options: -p, --pretend Run but do not make any changes. -f, --force Overwrite files that already exist. -s, --skip Skip files that already exist. -q, --quiet Suppress normal output. -t, --backtrace Debugging: show backtrace on errors. -h, --help Show this help message. -c, --svn Modify files with subversion. (Note: svn must be in path) Description: The scaffold generator creates a controller to interact with a model. ... Here, you need to specify a model and a controller name . So, to generate the scaffolding for the controller and views of our Photo model, type: > ruby script/generate scaffold photo photos ... Respond y when Rails asks if you want to replace a file. Any additional parameters are added as empty methods on the new controller. If you omit the name of the controller, Rails uses the English plural of the model name. So, to generate scaffolding for our slides, slideshows and categories, type: ruby script/generate scaffold slide ... ruby script/generate scaffold slideshow ... ruby script/generate scaffold category ... 4.3.1. Inside the Generated CodeLet's look at the controller Rails generated. Your version may be slightly different than the code you see here, but the principles should be the same. Open apps/controllers/photos_controller.rb : class PhotosController < ApplicationController def index list render :action => 'list' end def list @photo_pages, @photos = paginate :photos, :per_page => 10 end def show @photo = Photo.find(params[:id]) end def new @photo = Photo.new end def create @photo = Photo.new(params[:photo]) if @photo.save flash[:notice] = 'Photo was successfully created.' redirect_to :action => 'list' else render :action => 'new' end end def edit @photo = Photo.find(params[:id]) end def update @photo = Photo.find(params[:id]) if @photo.update_attributes(params[:photo]) flash[:notice] = 'Photo was successfully updated.' redirect_to :action => 'show', :id => @photo else render :action => 'edit' end end def destroy Photo.find(params[:id]).destroy redirect_to :action => 'list' end end
As you can see, Rails generates a controller with each of the methods found in Table 4-1. Point your browser to http://localhost:3000/photos to verify that the generated code behaves identically to the code generated with the scaffold :photo method. But the code is slightly different. Instead of generating the views from within the controller like the scaffold method, the generated code explicitly renders views in rhtml code. Let's look at one of the views. Open app/views/photos/list.rhtml : 1 <% for column in Photo.content_columns %> 2 <p> 3 <b><%= column.human_name %>:</b> <%=h @photo.send(column.name) %> 4 </p> 5 <% end %> 6 7 <%= link_to 'Edit', :action => 'edit', :id => @photo %> 8 <%= link_to 'Back', :action => 'list' %> This view is be rendered by the list method of PhotosController . Let's look at the first and third lines in detail:
Figure 4-5 shows the result. The view lists all the properties of a Photo record in the database. The Filename property was in the database from the beginning; the Created At, Thumbnail, and Description properties were added by a migration earlier in this chapter. Furthermore, if we add more properties, the list.rhtml view won't require any modification to display them. Figure 4-5. This show view is dynamicThe show.rhtml view reflects changes in the database. Now, let's look at a view that's a little less dynamic. Open app/views/photos/_form.rhtml : <%= error_messages_for 'photo' %> <!--[form:photo]--> <p><label for="photo_created_at">Created at</label><br/> <%= datetime_select 'photo', ' created_at ' %></p> <p><label for="photo_filename">Filename</label><br/> <%= text_field 'photo', ' filename ' %></p> <p><label for="photo_thumbnail">Thumbnail</label><br/> <%= text_field 'photo', ' thumbnail ' %></p> <p><label for="photo_description">Description</label><br/> <%= text_field 'photo', ' description ' %></p> <!--[eoform:photo]--> This view is called a partial, and it's responsible for rendering a form for a photo in edit.rhtml and new.rhtml . (You'll learn more about partials in the next chapter.) The words in bold are attributes on Photo . Because you've generated explicit code to render the form, this view works only for the database columns that were present when you created the scaffolding. So here, you see one of the primary differences between scaffolding created through metaprogramming and generated scaffolding. When we used metaprogramming, because our scaffold :photo method generated scaffolding at runtime, the scaffolding reflects changes in the database. With our generated code, the scaffolding gives a one-time benefit, but must be maintained thereafter. 4.3.2. The Best of Both WorldsMost Rails developers use both kinds of scaffolding. The scaffold method helps when you're revising your Active Record models quickly, because it reflects database changes in the user interface. Later, you can generate scaffolding and flesh out your controllers and user interfaces, starting from a foundation of generated code. Using both in combination is a powerful way to work. Scaffolding does have its limits, though. You get a one- size -fits-all user interface and controller. It's not going to be right for all purposes, and it's not complete. One of the biggest deficiencies of scaffolding is the lack of relationship management. Scaffolding does not take relationships in the existing model under consideration when creating the scaffold. |