4.3. Generating
Scaffolding Code
Code 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 Code
Let'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
|
When you run scaffolding, and you specify:
ruby script/generate scaffold Photo
you actually get a model called Photo
and a controller called PhotosController. You get this
behavior because the scaffolding generator takes two parameters:
the model and the controller. If you omit the controller, the
generator pluralizes the name. Often, that's what you want, because
Rails controllers typically deal with collections of things.
Usually, you want a singular model name. For
example, you might want an administration controller called
Admin. If you usually use plurals, you can omit the name
of the controller. If you want a singular controller name, specify
both the model and controller name. For more information, you can
get scaffolding help by typing:
ruby script/generate scaffold
|
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:
<% for column in Photo.content_columns
%>
-
In line 1, the view loops through each column in
the database. Recall that Active Record added metadata to
Photo, maintaining an array with each column in the table.
content_columns has all of the columns that are for public
display. (You don't see foreign keys or the id property,
for example.)
<b><%= column.human_name
%>
-
The view renders a friendly name to serve as a
label of the element.
<%=h @photo.send(column.name)
%>
-
The view sends a message to @photo with
the name of the column and renders the result. (For example,
@photo.send "filename" would be the same as
@photo.filename.)
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.
The 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 Worlds
Most 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.
 |