Ruby on RailsのScaffoldの仕組み
Scaffoldをはじめ、Ruby on Railsに組み込まれているコードジェネレータについて詳しく書かれた日本語の情報は少ないです。英語のドキュメントとしてはAPIドキュメントのModule Rails::Generatorが参考になりますが、やはりRuby on Railsのソースコードを読むのが一番分かりやすいです。
ScaffoldジェネレータのソースはRubyがC:¥Rrubyにインストールされている場合、C:¥Ruby¥lib¥ruby¥gems¥1.8¥gems¥rails-2.3.2¥lib¥rails_generator¥generators¥components¥scaffold¥scaffold_generator.rbになります。このファイルと同じディレクトリにあるtemplatesディレクトリの下に、生成するコードの元になるファイルが置かれています。

テンプレート
templatesディレクトリにあるview_show.html.erbを見てみましょう
<% for attribute in attributes -%> <p> <b><%= attribute.column.human_name %>:</b> <%%=h @<%= singular_name %>.<%= attribute.name %> %> </p> <% end -%> <%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> | <%%= link_to 'Back', <%= plural_name %>_path %>
これは詳細表示用のテンプレートで第1回の例ではapp/views/players/show.html.erbファイルがこのテンプレートから作られています。app/views/players/show.html.erbの一部を見てみましょう。
<p> <b>Name:</b> <%=h @player.name %> </p> <p> <b>Team:</b> <%=h @player.team %> </p> ・・・ 省略 ・・・ <b>Assist:</b> <%=h @player.assist %> </p> <%= link_to 'Edit', edit_player_path(@player) %> | <%= link_to 'Back', players_path %>
view_show.html.erb内の<%% ~ %>タグはそのまま<% ~ %>タグに置き換わっています。ただし<% ~ %>の内容が評価されてテンプレートを展開しています。またplural_name、singular_name、attributesなどの変数が使われているのが分かります。
Scaffoldジェネレータ
Scaffoldジェネレータ本体scaffold_generator.rbを上から見てみましょう。ScaffoldGeneratorクラスは、ジェネレータの元になるRails::Generator::NamedBaseクラスを継承しています。また、ジェネレータで使われるコントローラ名、そのファイル名などの属性(インスタンス変数)が定義されています。
class ScaffoldGenerator < Rails::Generator::NamedBase
default_options :skip_timestamps => false, :skip_migration => false, :force_plural => false
attr_reader :controller_name,
:controller_class_path,
:controller_file_path,
・・・ 省略 ・・・
初期化メソッドinitializeでは、ジェネレータで使われるコントローラ名、そのファイル名などの設定が行われます。
def initialize(runtime_args, runtime_options = {})
super
if @name == @name.pluralize && !options[:force_plural]
logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural."
@name = @name.singularize
end
@controller_name = @name.pluralize
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
・・・ 省略 ・・・
end
manifestメソッドがジェネレータのメインです。ここでは、m.class_collisions()でこれから作成するファイルがダブらないかチェックし、m.directory()で必要なディレクトリーを作成し、m.template()でテンプレートを元に必要なファイルを作成しています。
また、m.route_resourcesのようにconfig/route.rbを変更する専用のメソッドなどもあります。さらに、Scaffoldではモデルの作成部分はm.dependency 'model'……でmodelジェネレータを呼び出しています。
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper")
m.class_collisions(class_name)
# Controller, helper, views, test and stylesheets directories.
m.directory(File.join('app/models', class_path))
m.directory(File.join('app/controllers', controller_class_path))
m.directory(File.join('app/helpers', controller_class_path))
・・・ 省略 ・・・
for action in scaffold_views
m.template(
"view_#{action}.html.erb",
File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.erb")
)
end
# Layout and stylesheet.
m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb"))
m.template('style.css', 'public/stylesheets/scaffold.css')
m.template(
'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
)
・・・ 省略 ・・・
m.route_resources controller_file_name
m.dependency 'model', [name] + @args, :collision => :skip
end
end

