-
Notifications
You must be signed in to change notification settings - Fork 8
浜松rails3道場 routing編
浜松Rails3道場とは、習うより慣れろ方式で、Rails3のツボを浜松のRubyist達へ伝える試みである。
- Ruby on Rails Guides: Rails Routing from the Outside In
- RailsCasts - #203 Routing in Rails 3
- Ruby on Rails Documentation
練習用のRailsプロジェクトを作る。参考環境: Ruby 1.9.2, Rails 3.0.7
% rails new routing-dojo
% bundle install
HTTPリクエスト(URL、メソッド)を受け取って、コントローラーのアクションを呼び出す仕組み。また、パスやURLを生成し文字列によるこれらのハードコードを回避することができる。
The Rails router recognizes URLs and dispatches them to a controller’s action. It can also generate paths and URLs, avoiding the need to hardcode strings in your views.(Ruby on Rails Guides)
確認用のコントローラーを生成
rails g controller catalog view
設定ファイルにrouteを追加する。
# config/routes.rb
match 'products/:id' => 'catalog#view'
Viewで使う
<% # app/views/catalog/view.html.erb %>
<%= link_to 'Product: 2', :controller => 'catalog', :action => 'view', :id => 2 %>
Controllerで使う
# app/controllers/catalog_controller.rb
redirect_to :controller => 'catalog', :action => 'view', :id => 3
- サーバーを起動しブラウザで確認
- rake routes タスクで確認
- rails console コマンドで確認
- テストを書いて確認
% rake routes
...
/products/:id(.:format) {:controller=>"catalog", :action=>"view"}
...
% rails console
Loading development environment (Rails 3.0.7)
> r = ActionController::Routing::Routes
> r.recognize_path '/products/1'
=> {:controller=>"catalog", :action=>"view", :id=>"1"}
> r.generate :controller => 'catalog', :action => 'view', :id => '2'
=> "/products/2"
Railsのコントローラーのテストでは、Routingをテストするための3種類のビルトインアサーションが用意されている。
# test/functional/catalog_controller_test.rb
# assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
assert_generates "/products/1", :controller => 'catalog', :action => 'view', :id => '1'
# assert_recognizes(expected_options, path, extras={}, message=nil)
assert_recognizes({ :controller => "catalog", :action => "view", :id => "2" }, "/products/2")
# assert_routing(path, options, defaults={}, extras={}, message=nil)
assert_routing "/products/3", { :controller => "catalog", :action => "view", :id => "3" }
url_forなどを使いビューのテストでも検証することができる。
# test/unit/helpers/catalog_helper_test.rb
assert_equal '/products/1', url_for(:controller => 'catalog', :action => 'view', :id => '1')
- 'products/:id' の productsと:id の部分を変更してみよう。
- ビューで :controller や :action などのキーをTypoしてみよう。
Test::Unitの代わりにRSpecを使うと専用のRouting specsが生成される。
# spec/routing/catalog_routing.spec
describe "routing to catalog" do
it "routes /products/:id to catalog#show for id" do
{ :get => "/products/1" }.should route_to(
:controller => "catalog",
:action => "view",
:id => "1"
)
end
end
Routeには名前を付けることができる。
# config/routes.rb
match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
# test/functional/catalog_controller_test.rb
assert_equal "/products/1/purchase", purchase_path(:id => '1')
assert_equal "http://test.host/products/2/purchase", purchase_url(:id => 2)
アプリケーションのrootでは専用のショートカットが使える。
root :to => "welcome#index" # root_path => '/'
- 'catalog#view' へのRoutingにも名前を付けてみよう。
- ビューでxxx_path, xxx_url をTypoしてみよう。
名前なきRoute使うべからず! 名前付きの方がトラブルが少なく、テストもしやすい。
Resource Routingを使うことによって、レストフルなURLを通じてリソースフルなコントローラーにアクセスするためのRouteを素早く宣言できる。index, show, new, edit, create, update, deleteといったリソースを操作するためのお決まりのRouteを幾つも書かなくても、たった1行で済ませることができる。
RailsではResource Routingの規約に基づいて、HTTPメソッドとURLをコントローラーのアクションにマッピングする。
まず、設定ファイルに以下のようなRouteを追加する。
# config/route.rb
resources :products
これにより、PruductsControllerの7つのアクションとHTTPリクエストをマッピングする以下のようなRouteが追加される。
HTTPメソッド | リクエストPath | アクション | 一般的な動作 |
---|---|---|---|
GET | /products | index | Productのリストを表示する |
GET | /products/new | new | 新しいProductを追加するためのフォームを表示する |
POST | /products | create | 新しいProductを作成する |
GET | /products/:id | show | 指定されたProductを表示する |
GET | /products/:id/edit | edit | 指定されたProductを編集するためのフォームを表示する |
PUT | /products/:id | update | 指定されたProductを更新する |
DELETE | /products/:id | destroy | 指定されたProductを削除する |
Resource Routingで追加されたRouteは以下のような名前で呼び出すことができる。
products_path # => /products
new_product_path # => /products/new
edit_product_path(id) # => /products/:id/edit (例: edit_product_path(10) => /products/10/edit)
product_path(id) # => /products/:id (例: product_path(10) => /products/10)
- Resource Routing によって呼び出される7つのアクションを持つProductsControllerを追加して動作確認やテストをしてみよう。
- 同じリクエストPathを持ち、HTTPメソッドが違うRouteへのリンクをビューに追加してみよう。
可能なかぎりResource Routingを使うべし! 名前付けで悩まなくてすむ。規約により共通の理解が得やすい。Scaffoldがそのまま使える。
複数のコントローラーを単一のネームスペースに配置したい場合がある。例えば、管理者用に幾つかのリソースフルなコントローラーを Admin::ネームスペースに配置したいとする。つまり、 app/controllers/admin ディレクトリの中にコントローラーを作成した場合は以下のように記述すれば良い。
namespace :admin do
resources :products
end
これにより、以下のようなRouteが追加される。
HTTPメソッド | リクエストPath | アクション | ヘルパー |
---|---|---|---|
GET | /admin/products | index | admin_products_path |
GET | /admin/products/new | new | new_admin_product_path |
POST | /admin/products | create | admin_products_path |
GET | /admin/products/1 | show | admin_product_path(id) |
GET | /admin/products/1/edit | edit | edit_admin_product_path(id) |
PUT | /admin/products/1 | update | admin_product_path(id) |
DELETE | /admin/products/1 | destroy | admin_product_path(id) |
- Admin::ProductsControllerを追加して動作確認やテストをしてみよう。
It’s common to have resources that are logically children of other resources. For example, suppose your application includes these models:
class Magazine < ActiveRecord::Base has_many :ads end
class Ad < ActiveRecord::Base belongs_to :magazine end Nested routes allow you to capture this relationship in your routing. In this case, you could include this route declaration:
resources :magazines do resources :ads end In addition to the routes for magazines, this declaration will also route ads to an AdsController. The ad URLs require a magazine:
Verb Path action used for GET /magazines/1/ads index display a list of all ads for a specific magazine GET /magazines/1/ads/new new return an HTML form for creating a new ad belonging to a specific magazine POST /magazines/1/ads create create a new ad belonging to a specific magazine GET /magazines/1/ads/1 show display a specific ad belonging to a specific magazine GET /magazines/1/ads/1/edit edit return an HTML form for editing an ad belonging to a specific magazine PUT /magazines/1/ads/1 update update a specific ad belonging to a specific magazine DELETE /magazines/1/ads/1 destroy delete a specific ad belonging to a specific magazine