2 Feb 2010

rails ルートの操作

久しぶりにrailsの復習をしています。理解がおざなりだった部分とかも混ぜてちょっとずつちょっとずつ積み立てる予定。しばらくはこの状態が続きそう

今回はルートの操作のメモです。リクエストのルーティングがうまく通っているか確認したり、ルーティングを作成する方法を復習します。

サンプルコードを作ります。フィードを一覧、登録、編集するFeedsControllerを作ります。
rails -d mysql sample_routes ruby script/generate scaffold feeds


config/routes.rbで以下のルート定義が既に行われているものとします。
#関係あるのはこっちだけ
map.resources :feeds

map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'

scaffoldしたのでroutes.rbには既に自動で2行目のRestfulなマッピングが定義されているはずです。4、5行目はrailsコマンドでプロジェクトを作成したときに既に作成されているものです。

まずはscript/consoleを使ってRoutesオブジェクトを取得します。
$ ruby script/consoleLoading
development environment (Rails 2.3.5)
>> rs = ActionController::Routing::Routes


定義されたルートの確認



>>puts rs.routes
GET    /feeds(.:format)?                        {:controller=>"feeds", :action=>"index"}
POST   /feeds(.:format)?                        {:controller=>"feeds", :action=>"create"}
GET    /feeds/new(.:format)?                    {:controller=>"feeds", :action=>"new"}
GET    /feeds/:id/edit(.:format)?               {:controller=>"feeds", :action=>"edit"}
GET    /feeds/:id(.:format)?                    {:controller=>"feeds", :action=>"show"}
PUT    /feeds/:id(.:format)?                    {:controller=>"feeds", :action=>"update"}
DELETE /feeds/:id(.:format)?                    {:controller=>"feeds", :action=>"destroy"}::

出力される項目は左からHTTP Method、ルート定義、割り当てられるコントローラ/アクション。

またはrake taskからでもルート定義を表示できます
$rake routes

#grepが使える
$rake routes|grep feeds


URLから割り当てられるコントローラ、アクションを見つける



>> rs.recognize_path
"/feeds"=> {:controller=>"feeds", :action=>"index"}


混乱を避けるためmethodは指定しておいた方が良いです。
>> rs.recognize_path '/feeds/1'
=> {:controller=>"feeds", :action=>"1"}
>> rs.recognize_path '/feeds/1', :method=>:get
=> {:controller=>"feeds", :action=>"show", :id=>"1"}


また、そもそもコントローラが実装されていない場合はルートはマッチしません
>> rs.recognize_path '/foo'
ActionController::RoutingError:
No route matches "/foo" with {} from /opt/ruby-enterprise-1.8.7/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/routing/recognition_optimisation.rb:66:in 
`recognize_path' from (irb):24


コントローラ、アクションからURLを逆引きする


#コントローラだけ指定すると期待した動作をする。
>> rs.generate :controller => :feeds
=> "/feeds"#idを指定すると定義されていないURLが帰ってくる
>> rs.generate :controller => :feeds, :id => '123'
=> "/feeds?id=123"#recognize_pathでマッチしないルートなのでまあ無視しておこう。。。
>> rs.recognize_path '/feeds?id=123'
ActionController::RoutingError: No route matches "/feeds?id=123"
with {} from /opt/ruby-enterprise-1.8.7/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/routing/recognition_optimisation.rb:66:in 
`recognize_path' from (irb):20


作成前のコントローラのルートを作成する


#use_controllers! [”name_of_controller”]
>> ActionController::Routing.use_controllers! ['entries']
=> ["entries"]
#ルート定義をリロードしないとコントローラが有効にならない
>> load 'config/routes.rb'=> []
#確認
>> rs.recognize_path '/entries/show/1'
=> {:controller=>"entries", :action=>"show", :id=>"1"}
#routes.rbのmapの設定を行っていないのでこうなる。
>> rs.recognize_path '/entries/1'=> {:controller=>"entries", :action=>"1"}


http://api.rubyonrails.org/classes/ActionController/Routing/RouteSet/Mapper.html



ActionController::Routing::Routes.drawのブロック中の操作



準備
>> rs = ActionController::Routing::Routes
>> map = ActionController::Routing::RouteSet::Mapper.new rs


map.root
>> map.root :controller => "welcom", :action => "index"
=>
ANY    /                                        {:controller=>"welcome", :action=>"index"}


map.connect
>>   map.connect ':controller/ajax/:action'
=>
ANY    /:controller/ajax/:action/               {}


map.resources
>>   map.resources :feeds
=>
GET    /feeds(.:format)?                        {:controller=>"feeds", :action=>"index"}
POST   /feeds(.:format)?                        {:controller=>"feeds", :action=>"create"}
GET    /feeds/new(.:format)?                    {:controller=>"feeds", :action=>"new"}
GET    /feeds/:id/edit(.:format)?               {:controller=>"feeds", :action=>"edit"}
GET    /feeds/:id(.:format)?                    {:controller=>"feeds", :action=>"show"}
PUT    /feeds/:id(.:format)?                    {:controller=>"feeds", :action=>"update"}
DELETE /feeds/:id(.:format)?                    {:controller=>"feeds", :action=>"destroy"}


map.resource
>> map.resource :daily_sum
=> 
GET    /daily_sum/new(.:format)?                {:controller=>"daily_sums", :action=>"new"}
GET    /daily_sum/edit(.:format)?               {:controller=>"daily_sums", :action=>"edit"}
GET    /daily_sum(.:format)?                    {:controller=>"daily_sums", :action=>"show"}
PUT    /daily_sum(.:format)?                    {:controller=>"daily_sums", :action=>"update"}
DELETE /daily_sum(.:format)?                    {:controller=>"daily_sums", :action=>"destroy"}
POST   /daily_sum(.:format)?                    {:controller=>"daily_sums", :action=>"create"}

map.resourcesと比較した違い
- indexがない
- リソースが単一なので:idをURLに使用しない

resourceのオプション
- :member, :collection
map.resources :japan :member => {:county => :get}, :collection => {:progress_population => :get}
member => /nihon/:id/history, collection => /nihon/log

map.named_route
>> map.named_route 'error_occurred', '/:controller/internal_server_error', {:action=>"resque"}
=> 
ANY    /:controller/internal_server_error/      {:action=>"resque"}



map.namespace
>> map.namespace :admin do |admin|
>>  admin.resources :users
>>  admin.connect ':controller/:action/:id'
>> end
=>
GET    /admin/users(.:format)?                  {:controller=>"admin/users", :action=>"index"}
POST   /admin/users(.:format)?                  {:controller=>"admin/users", :action=>"create"}
GET    /admin/users/new(.:format)?              {:controller=>"admin/users", :action=>"new"}
GET    /admin/users/:id/edit(.:format)?         {:controller=>"admin/users", :action=>"edit"}
GET    /admin/users/:id(.:format)?              {:controller=>"admin/users", :action=>"show"}
PUT    /admin/users/:id(.:format)?              {:controller=>"admin/users", :action=>"update"}
DELETE /admin/users/:id(.:format)?              {:controller=>"admin/users", :action=>"destroy"}
ANY    /admin/:controller/:action/:id/          {}


map.method_missing(定義されていないメソッド)
>> map.login '/login', :controller => 'sessions', :action => 'new'
>> map.logout '/logout', :controller => 'sessions', :action => 'destroy'
=>
ANY    /login/                                  {:controller=>"sessions", :action=>"new"}
ANY    /logout/                                 {:controller=>"sessions", :action=>"destroy"}