Rails2.1でform_forブロックの中のrender :partialの悩み解決!

以前、form_forブロック内の共通する部分を、パーシャルファイル(render :partial)として抜き出すときの書き方で悩んでいたい時期があった。実務的にはどうとでも書けてしまうので、そんなに深く悩む必要も無いのだが、徹底的なDRYを目指すRailsらしい書き方はどんな感じになるか気になっていたのだ*1。最近までずっと...。
で、Ruby on Rails 2.1 の変更点 PDF 日本語訳を読んでいたら、ようやくその答えが見つかったので嬉しくなってしまった。

Rails 2.1のscaffold

  • ソフトウェアの名前とダウンロード先を保存する適当なサンプルを作ってみる。
$ cd
$ rails partial_test
$ cd partial_test
$ script/generate scaffold software name:string url:string
$ rake db:migrate 
  • あっという間に動くものは出来上がるので、newとeditファイルを確認すると以下のようになっている。
  • これだと、nameとurlのフォームの見栄えを変更しようとした時、同じ修正を2回繰り返す必要がある。(たしかrails2.0以前は、_formファイルに抜き出して、共用していた記憶が...。)
<%# ビュー: app/views/softwares/new.html.erb %>
<h1>New software</h1>

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

  <p>
    <%= f.label :name %>
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :url %>
    <%= f.text_field :url %>
  </p>
  <p>
    <%= f.submit "Create" %>
  </p>
<% end %>

<%= link_to 'Back', softwares_path %>
<%# ビュー: app/views/softwares/edit.html.erb %>
<h1>Editing software</h1>

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

  <p>
    <%= f.label :name %>
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :url %>
    <%= f.text_field :url %>
  </p>
  <p>
    <%= f.submit "Update" %>
  </p>
<% end %>

<%= link_to 'Show', @software %> |
<%= link_to 'Back', softwares_path %>

ブロック内でのrender :partial記法

Rails2.1からform_forブロック内で以下のように書けるようになった。

<%# ビュー: app/views/softwares/new.html.erb %>
<h1>New software</h1>

<% form_for(@software) do |f| %>
  <%= render :partial=>f %>
  <p>
    <%= f.submit "Create" %>
  </p>
<% end %>

<%= link_to 'Back', softwares_path %>
<%# ビュー: app/views/softwares/edit.html.erb %>
<h1>Editing software</h1>

<% form_for(@software) do |f| %>
  <%= render :partial=>f %>
  <p>
    <%= f.submit "Update" %>
  </p>
<% end %>

<%= link_to 'Show', @software %> |
<%= link_to 'Back', softwares_path %>
<%# ビュー: app/views/softwares/_form.html.erb %>
  <%= form.error_messages %>

  <p>
    <%= form.label :name %>
    <%= form.text_field :name %>
  </p>
  <p>
    <%= form.label :url %>
    <%= form.text_field :url %>
  </p>
  • form_forブロック内でブロック引数を:render :partial=>fのように指定すると...(ブロック引数名はfでも、abcでも、任意の名前でOK)
    • _formファイルを呼び出して描画する。(render :partialの引数がブロック引数の場合は_formファイルの描画という規約になっているようだ。)
    • _formファイルを描画する時には、ブロック引数の内容が変数formに引き継がれているので、<%= form.text_field :name %>のような書き方が可能になり、同じform_forブロック内のように扱える。
  • つまり、ブロック引数がfだとして、以下の書き方は同等なのだ。
<%= render :partial=>f %>
以下と同等
<%= render :partial=>'form', :locals=>{:form=>f} %>


自分がRailsを触り始めた頃からの、一番シンプルな書き方がまた出来るようになって嬉しい!

      • それにしても、もしRails2.1から触り始めたら、この書き方で「うん、なるほど!」とすんなり理解できるのだろうか?(Railsの多少の歴史を知っている今なら納得できるが)自分にとっては超アウンの呼吸的な書き方に見える。おそらく「何で_formファイルが描画されるの?」でまた悩んでしまいそうだ...。

*1:Rails2.0.2から、scaffoldでもnewとeditの共通項目を_formとして抜き出さなくなってしまった。なぜ?