Rails2でのrouting ActionController::Resources

Resources

ActionController::Resourcesを使用することで、GET、POST、PUT、DELETEに対応したRestfulなリソースが動的に生成される。

  map.resources :products

とconfig/routes.rbに記述すると、以下のリソースが生成される。

Named Route Method URL 対応するコントローラとアクション
products GET /products {:controller=>"products", :action=>"index"}
formatted_products GET /products.:format {:controller=>"products", :action=>"index"}
POST /products {:controller=>"products", :action=>"create"}
POST /products.:format {:controller=>"products", :action=>"create"}
new_product GET /products/new {:controller=>"products", :action=>"new"}
formatted_new_product GET /products/new.:format {:controller=>"products", :action=>"new"}
edit_product GET /products/:id/edit {:controller=>"products", :action=>"edit"}
formatted_edit_product GET /products/:id/edit.:format {:controller=>"products", :action=>"edit"}
product GET /products/:id {:controller=>"products", :action=>"show"}
formatted_product GET /products/:id.:format {:controller=>"products", :action=>"show"}
PUT /products/:id {:controller=>"products", :action=>"update"}
PUT /products/:id.:format {:controller=>"products", :action=>"update"}
DELETE /products/:id {:controller=>"products", :action=>"destroy"}
DELETE /products/:id.:format {:controller=>"products", :action=>"destroy"}
Named Route Helpers
products products_url, hash_for_products_url, products_path, hash_for_products_path
product product_url(id), hash_for_product_url(id), product_path(id), hash_for_product_path(id)
new_product new_product_url, hash_for_new_product_url, new_product_path, hash_for_new_product_path
edit_product edit_product_url(id), hash_for_edit_product_url(id), edit_product_path(id), hash_for_edit_product_path(id)

これらを使うと、例えば以下の用に記述することができます。

redirect_to :contorller => 'products', :action => 'index'
# ↓
redirect_to products_url
<%= link_to "Edit", :controller => 'products', :action => 'edit', :id => @product %>
# ↓
<%= link_to "Edit", edit_product_url(@product) %>


さらに、form_forを使うともっと素敵な事が起こるのです。

・new.html.erb

<h1>New product</h1>

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

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

・edit.html.erb

<h1>Editing product</h1>

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

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

上記の2つのform_forは一見同じに見えますが、それぞれのアクションを実行すると
・/products/new

<form action="/products" class="new_product" id="new_product" method="post">
  <div style="margin:0;padding:0">
    <input name="authenticity_token" type="hidden" value="f0a853363c1b8ec3e060051b00d57b07a0a5bf4c" />
  </div>
  <p>
    <label for="product_name">Name</label><br />
    <input id="product_name" name="product[name]" size="30" type="text" />
  </p>
  <p>
    <label for="product_comment">Comment</label><br />
    <textarea cols="40" id="product_comment" name="product[comment]" rows="20"></textarea>
  </p>
  <p>
    <input id="product_submit" name="commit" type="submit" value="Create" />
  </p>
</form>

・/products/1/edit

<form action="/products/1" class="edit_product" id="edit_product_1" method="post">
  <div style="margin:0;padding:0">
    <input name="_method" type="hidden" value="put" />
    <input name="authenticity_token" type="hidden" value="f0a853363c1b8ec3e060051b00d57b07a0a5bf4c" />
  </div>
  <p>
    <label for="product_name">Name</label><br />
    <input id="product_name" name="product[name]" size="30" type="text" value="test" />
  </p>
  <p>
    <label for="product_comment">Comment</label><br />
    <textarea cols="40" id="product_comment" name="product[comment]" rows="20">testだよ</textarea>

  </p>
  <p>
    <input id="product_submit" name="commit" type="submit" value="Update" />
  </p>
</form>

アクションに応じて、formも動的に変化しているです。素晴らしい。
因みに、見てわかるようにPUTやDELETEはブラウザが対応していないので、隠し要素へput、deleteの値を入れて送信することで実現されています。

new、show、edit、destoryへのリンクは以下のように展開されます。

#new

<%= link_to 'New', new_product_url %>
# ↓
<a href="/products/new">New</a>


# show

<%= link_to 'Show', product_url(@product) %>
# ↓
<a href="/products/1">Show</a>


# edit

<%= link_to 'Edit', edit_product_url(@product) %>
# ↓
<a href="/products/1/edit">Edit</a>


#destory

<%= link_to 'Destroy', product_url(@product), :method => 'delete' %>
# ↓
<a href="/products/1" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);var s = document.createElement('input'); s.setAttribute('type', 'hidden'); s.setAttribute('name', 'authenticity_token'); s.setAttribute('value', 'f0a853363c1b8ec3e060051b00d57b07a0a5bf4c'); f.appendChild(s);f.submit(); };return false;">Destroy</a>

ということでここまでは、scaffoldで生成されるコードで同じものが確認可能なので、実際のコードを確認したい場合は、以下のとおり実行して下さい。

$ ./script/generate scaffold product name:string comment:text


最後にURLへのパラメータの追加は以下の様になります。

<%= link_to, 'Show', :controller => 'products', :action => 'show', :id => @product, :lang => 'jp' %>
# ↓
<%= link_to, 'Show', product_path(@product, {:lang => 'jp'}) %>
# ↓
<a herf="/products/1?lang=jp">Show</a>

関連エントリー