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

 
              
               
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                              
                               
                              
                               
                              
                               
                              
                               
                              
                               
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
															
														 
															
														.png) 
     
     
     
     
     
													 
													 
													 
													 
													 
										
									
 
                    
