Engine Yard

ブログ

avatar
Rails 3 のルーティングについて (翻訳版)

By | October 3rd, 2012 at 8:10AM
今日のブログ記事はゲスト コミュニティ投稿者の Rizwan Reza 氏が 2010 年 3 月 30 日に執筆したブログ記事の翻訳版です。同氏は Rails コミュニティのアクティブなメンバーであり、最近は膨れ上がった感のある Lighthouse キューのクリーンアップに取り組んでいます。

ちょっと待ってください!これから重要なお知らせをしますが、ちょっとショックかも知れないので、腰を落ち着けて聞いていただければと思います。いいでしょうか? 重要なお知らせとは、「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_urlposts_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 のルーティングはこれまでと次元が違うと言ってもよいと思います。