Rails 3 のルーティング定義について

Rails 3 のルーティング定義について


Rails 3のルーティングで気になったところについて。

基本形

map.connect から match メソッドに変更。オプションも下記のように変更。

# Rails 2

map.connect 'products/:id', :controller => 'products', :action => 'view'

# Rails 3

match 'products/:id', :to => 'catalog#view'

# :to は省略可能

match "/account" => "account#index"

# :controller/:action 形式であればさらに省略可能

match "account/overview"
Named Routes

asオプションで指定するように変更になった。

# Rails 2

map.logout '/logout', :controller => 'sessions', :action => 'destroy'

# Rails 3

match 'logout', :to => 'sessions#destroy', :as => "logout"

メソッドの指定

get(post,put,delete)メソッドで指定できるように。またはviaオプションで。

# Rails 2

map.connect "account/overview", :controller => "account", :action => "overview", :conditions => {:method => :get }

# Rails 3

get "account/overview"

# または:viaオプションで指定

match "account/overview", :to => "account#overview", :via => :get

デフォルトパラメータの指定

defaults オプションでデフォルトで渡すパラメータを指定できる

match 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' }

RESTfulなルーティング

resourcesには複数の引数が渡せる(これはRails 2 からできたみたい)

resources :photos, :books, :videos

これまでresourcesメソッドのオプションとして扱われていたものがブロック内のメソッドとして使えるように。

resources :products do
collection do
get :sold
post :on_offer
end
end

インラインでも指定出来る

resources :products do
get :sold, :on => :member
end
resourcesで出来るアクションのルーティングをブロックの中で上書きすることが出来るみたい。

resources :sessions do
get :create
end

asオプションを使うとnamed routesの名前を変更できる

# devices_url で products#index にアクセスできる

resources :products, :as => 'devices'
pathも変えたければ下記のようにする

# /devices にアクセスすると products#index へ

resources :devices, :controller => "products"

onlyとexceptで不必要なルーティングを作成しないように出来る(これは Rails 2.2.1 以降 と 3 で共通)

resources :posts, :except => [:index]

Resourceのネストは Rails 2 と 3 で同じ。

resources :projects do
resources :tasks, :people
end

singularも Rails 2と同じ

resources :teeth, :singular => "tooth"

Namespaceを定義

Admin::PostsController 用のRESTfulなルーティングは下記のように定義できる

namespace :admin do
resources :posts
end

パスは /admin/posts みたいになる。/admin を削除したければ下記のようにする。

scope :module => "admin" do
resources :posts, :comments
end

一行で書きたい場合は下記のようにする。

resources :posts, :module => "admin"

逆に/admin/photosでPostsControllerにアクセスしたいときは下記のようにする

scope "/admin" do
resources :posts, :comments
end

一行で書きたいときには下記のようにする

resources :posts, :path => "/admin"

newやeditのアクションのパスを変更するにはpath_namesオプションを使う。

resources :projects, :path_names => { :edit => 'cambiar' }

scope を使えばブロック中全部のルーティングに適用できる

scope :path_names => { :new => "make" } do
# rest of your routes
end

リダイレクト

redirectメソッドを使うことで、ルーティング上でリダイレクトを設定できる。ただし他のメソッドと違い、ブロック中では使えないらしい。

# /foo/1 にアクセスしたら /bar/1sにリダイレクトする

match "/foo/:id" => redirect("/bar/%{id}s")

# /account/proc/john にアクセスしたら /johnsにリダイレクトする

match 'account/proc/:name' => redirect {|params| "/#{params[:name].pluralize}" }

# request オブジェクトもブロックパラメータとして渡せる

match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" }

ホストのドメインを登録していなければ、Railsはリクエストベースでドメインを判断する。

Rack

ルーティング先にRackアプリケーションを指定できる。

match "/application.js" => Sprockets

"posts#index"のように指定したときは、最終的にRackアプリを返す PostsController.action(:index) が呼ばれるようになっているので全部のルーティングで最終的には
Rack アプリが指定されていることになる。

Constraints

requirements オプションから constraints オプションに変わった。

match "/posts/show/:id" => "posts#index", :constraints => {:id => /\d/}

上記のような場合はconstraintsを省略できる

match "/posts/show/:id" => "posts#index", :id => /\d/

resources なルーティングには id の constraints がかけられる。

resources :photos, :constraints => {:id => /[A-Z][A-Z][0-9]+/}

もちろんブロックをとるメソッドも用意されている

constraints(:id => /[A-Z][A-Z][0-9]+/) do
resources :photos
resources :accounts
end

Requestオブジェクトのメソッドのうち、Stringを返す全てのメソッドに constraints を設定できる。例えば、Request#subdomainが定義されているので、下記のようにするとサブドメインを利用したルーティング定義が出来る*1

match "photo", :constraints => {:subdomain => "admin"}

RegExpもキーにとれる

match "photo", :constraints => {:subdomain => /user/}

もちろんブロックをとるメソッドも用意されている

namespace "admin" do
constraints :subdomain => "admin" do
resources :photos
end
end

constraints 用のオブジェクトを作ってルーティングに使用することが出来る。オブジェクトはmathces?が定義されていればあとは特に条件はないみたい。

class BlacklistConstraint
def initialize
@ips = Blacklist.retrieve_ips
end

def matches?(request)
@ips.include?(request.remote_ip)
end
end


TwitterClone::Application.routes.draw do
match "*path" => "blacklist#index",
:constraints => BlacklistConstraint.new
end

オプション

括弧でくくったパスはオプションとして扱われる

match 'posts(/new)', :to => 'posts#create'

残り全部とマッチさせる

残り全部とマッチさせる書き方は Rails 2 と同じくアスタリスクを使う。

match 'photo/*other' => 'photos#unknown'
/photo 以下のパスは params[:other] に入る。

感想

Rails 3のルーティング定義は、2の頃ではできなかったいろんな書き方が定義されてこれまで複雑な記述をしていた定義がエレガントに書けるようになり嬉しい反面、出来ることが多すぎて覚えるのが大変そうですね><

参考

The Lowdown on Routes in Rails 3 | Engine Yard Ruby on Rails Blog

Ruby on Rails Guides: Rails Routing from the Outside In