scaffoldのコードを読む(その1)

scaffoldした際に、引数で指定される各フィールドの型によって自動的に決まるwebのコントロールを、自分の都合の良いように変えてみたいと思います。その為に、scaffoldに関連するコードを読んでみます。

前回railsからコピーしてきたtemplateから、view_edit.html.erbを見てみます。

<h1>編集 <%= singular_name %></h1>

<%% form_for(@<%= singular_name %>) do |f| %>
  <%%= f.error_messages %>

<% for attribute in attributes -%>
  <p>
    <%%= f.label :<%= attribute.name %> %><br />
    <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
  </p>
<% end -%>
  <p>
    <%%= f.submit 'Update' %>
  </p>
<%% end %>

<%%= link_to 'Show', @<%= singular_name %> %> |
<%%= link_to 'Back', <%= plural_name %>_path %>
</macro:code>

attribute.field_typeによって、コントロールが決まる仕組みになっています。field_typeは以下のようになっていました。

[rails-2.3.2/lib/rails_generator/generated_attribute.rb] 
>||
def field_type
  @field_type ||= case type
    when :integer, :float, :decimal   then :text_field
    when :datetime, :timestamp, :time then :datetime_select
    when :date                        then :date_select
    when :string                      then :text_field
    when :text                        then :text_area
    when :boolean                     then :check_box
    else
      :text_field
  end      
end

変数typeは、script/generate scaffoldの引数に指定する「名前:sql_type」のsql_typeになります。

ちなみに、同ファイルにはdefaultというメソッドが含まれており、以下のようになっています。

def default
  @default ||= case type
    when :integer                     then 1
    when :float                       then 1.5
    when :decimal                     then "9.99"
    when :datetime, :timestamp, :time then Time.now.to_s(:db)
    when :date                        then Date.today.to_s(:db)
    when :string                      then "MyString"
    when :text                        then "MyText"
    when :boolean                     then false
    else
      ""
  end      
end

先ほどのfield_typeメソッドの定義から、scaffoldの引数にoneday:dateと指定すると、「f.date_select :oneday」というコードが生成されます。

このfという変数は何かとform_forやその先のfields_forを辿っていくと、デフォルトではActionView::Base.default_form_builder(=FormBuilder)のインスタンスになります。

[actionpack/lib/action_view/helpers/form_helper.rb]

def fields_for(record_or_name_or_array, *args, &block)
...
  builder = options[:builder] || ActionView::Base.default_form_builder
  yield builder.new(object_name, object, self, options, block)
end

上記コードより、指定されたsql_typeによって任意のwebのコントロールを配置するには、カスタムFormBuilderを作ってform_forのoptionsに:builderを明示するか、FormBuilderの各メソッドを上書くことにより実現できそうです。