ちょっと待ってください!これから重要なお知らせをしますが、ちょっとショックかも知れないので、腰を落ち着けて聞いていただければと思います。いいでしょうか? 重要なお知らせとは、「Rails 2 でのルーティング取り扱いの知識はすべて過去のものになった」ということです。Rails 3 では、これまで身に付けたことを全部忘れて、新しいルーティング方法を腕まくりで学ぶ必要があります。ルーティングは高速でクリーンになり、ずっと Ruby に近くなっています。
今回の投稿では、Rails 3 のルーティングの基礎を順に見ていきます。ルーティングの方法は書き直されていますが、それにはもっともな理由があります。以下の説明をお読みになれば、きっと納得いただけるはずです。
まずは次のコードを見てください。新しい DSL がうまく生かされています。
resources :products do resource :category member do post :short end collection do get :long end end match "/posts/github" => redirect("http://github.com/rails.atom") |
これを従来の方法と比べてみましょう。
map.resources :products, :member => {:short => :post}, :collection => {:long => :get} do |products| products.resource :category end |
Rails 3 の例はずっとクリーンで Ruby に近い形となっているのがわかりますね。ではさっそく、実際に Rails 3 で各種のルーティングを定義する方法についてひと通り説明していきたいと思います。
既定のルーティング
Rails 3 の既定のルーティング match '/:controller(/:action(/:id))' は、オプションのパラメーターがかっこで囲まれ、ずっと明示的になっています。
標準ルーティング
コントローラーとアクションに異なるキーを定義せず、catalog#view だけを使います。とても簡単ですね。
match 'products/:id', :to => 'catalog#view' |
Rails 2 では次のようにしていました。
map.connect 'products/:id', :controller => 'products', :action => 'view' |
名前を指定したルーティング
link_to などのヘルパー メソッドでアクションやコントローラーに対して手動でハッシュを定義する代わりに、名前を指定したルーティングによって posts_url や posts_path といったヘルパーを生成します。
match 'logout', :to => 'sessions#destroy', :as => "logout" |
キー :as はヘルパーを生成するための名前を指定します。Rails 2 では次のようにしていました。
map.logout '/logout', :controller => 'sessions', :action => 'destroy' |
空のルーティング
ウェブサイトのルート ディレクトリは空のルーティングです。Rails 2 ではこのディレクトリへの便利なショートカットが追加されましたが、Rails 3 ではさらにシンプルになっています。
# Rails 3 root :to => 'welcome#show' # Rails 2 map.root :controller => "welcome", :action => 'show' |
省略形
Rails 3 の改訂されたルーティングでは、よく使われるルーティングの便利なショートカットが利用できます。2 種類の省略形があります。まず、:to の省略形を使用すると、次のように :to キーをスキップしてマッチャーへのルーティングを直接に指定することができます。
match "/account" => "account#index" match "/info" => "projects#info", :as => "info" |
次に、match の省略形を使用すると、パスとコントローラーを、そのアクションと共に同時に定義できます。
match "account/overview" # identical to match "account/overview", :to => "account#overview" |
動詞ルーティング
:via を使用してルートを HTTP 要求のみに限定できますが、動詞ルーティングを使用するとさらに便利になります。それだけでなく、省略形を一緒に使用することもできます。
get "account/overview" # identical to match "account/overview", :to => "account#overview", :via => "get" |
キー
match メソッド (および動詞の省略形) にはいくつかのオプション キーがあります。
:as
:as キーはルーティングに名前を付けます。すると、url_for を利用できるときはいつでも、名前指定のルーティング ヘルパーを使用できるようになります (コントローラー、テスト、メーラーなど)。リソース ルーティングは (resources ヘルパーを使用して) Rails 2.3 と同様に名前指定のルーティングを自動的に作成します。
match "account/overview/:id", :as => "overview" # in your controller overview_path(12) #=> "/account/overview/12" |
:via
一連の動詞を指定して、ルーティングに対してそれらの HTTP 要求のみが受け入れられるようにします。
match "account/setup", :via => [:get, :post] |
Rack
Rack は Ruby フレームワークに統合 API を提供する、ウェブ サーバーとの便利なインターフェイスです。今日ではほとんどの Ruby フレームワークが Rack を基盤として構築されています。最近は Rack のサポートが組み込まれているので、アプリケーションを Rails 固有のものとする必要はありません。したがって、Sinatra や Cramp など、任意の Rack 対応フレームワークによってアプリケーションの各部を処理できます。Rails スタックを完全にスキップして要求を Rack アプリケーションに渡すことが可能です。
次に示すのは Sinatra アプリケーションの例です。
class HomeApp < Sinatra::Base get "/" do "Hello World!" end end Rizwan::Application.routes do match "/home", :to => HomeApp end |
次に示すのは Rack アプリケーションの例です。
match "/foo", :to => proc {|env| [200, {}, ["Hello world"]] } match 'rocketeer.js' => ::TestRoutingMapper::RocketeerApp RocketeerApp = lambda { |env| [200, {"Content-Type" => "text/html"}, ["javascripts"]] } |
リソースベースのルーティング
Rails 1.2 以降、ルーターの使用にはリソースベースのルーティングが推奨されてきました。Rails コア チームはこの事実を認識した上で、さらに便利な強化機能をいくつか追加しました。次に示すのは、Rails 3 における典型的な RESTful なルーティングです。
resources :products |
このコードは、頻繁に使用される便利なヘルパーをすべて生成し、さらに URL を正しくルーティングします。従来と同様、1 つの行に複数のリソースを追加することもできます。
resources :products, :posts, :categories |
RESTful な追加アクション
RESTful なアーキテクチャが提供する 7 つのアクションだけでなく、リソースに追加のアクションを定義することもできます。しかし、1 つのリソースにいくつものアクション定義する場合、それぞれが個別のリソースとして処理される可能性があるので十分な注意が必要です。
RESTful なアクションは、いくつかの方法でこのリソースに追加することができます。以下に挙げるのは、collection ブロック内の RESTful なアクションの例です。
resources :products do collection do get :sold post :on_offer end end |
また、次のようなインライン メンバーの RESTful なアクションもあります。
resources :products do get :sold, :on => :member end |
それだけでなく、7 つの既定の RESTful アクションのスコープを再定義して拡張することも可能です。
resources :session do collection do get :create end end |
create アクションは通常 POST 要求のみを受け入れますが、このコードによって GET 要求も受け入れ可能となります。
resource :session do get :create end |
入れ子リソース
Rails 2 において入れ子リソースは、ブロックにより定義するか、:has_many または :has_one キーを使用して定義していました。この両者がブロックで置き換えられ、関連リソースの定義を行うインターフェイスがより Ruby に近くなりました。
次に示すのは、多くのタスクと人名が含まれるプロジェクトのルーティングです。
resources :projects do resources :tasks, :people end |
名前空間ベースのリソース
これは、フォルダー内にリソースを定義する場合に特に便利で、次に示すように実に明快です。
namespace :admin do resources :projects end |
リソースの名前変更
:as キーを使用してリソースの名前を変更することもできます。次のコードでは、リソース ベースのルーティングで :as を使用して、デバイスへの製品パスを変更します。
namespace :forum do resources :products, :as => 'devices' do resources :questions end end |
リソースの制限
リソースを指定のアクションのみに制限することができます。
resources :posts, :except => [:index] resources :posts, :only => [:new, :create] |
パス名の変更
特定の REST アクションに対して異なるパス名を定義できます。これは RESTful ルーティングのカスタマイズに役立ちます。次のコードは、/projects/1/cambiar を編集アクションにルーティングします。
resources :projects, :path_names => { :edit => 'cambiar' } |
リダイレクト (redirect) メソッド
Rails 3 で新しく追加された redirect メソッドは、従来とは桁違いに便利になっています。たとえば、指定された任意のパスにリダイレクトを行って、最終的には完全な URI へと渡すこともできます。これは redirect_routing などの Rails プラグインで従来行われてきた機能です。
また、redirect メソッドでは汎用アクションも導入しています。汎用アクションは Rails 3 に特有の機能で、redirect に渡される内容に応じて、複数の複雑なパスに同じアクションを提供する方法です。
次のコードは /foo/1 を /bar/1s にリダイレクトします。
match "/foo/:id", :to => redirect("/bar/%{id}s") |
次のコードは /account/proc/john を /johns にリダイレクトします。
match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" } |
redirect は、他の制限やスコープと異なり、ブロック内では使用できない点に注意してください。
制限 (constraints) メソッド
constraints を使用して、ルーティングにおけるパス セグメントの要件を指定できます。また、いくつかのメソッドを使用してある事項が特定条件に一致するかどうかを確認することも可能です。たとえば、要求が AJAX かどうかを確認するルーティングなどが定義できます。
次のコードでは正規表現を使用して、1 桁の ID のみを受け入れるようにルーティングが制限されています。
match "/posts/show/:id", :to => "posts#index", :constraints => {:id => /\d/} |
スコープ (scope) メソッド
scope メソッドに単一の記号を渡すと、その記号はコントローラーとして想定されます。引数が文字列の場合には、scope メソッドはその文字列をパスの冒頭に追加します。
scope には path_segments も指定できます。これは制限が可能なので、ルーティングの柔軟性が高まります。
controller :articles do scope '/articles', :name_prefix => 'article' do scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do match '/:id', :to => :with_id end end end scope :posts, :name_prefix => "posts" do match "/:action", :as => "action" end scope ':access_token', :constraints => { :access_token => /\w{5,5}/ } do # See constraint here resources :rooms end |
ここでわかるように、scope の引数として文字列を渡すとその文字列はパスの冒頭に追加されます。Rails 2 では path_prefix を使ってこれを行っていました。name_prefix は基本的に以前と同じです。
オプションのセグメント
以前のバージョンの Rails と異なり、パス セグメントはルーティングでオプションとできるようになりました。オプションのセグメントは、アクションにパラメーターとして渡されるパス セグメントである必要はありません。既定のルーティングは、オプションのセグメントの良い使用例です。次の例では /posts/new と /posts の両方が posts コントローラーの作成アクションにリダイレクトされていますが、/posts/edit は機能しません。
match 'posts(/new)', :to => 'posts#create' |
次に示すのは、リソースの前にパスを追加できるオプションのパス スコープです。
scope '(:locale)', :locale => /en|pl/ do resources :descriptions end |
パーベイシブ ブロック
例からも明らかなように、Rails 3 のルーティングでは、ブロックを通常渡すほぼすべてのメソッドに対しパーベイシブ ブロックが使用され、routes.rb における DRY の実現を助けます。
controller :posts do match 'export', :to => :new, :as => :export_request match '/:action' end |
現時点でこれらのメソッドをすべて使用する Rails 開発者はまずいませんが、ニーズが生じたとき、つまり、より複雑なルーティングを定義する必要が出てきたときに、上記の情報を知っておくと役に立ちます。この機能は既に組み込まれているので、プラグインを使ったり細工をする必要はありません。Rails 3 のルーティングはこれまでと次元が違うと言ってもよいと思います。




