Railsのルーティング

このガイドでは、Railsルーティング(経路)のユーザー向けの機能について説明します。 このガイドを読むことで、次の事が学べるはずです。

  • routes.rb内コードの解釈の仕方について
  • resourceful(推奨)な、またはmatchメソッドを使用した独自のルーティングの組み立て方について
  • 何のパラメータをアクションが受け取る事を期待するのか
  • routeヘルパーを使用したパスとURLの自動生成の方法について
  • 制約やRackエンドポイントのような高度なテクニックについて

1. Railsルーターの目的

RailsルーターはURLを解析し、それぞれをコントローラーのアクションに遷移させます。 また、ビュー内においてハードコーディングを必要とせずに、パスとURLを生成することを可能にしてくれます。

1.1 URLからコードへの橋渡し

Railsアプリケーションが次のリクエストを受け取った場合、

GET /patients/17

ルーターに対し、これにマッチするコントローラーアクションが無いかを訪ねます。 もし最初に下記のルートにマッチした場合、

get '/patients/:id', to: 'patients#show'

リクエストはpatientsコントローラーのshowアクションに、 paramsの値{id: '17'}付きで遷移させられます。

1.2 コードからパスとURLを生成

パスとURLを生成することも出来ます。 もし、上記の例を次のように修正し、

get '/patients/:id', to: 'patients#show', as: 'patient'

アプリケーションのコントローラー内には下記のコードを、

@patient = Patient.find(17)

そして対応するビューにこれを含めると、

<%= link_to 'Patient Record', patient_path(@patient) %>

ルーターは/patients/17のパスを生成します。 これはビューが脆くなること防ぎ、コードを分かりやすくしてくれます。 ルートヘルパー内では、idの指定が不要であることに注意してください。

2. Resourceルーティング: Railsデフォルト

ブラウザは、RailsがGETPOSTPATCHPUTDELETEのような、 特定のHTTPメソッドを使用してURLのためのリクエストを作成することによってページにリクエストします。 各メソッドはリソース上の操作を実行するメソッドです。 リソースのルートは、単一コントローラー内のアクションに関連する複数のリクエストをマッピングします。

Railsアプリケーションが次のようなリクエストを受け取ると、

DELETE /photos/17

ルーターにマッピングするコントローラーアクションを問い合わせます。 もし、次のルートが最初にマッチすると、

resources :photos

Railsはリクエストをphotosコントローラーのdestroyメソッドへ paramsの値{ id: '17' }付きで遷移させます。

2.2 CRUD、動詞、アクション

RailsでのResourcefulなルート(経路)は、HTTPの動詞(verbs)とURLを、コントローラーのアクションへのマッピングを提供します。 慣習に従い、各アクションは特定のデータベースのCRUD操作にもマッピングされます。 下記のような1行をルーティングファイルに書き込みます。

resources :photos

アプリケーション内に7つの異なるルートが作成され、全てPhotosコントローラーへマッピングされます。

HTTP動詞 パス アクション 用途
GET /photos index 全てのphotoを一覧表示します。
GET /photos/new new 新しいphotoを作成するためのHTMLフォームを返します。
POST /photos create photoを新規作成します。
GET /photos/:id show 特定のphotoを表示します。
GET /photos/:id/edit edit photoを編集するためのHTMLフォームを返します。
PATCH/PUT /photos/:id update 特定のphotoを更新します。
DELETE /photos/:id destroy 特定のphotoを削除します。

ルーターはHTTP動詞とURLの組み合わせでリクエストのマッチを行うため、 4つのURLが異なるアクションにマッピングされます。

Railsのルートは、指定された順にマッチするため、もしresources :photosを、 get 'photos/poll'の上に配置していると、この行にマッチする前に、 showアクションは先にresourcesにマッチします。 これを修正するには、その行をresources行の上に配置します。

2.3 パスとURLヘルパー

Resourcefulの経路の作成により、コントローラー内で幾つかのヘルパーが使用できるようになります。 resources :photosの場合、

  • photos_pathは、/photosを返します。
  • new_photo_pathは、/photos/newを返します。
  • edit_photo_path(:id)は、/photos/:id/editを返します。 (例えば、edit_photo_path(10)は/photos/10/editを返します。)
  • photo_path(:id)は、/photos/:idを返します。 (例えば、photo_path(10)は/photos/10を返します。)

これらの各ヘルパーは、対応する_urlヘルパー(photos_urlのような)を持ち、 現在のホスト名、ポート、パスを補った同じものを返します。

2.4 一度に複数のresourceの定義

もし複数のresourceの経路の作成が必要な場合、1つのresourcesの呼び出しだけで、 全て定義することが可能です。

resources :photos, :books, :videos

これは下記と同じように動作します。

resources :photos
resources :books
resources :videos

2.5 単数Resources

IDを参照すること無く、常に顧客情報を参照したいケースがあるかもしれません。 例えば、常にログイン中のユーザーのプロフィールを/profileで表示したいと考えたとします。 この場合、単数のリソースを使用して/profile(/profile/:idでは無く)をshowアクションにマッピングすることが出来ます。

get 'profile', to: 'users#show'

Stringmatchへ渡すことは、controller#action形式が期待されますが、 Symbolが渡されると直接アクションにマッピングされます。

get 'profile', to: :show

下記のResourcefulなルートは、

resource :geocoder

resourcesではなく、複数形のsが無い、resourceである事に注意してください。

Geocodersコントローラーへマッピングする6つの異なるルートを作成します。

HTTP動詞 パス アクション 用途
GET /geocoder/new new 新しいgeocoderを作成するためのHTMLフォームを返します。
POST /geocoder create geocoderを新規作成します。
GET /geocoder show 1つだけのgeocoderの情報を表示します。
GET /geocoder/edit edit geocoderを編集するためのHTMLフォームを返します。
PATCH/PUT /geocoder update 1つだけのgeocoderの情報を更新します。
DELETE /geocoder destroy geocoderを削除します。

あなたは、同じコントローラーに単数ルート(/account)と複数ルート(/accounts/45)を使用したいかもしれないため、 単数resourcesを複数コントローラーにマッピングします。 そのため、例えばresource :photoresources :photosは単数と複数のルートの両方を作成し、 同じコントローラー(PhotosController)にマッピングします。(翻訳に自信なし)

単数resourceのルートは、下記のようなヘルパーを生成します。

  • new_geocoder_path/geocoder/newを返します。
  • edit_geocoder_path/geocoder/editを返します。
  • geocoder_path/geocoderを返します。

複数resourcesと同様、同じヘルパーの最後に_urlを付けると、 現在のホスト名、ポート、パスを補った同じものを返します。

2.6 コントローラーへの名前空間付けとルーティング

コントローラーを名前空間を使用して整理したいケースがあるかもしれません。 多くの場合、管理者系のコントローラーをAdmin::名前空間下でグループ化したいと考えると思います。 app/controllers/adminディレクトリ下にこれらのコントローラーを配置して、 ルーター内でこれらを一緒のグループにすることが可能です。

namespace :admin do
  resources :posts, :comments
end

これは、各postscommentsのコントローラーのルートを生成します。 Admin::PostsControllerのために、Railsは下記のものを生成します。

HTTP動詞 パス アクション ヘルパー
GET /admin/posts index admin_posts_path
GET /admin/posts/new new new_admin_post_path
POST /admin/posts create admin_posts_path
GET /admin/posts/:id show admin_post_path(:id)
GET /admin/posts/:id/edit edit edit_admin_post_path(:id)
PATCH/PUT /admin/posts/:id update admin_post_path(:id)
DELETE /admin/posts/:id destroy admin_post_path(:id)

もし、/posts(/adminを付けずに)をAdmin::PostsControllerへのルートにしたければ、 下記のように使用することが出来ます。

scope module: 'admin' do
  resources :posts, :comments
end

また、1つであれば次のように書くことが出来ます。

resources :posts, module: 'admin'

もし、/admin/postsPostsController(Admin::モジュール接頭辞無しで)へのルートにしたいのであれば、 次のように書くことが出来ます。

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

また、1つであれば次のように書くことが出来ます。

resources :posts, path: '/admin/posts'

いずれの場合も、名前が付けられたルートはscopeを使用しなかった場合のものも残します。(翻訳に自信なし) 最後のケースでは、下記のパスがPostsControllerにマッピングされます。

HTTP動詞 パス アクション ヘルパー
GET /admin/posts index posts_path
GET /admin/posts/new new new_post_path
POST /admin/posts create posts_path
GET /admin/posts/:id show post_path(:id)
GET /admin/posts/:id/edit edit post_path(:id)
PATCH/PUT /admin/posts/:id update post_path(:id)
DELETE /admin/posts/:id destroy post_path(:id)

2.7 入れ子のResources

resourcesが理論的に他のresourcesの子になるという事は、よくある事です。 例えば、あなたのアプリケーションが次のモデルを含むと仮定すると、

class Magazine < ActiveRecord::Base
  has_many :ads
end

class Ad < ActiveRecord::Base
  belongs_to :magazine
end

入れ子のルートは、あなたのルーティング内において、この関連性を適用出来るようにしてくれます。 このケースでは、下記のルート宣言を含めることが出来ます。

resources :magazines do
  resources :ads
end

magazines(雑誌)へのルートに加えて、この宣言はads(広告)のAdsControllerへのルートも宣言します。 adのURLはmagazineを必要とします。

HTTP動詞 パス アクション 用途
GET /magazines/:magazine_id/ads index 特定の雑誌(magazine)の全ての広告(ads)を一覧表示します。
GET /magazines/:magazine_id/ads/new new 特定の雑誌に付属する新しい広告を作成するためのHTMLフォームを返します。
POST /magazines/:magazine_id/ads create 特定の雑誌に付属する広告を新規作成します。
GET /magazines/:magazine_id/ads/:id show 特定の雑誌に付属する特定の広告を表示します。
GET /magazines/:magazine_id/ads/:id/edit edit 特定の雑誌に付属する特定の広告を編集するためのHTMLフォームを返します。
PATCH/PUT /magazines/:magazine_id/ads/:id update 特定の雑誌に付属する特定の広告を更新します。
DELETE /magazines/:magazine_id/ads/:id destroy 特定の雑誌に付属する特定の広告を削除します。

これは、magazine_ads_urledit_magazine_ad_pathのようなルーティングヘルパーも作成します。 これらのヘルパーは1つ目の引数としてMagazineのインスタンスを取得します。(magazine_ads_url(@magazine))

2.7.1 入れ子の限界

お望みであれば、他の入れ子のresources内に、更にresourcesを入れ子にすることが出来ます。 下記はその例になります。

resources :publishers do
  resources :magazines do
    resources :photos
  end
end

ただし、深い入れ子のresourcesはすぐに破綻します。 このケースでは、例えば、アプリケーションは次のようなパスを解析することになります。

/publishers/1/magazines/2/photos/3

対応するルートヘルパーはpublisher_magazine_photo_urlになり、 各3つの階層全てでそれぞれのオブジェクトが必要になります。 実際これは、Jamis Buckの良質なRails設計のための指南による、 人気記事で書かれているように、 混乱を引き起こすのに十分な状況であると言えます。(翻訳に自信なし)

Resourcesは、1階層よりも深い入れ子を作るべきではありません。

2.7.2 浅い入れ子

深い入れ子を避ける1つの方法として(上記で推奨しているように)、親のスコープ下にコレクションのアクションを生成し、 階層のようでありながら、各アクションが入れ子にならないようにします。 言い換えると、下記のように一意のidのresourceのために、最小の情報によるルートだけを構築するという事になります。

resources :posts do
  resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]

この考えに沿うと、説明的なルートと深い入れ子の間のバランスを上手く取ることが出来ます。 また、:shallowを使用した、上記の略記文法が存在します。

resources :posts do
  resources :comments, shallow: true
end

これは最初の例と全く同じルートを生成します。 親resource内に:shallowオプションを指定することも可能で、 全ての入れ子のresourcesにshallowが適用されます。

resources :posts, shallow: true do
  resources :comments
  resources :quotes
  resources :drafts
end

DSLのshallowメソッドは、内部の各入れ子がshallowであるスコープを作成します。 これは前述した例と同じルートを生成します。

shallow do
  resources :posts do
    resources :comments
    resources :quotes
    resources :drafts
  end
end

shallowルートをカスタマイズするscopeのために、2つのオプションが存在します。 :shallow_pathは、指定したパラメーターを使用して各パスに接頭辞を付けます。

scope shallow_path: "sekret" do
  resources :posts do
    resources :comments, shallow: true
  end
end

このcommentsのresourceは、下記のルートを生成します。

HTTP動詞 パス ヘルパー
GET /posts/:post_id/comments(.:format) post_comments
GET /posts/:post_id/comments(.:format) post_comments
POST /posts/:post_id/comments/new(.:format) new_post_comment
GET /sekret/comments/:id/edit(.:format) edit_comment
GET /sekret/comments/:id(.:format) comment
PATCH/PUT /sekret/comments/:id(.:format) comment
DELETE /sekret/comments/:id(.:format) comment

:shallow_prefixオプションは、名前付けされたヘルパーへ特定のパラメーターを追加します。

scope shallow_prefix: "sekret" do
  resources :posts do
    resources :comments, shallow: true
  end
end

このcommentsのresourceは、このために生成される下記のルートを持つことになります。

HTTP動詞 パス ヘルパー
GET /posts/:post_id/comments(.:format) post_comments
GET /posts/:post_id/comments(.:format) post_comments
POST /posts/:post_id/comments/new(.:format) new_post_comment
GET /sekret/comments/:id/edit(.:format) edit_sekret_comment
GET /comments/:id(.:format) sekret_comment
PATCH/PUT /comments/:id(.:format) sekret_comment
DELETE /comments/:id(.:format) sekret_comment

2.8 関係性のルーティング

関連性のルーティングは、他のresourcesとルート内で再利用可能な共通のルートを宣言できるようにしてくれます。 関連性を定義するにはまず次のようにして、

concern :commentable do
  resources :comments
end

concern :image_attachable do
  resources :images, only: :index
end

これら上記のconcernsをresources内で次のように使用することで、コードの重複を避け、 ルート全体で挙動の共有を可能にしてくれます。

resources :messages, concerns: :commentable

resources :posts, concerns: [:commentable, :image_attachable]

これは下記のように評価されます。

resources :messages do
  resources :comments
end

resources :posts do
  resources :comments
  resources :images, only: :index
end

また、あなたが望むルート内部、例えばscope、namespace呼び出し内に、にこれらを使用することも可能です。

namespace :posts do
  concerns :commentable
end

2.9 オブジェクトからパスとURLを生成

更にルーティングヘルパーを使用する事で、Railsはパラメーターの配列からパスとURLを作成することも可能です。 例えば、あなたが下記のルートを持っていると仮定すると、

resources :magazines do
  resources :ads
end

magazine_ad_pathを使用する場合、MagazineAdのインスタンスを、 IDの数値の代わりに渡すことが可能になります。

<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %>

また、オブジェクトのセットを使用してurl_forを使用することも可能で、 Railsは自動的に必要とされているルートを判断してくれます。

<%= link_to 'Ad details', url_for([@magazine, @ad]) %>

このケースでは、Railsは@magazineMagazine@adAdであることを確認し、 magazine_ad_pathヘルパーを選択します。 link_toのようなヘルパー内では、url_for呼び出しで指定されるオブジェクトだけを指定することも可能です。

<%= link_to 'Ad details', [@magazine, @ad] %>

もし、magazineだけをリンクしたい場合は、次のようにします。

<%= link_to 'Magazine details', @magazine %>

その他のアクションを指定するには、配列の第一要素にアクション名を指定する必要があります。

<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>

これは、URLとしてモデルのインスタンスを扱う事を可能にし、 resourcefulなスタイルを使用する上での重要な利点になります。

2.10 更にRESTfulアクションの追加

デフォルトで生成されるRESTfulルーティングの7つのルートは、制限されているわけではありません。 お望みであれば、コレクションまたはコレクションの特定のmemberに適用するルートを追加することも出来ます。

2.10.1 メンバー(member)ルートの追加

1要素のルートの追加をするには、resourceブロックへmemberブロックを追加するだけです。

resources :photos do
  member do
    get 'preview'
  end
end

これは、GETで/photos/1/previewを認識し、 PhotosControllerpreviewアクションへ、 params[:id]内にresourceのid値を渡すルーティングを行います。 また、preview_photo_urlpreview_photo_pathヘルパーを生成します。

memberルートのブロック内で、各ルート名にはそれが認識できるHTTP動詞を指定します。 getpatchputpostdeleteを使用することが出来ます。 もし複数のmemberルートを持たない場合は、ルートに:onを使用することでブロックを排除することが出来ます。

resources :photos do
  get 'preview', on: :member
end

:onオプションを取り除くことが可能で、これはresourceのid値を除いて、同じmemberルートを作成し、 params[:id]の代わりに、params[:photo_id]を利用可能にします。(翻訳に自信なし)

2.10.2 コレクション(collection)ルートの追加

コレクションのルートを追加するには、次のようにします。

resources :photos do
  collection do
    get 'search'
  end
end

これは/photos/searchのようなGETパスの認識をRailsで有効にし、 PhotosControllersearchアクションをルート(経路)に加えます。 また、search_photos_urlsearch_photos_pathのルート(経路)ヘルパーを作成します。

memberルートと同様に、:onをルートに渡すことが出来ます。

resources :photos do
  get 'search', on: :collection
end
2.10.3 新しい追加アクションのためのルート(経路)の追加

:onショートカットを使用して、新しいアクションを追加するには下記のようにします。

resources :comments do
  get 'preview', on: :new
end

これは/comments/new/previewのようなGETパスの認識をRailsで有効にし、 CommentsControllerpreviewアクションのルート(経路)を追加します。 また、preview_new_comment_urlpreview_new_comment_pathのルート(経路)ヘルパーを追加します。

もし、Resourcefulなルートに対して、多くのアクションを追加していることに気づいたのであれば、 一度立ち止まって、これは本来別のresourceを作るべきだったのではないかと自身に問いかけてみてください。

3. 非Resourcefulルート

resourceルーティングに加え、Railsはアクションへの任意のURLルーティングも強力にサポートしてくれます。 ここでは、resourcefulなルーティングによって、自動的に生成されるルート(経路)のグループを取得するということはしません。 代わりに、アプリケーションを分割することで各ルートを設定します。

普段はresourcefulなルーティングを使用すべきですが、 一方でシンプルなルーティングが適切であるケースも存在します。 resourcefulなフレームワークに上手く適合しないものを、無理に押し込めようとする必要はありません。

特に単純なルーティングであれば、古いURLから新しいRailsアクションへ非常に簡単にマッピングしてくれます。

3.1 パラメータのバインド(紐付け)

通常のルートを設定する場合、やって来るHTTPリクエストのパーツをRailsがマッピングする一連のシンボルを提供します。 これらのシンボルの2つは特別なもので、:controllerはコントローラー名のマッピングを、 :actionはそのコントローラー内のアクション名をマッピングします。 例えば、下記のルートで考えてみた場合、

get ':controller(/:action(/:id))'

/photos/show/1がこのルートにリクエストされると(その前にどのルートにもマッチしなかったと仮定します)、 PhotosControllershowアクションが実行され、 最後のパラメータである"1"はparams[:id]として利用可能になるという結果を得ることになります。 このルートはまた、/photosへのリクエストは :action:idが括弧で指定されていることから任意のパラメータとされ、 PhotosController#indexにマッピングします。

3.2 動的セグメント(区分)

お望みであれば、通常のルート内に多くの動的にセグメントとして設定することが可能です。 :controllerまたは:actionだけが、 paramsの一部として利用可能になるわけではありません。 もし、次のようにルートを設定すると、

get ':controller/:action/:id/:user_id'

/photos/show/1/2のパスは、PhotosControllershowアクションに送られ、 params[:id]は"1"に、params[:user_id]は"2"になります。

:controllerパスセグメントと一緒に、:namespaceまたは:moduleを使用することは出来ません。 もし、これを行う必要がある場合は、必要とするnamespaceを:controllerでマッチさせるように制約を設けます。 下記はその例になります。

get ':controller(/:action(/:id))', controller: /admin/[^/]+/

デフォルトで、動的セグメントはフォーマット(format)ルートの判定に使用されることから、ドットを受け付けません。 動的セグメントでドットが必要な場合は、制約を追加することでこれを上書きします。 例えば、id: /[^\/]+/とすれば、スラッシュを除く全てを許可します。

3.3 静的セグメント

ルートを作成する際にセグメントの一部の先頭にコロンを追加すること無く、 静的セグメントを指定することが出来ます。(下記の例では、with_userがそれに該当)

get ':controller/:action/:id/with_user/:user_id'

このルートは/photos/show/1/with_user/2のようなパスに応答します。 このケースでは、params{controller:'photos', action:'show', id:'1', user_id:'2'}になります。

3.4 クエリー文字列

paramsはクエリー文字列から、何らかのパラメータを含むこともあります。 例えば、このルートは

get ':controller/:action/:id'

/photos/show/1?user_id=2のパスが来ると、 Photosコントローラーのshowアクションに送られます。 params{controller:'photos', action:'show', id:'1', user_id:'2'}になります。

3.5 デフォルトの定義

必ずしも、明示的に:controller:actionシンボルをルート内で使用する必要はありません。 これらをデフォルトとして提供することも可能です。

get 'photos/:id', to: 'photos#show'

このルート指定では、Railsは/photos/12のパスを、 PhotosControllershowアクションにマッチさせます。

また、:defaultsオプションにハッシュを提供することで、その他のデフォルト定義を行うことも可能です。 これは動的セグメントとして指定しなくても、パラメータが提供されます。 例えば下記のケースでは、

get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }

Railsはphotos/12PhotosControllershowアクションにマッチさせ、 params[:format]"jpg"を設定します。

3.6 ルートへの名前付け

:asオプションを使用して、ルートに対して名前を指定することが可能です。

get 'exit', to: 'sessions#destroy', as: :logout

これはlogout_pathlogout_urlを、アプリケーション内の名前付けされたヘルパーとして生成します。 logout_pathが呼び出されると、/exitを返します。

これを使用することで、resourcesによって定義されたルーティングのメソッドを、 下記のように上書きすることが出来ます。

get ':username', to: 'users#show', as: :user

これはコントローラー、ヘルパー、ビュー内で使用可能な、 /bob(bobはユーザー名)のようなルートへ遷移するuser_pathメソッドを定義します。 UsersControllershowアクション内では、 params[:username]はユーザーのためのusernameを含むことになります。 もし、パラメーター名を:usernameにしたくないのであれば、 ルートの定義で:usernameを変更してください。

3.7 HTTP動詞の制約

通常、getpostputdeleteメソッドを使用して、 ルートに特定の動詞の制約を設けるべきです。 :viaオプション付きでmatchメソッドを使用して、一度に複数の動詞にマッチさせることが可能です。

match 'photos', to: 'photos#show', via: [:get, :post]

特定のルートにvia: :allを使用して、全ての動詞にマッチさせることも可能です。

match 'photos', to: 'photos#show', via: :all

GETPOSTリクエストの両方を1つのアクションにルーティングする事は、 セキュリティに対して影響を与えることになります。 特に理由がない限り、動詞へのアクションへ全ての動詞をルーティングしてしまう、この書き方は避けるべきです。

3.8 セグメントの制約

:constraintsオプションを使用して、動的セグメントのフォーマットを強制する事が出来ます。

get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]d{5}/ }

このルートは/photos/A12345のようなパスにマッチし、/photos/893のようなパスにはマッチしません。 下記のようにより簡潔に、同じルートを表す事が出来ます。

get 'photos/:id', to: 'photos#show', id: /[A-Z]d{5}/

:constraintsは、正規表現アンカー(^$など)が使用出来ないという制限のある正規表現を受け付けます。 例えば、下記のルートは動作しません。

get '/:id', to: 'posts#show', constraints: {id: /^d/}

ただし、全てのルートは先頭にアンカーされているので、正規表現アンカーを使用する必要が無いことに注意してください。

例えば、下記のルートは1-hello-worldのような数値から始まるpostsと、 davidのような数値から始まらないto_param値のユーザーを、 名前空間のルートで共有出来るようにしています。

get '/:id', to: 'posts#show', constraints: { id: /d.+/ }
get '/:username', to: 'users#show'

3.9 リクエストを基にした制約

Stringを返すRequestオブジェクト上のメソッドを基に、ルートに制限を掛ける事も可能です。

セグメント制約と同じ方法で、リクエストを基にして制約を指定します。

get 'photos', constraints: {subdomain: 'admin'}

また、ブロック形式の制約を指定することも可能です。

namespace :admin do
  constraints subdomain: 'admin' do
    resources :photos
  end
end

3.10 高度な制約

もし、より高度な制約を指定する必要があるのであれば、 Railsが使用するべきとするmatches?に応答するオブジェクトを提供することが出来ます。 全てのユーザーに対し、BlacklistControllerへのブロックリストを通したルートにしたいとします。 それは、次のようにすることが出来ます。

class BlacklistConstraint
  def initialize
    @ips = Blacklist.retrieve_ips
  end

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

TwitterClone::Application.routes.draw do
  get '*path', to: 'blacklist#index',
    constraints: BlacklistConstraint.new
end

lambdaとして制約を指定することも可能です。

TwitterClone::Application.routes.draw do
  get '*path', to: 'blacklist#index',
    constraints: lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) }
end

matches?メソッドとlambdaの両方とも、引数としてrequestオブジェクトを取得します。

3.11 ルートのglobとワイルドカードのセグメント

ルートのglobは、特定のパラメータが残りのルートの全ての部分としてマッチすべき事を指定する方法です。 例えば、

get 'photos/*other', to: 'photos#unknown'

このルートはphotos/12または/photos/long/path/to/12にマッチし、 前者ではparams[:other]に"12"を、後者では"long/path/to/12"を設定します。 アスタリスク(*)で始まる部分は、"ワイルドカードセグメント"と呼ばれています。

ワイルドカードセグメントは、ルートのどこにでも設定可能です。 例えば、

get 'books/*section/:title', to: 'books#show'

これはbooks/some/section/last-words-a-memoirにマッチし、 params[:section]'some/section'に、 params[:title]は、'last-words-a-memoir'になります。

技術的には、ルートは1つ以上のワイルドカードセグメントを持つことが可能です。 マッチャーはパラメータへ直感的にセグメントを割り当てます。 例えば、

get '*a/foo/*b', to: 'test#index'

は、zoo/woo/foo/bar/bazへマッチし、 params[:a]'zoo/woo'に、 params[:b]'bar/baz'になります。

'/foo/bar.json'へのリクエストでは、JSONフォーマットのリクエストとなり、 params[:pages]'foo/bar'になります。 もし、古い3.0.xの挙動に戻したいのであれば、下記のようにformat: falseを提供します。

get '*pages', to: 'pages#show', format: false

もし、フォーマットセグメントを必須にしたいのであれば、 下記のようにformat: trueを提供します。

get '*pages', to: 'pages#show', format: true

3.12 リダイレクト

ルート内でredirectヘルパーを使用して、任意のパスを他のパスへリダイレクトさせる事が可能です。

get '/stories', to: redirect('/posts')

また、リダイレクトするパスにマッチする動的セグメントを再利用する事も可能です。

get '/stories/:name', to: redirect('/posts/%{name}')

また、paramsとリクエストオブジェクトを受け取るブロックでリダイレクトを指定することも可能です。

get '/stories/:name', to: redirect {|params, req| "/posts/#{params[:name].pluralize}" }
get '/stories', to: redirect {|p, req| "/posts/#{req.subdomain}" }

このリダイレクトは、301 "Moved Permanently"リダイレクトであることに注意してください。 ブラウザまたはプロキシサーバはこの種のリダイレクトをキャッシュし、古いページにはアクセスしないようにする事を覚えておいてください。

これらのケースの全てで、もし主要ホスト(例:http://www.example.com)を提供しない場合、 Railsは現在のリクエストから詳細情報を取得します。

3.13 Rackアプリケーションへのルーティング

PostsController内のindexアクションに該当する'posts#index'のような文字列の代わりに、 マッチャーのエンドポイントとしてのRackアプリケーションを指定することが可能です。

match '/application.js', to: Sprockets, via: :all

Sprocketsが、呼び出しと[status, headers, body]を返すのに応答をする限り、 ルーターはRackアプリケーションとアクションの違いを認識しまんせん。 これは、あなたがRackアプリケーションで全ての動詞を適切に処理したいと考えている場合に、 via: :allを使用するのに適しています。

'posts#index'は実際には、有効なRackアプリケーションを返す、 PostsController.action(:index)を拡張しています。(翻訳に自信なし)

3.14 rootの使用

rootメソッドを使用して、Railsが'/'ルートをどうするかを指定することが出来ます。

root to: 'pages#main'
root 'pages#main' # 上記の省略文法

一番多く訪問されるルートであり、また最初にマッチされるべきである事から、 ファイルの先頭にrootルートを配置すべきです。

rootルートは、アクションへのGETリクエストへのルートのみになります。

rootは同様に名前空間(namespace)とスコープを使用することが可能です。 例えば、

namespace :admin do
  root to: "admin#index"
end
 
root to: "home#index"

3.15 ユニコード文字のルート

ユニコード文字のルートを直接指定することが可能です。 例えば、

get 'こんにちは', to: 'welcome#index'

4. Resourcefulルートのカスタマイズ

resources :postsによって生成されるデフォルトのルートとヘルパーは、 何らかの方法でカスタマイズする事が可能です。 Railsは、resourcefulなヘルパーの一般的な部分を実質カスタマイズ出来るようにしてくれます。

4.1 コントローラー使用の指定

:controllerオプションは、resourceに使用するコントローラーを明示的に指定することを可能にしてくれます。 例えば、

resources :photos, controller: 'images'

上記は/photosで始まるパスを認識しますが、Imagesコントローラーへルートを通します。

HTTP動詞 パス アクション ヘルパー
GET /photos index photos_path
GET /photos/new new new_photo_path
POST /photos create photos_path
GET /photos/:id show photo_path(:id)
GET /photos/:id/edit edit edit_photo_path(:id)
PATCH/PUT /photos/:id update photo_path(:id)
DELETE /photos/:id destroy photo_path(:id)

photos_pathnew_photo_path等を使用して、このリソースのパスが生成されます。

名前空間のコントローラーの場合は、ディレクトリの表記を使用することが出来ます。 例えば、

resources :user_permissions, controller: 'admin/user_permissions'

これは、Admin::UserPermissionsコントローラーへのルートになります。

ディレクトリ表記だけがサポートされます。 ruby定数表記でのコントローラーの指定(例: :controller => 'Admin::UserPermissions')は、 ルーティングの問題を引き起こし、警告される結果となります。

4.2 制約の指定

:constraintsオプションを使用して、明示的にidで必須とするフォーマットを指定することが可能です。 例えば、

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

この宣言は、:idパラメーターが提供された正規表現にマッチする制約を設けます。 そのため、このケースではルーターは/photos/1をこのルートにマッチさせません。 /photos/RR27であればマッチします。

ブロック形式を使用することで、複数のルートに同一の制約を指定することが可能です。

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

もちろん、このコンテキスト内でresourceでは無いもので、より進んだ制約を指定することが可能です。

デフォルトでは、:idパラメーターはドットを受け付けません。 これは、ドットがフォーマットのルートの区切りとして使用されているためです。 もし、:id内にドットを使用する必要があれば、制約を追加することでこれを上書きします。 例えば、id: /[^\/]+/とすれば、スラッシュ以外の全てを受け入れます。

4.3 名前付けされたヘルパーの上書き

:asオプションは、ルートの名前付けがされたヘルパーの標準の名前の上書きを可能にしてくれます。 例えば、

resources :photos, as: 'images'

上記は、/photosから始まるパスを認識し、PhotosControllerへのリクエストとしてルートを通しますが、 :asオプションの値が使用された名前がヘルパーに付けられます。

HTTP動詞 パス アクション ヘルパー
GET /photos index images_path
GET /photos/new new new_image_path
POST /photos create images_path
GET /photos/:id show image_path(:id)
GET /photos/:id/edit edit edit_image_path(:id)
PATCH/PUT /photos/:id update image_path(:id)
DELETE /photos/:id destroy image_path(:id)

4.4 newとeditの上書き

:path_namesオプションは、パス内に自動的に生成される"new"と"edit"セグメントの上書きを可能にしてくれます。

resources :photos, path_names: { new: 'make', edit: 'change' }

これは次のようなパスを認識するようになります。

/photos/make
/photos/1/change

実際のアクション名は、このオプションによって変更される事はありません。 2つのパスは、neweditアクションへルートしたままになります。

もし、このオプションを全てのルートに対して変更したいと自身で気がついた場合、 スコープを使用してそれを行うことが可能です。

scope path_names: { new: 'make' } do
  # ルートを指定
end

4.5 名付けルート(経路)ヘルパーの接頭辞

:asオプションを使用することで、Railsがルート(経路)のために生成する名付けルートヘルパーに接頭辞を付ける事が可能です。 このオプションを使用することで、パススコープ(scope)を使用したルート名の衝突を避ける事が出来ます。 例えば、

scope 'admin' do
  resources :photos, as: 'admin_photos'
end

resources :photos

これはadmin_photos_pathnew_admin_photo_pathのような、ルートヘルパーを提供します。

ルートヘルパーのグループに接頭辞を付けるには、スコープ(scope)に:asを使用します。

scope 'admin', as: 'admin' do
  resources :photos, :accounts
end

resources :photos, :accounts

これは、admin_photos_pathadmin_accounts_pathのようなルートを生成し、 それぞれ/admin/photos/admin/accountsにマッピングされます。

namespaceスコープは、:module:pathの接頭辞付けと同様に、 自動的に:asを追加します。

また、名付けパラメーターを使用してルートに接頭辞を付けることも可能です。

scope ':username' do
  resources :posts
end

これは、/bob/posts/1のようなURLを提供し、 コントローラーとヘルパーとビューでparams[:username]として、パスのusernameの部分を参照できるようにしてくれます。

4.6 生成されたルートの制約

デフォルトでは、Railsは7つのRESTfulなルート(index、show、new、create、edit、update、destroy)を生成します。 :only:exceptオプションを使用して、この振る舞いを調整することが可能です。 :onlyオプションは、Railsに指定したルートだけを作成することを伝えます。

resources :photos, only: [:index, :show]

これにより、/photosへのGETリクエストは成功しますが、 /photosへのPOSTリクエスト(通常はcreateアクションへのルート)は失敗するようになります。

:exceptオプションには、Railsが生成すべきでは無いルートまたはルートのリストを指定します。

resources :photos, except: :destroy

このケースでは、Railsはdestroy(/photos/:idへのDELETEリクエスト)を除く全ての通常のルートを生成します。

もし、あなたのアプリケーションが多くのRESTfulなルートを持っているのであれば、 :only:exceptを使用して、実際に必要とされているるルートのみを生成することで、 ルーティングプロセスのメモリー使用量を抑え、スピードアップさせることが出来るかもしれません。

4.7 翻訳パス

scopeを使用して、resourcesによって生成されたパス名を変更することが可能です。

scope(path_names: { new: 'neu', edit: 'bearbeiten' }) do
  resources :categories, path: 'kategorien'
end

こうすることで、Railsは次のようなCategoriesControllerへのルートを生成します。

HTTP動詞 パス アクション ヘルパー
GET /kategorien index categories_path
GET /kategorien/neu new new_category_path
POST /kategorien create categories_path
GET /kategorien/:id show category_path(:id)
GET /kategorien/:id/bearbeiten edit edit_category_path(:id)
PATCH/PUT /kategorien/:id update category_path(:id)
DELETE /kategorien/:id destroy category_path(:id)

4.8 単数フォームの上書き

もし、resourceの単数フォームを定義したいのであれば、 Inflectorへルールを追加する必要があります。

ActiveSupport::Inflector.inflections do |inflect|
  inflect.irregular 'tooth', 'teeth'
end

4.9 入れ子Resources内での:asの使用

:asオプションは、入れ子のルートヘルパー内でResourceのために自動生成された名前を上書きします。 例えば、

resources :magazines do
  resources :ads, as: 'periodical_ads'
end

これは、magazine_periodical_ads_urledit_magazine_periodical_ad_pathのようなルーティングヘルパーを生成します。

5. ルートの検証とテスト

Railsは、ルートを検証・テストするための便利な機能を提供します。

5.1 既存ルートのリスト化

アプリケーション内で利用可能な全ルートを取得するには、 development環境の実行下でhttp://localhost:3000/rails/info/routesへアクセスします。 また、rake routesコマンドをターミナル上で実行することで、同じ出力結果を得ることが可能です。

どちらも同じ方法で、routes.rb内の全てのルートの内容をリスト化します。 各ルートで次の事が確認できるはずです。

  • ルート名(指定されていれば)
  • 使用されるHTTP動詞(全ての動詞に応答しない経路であれば)
  • マッチするURLパターン
  • ルートのためのルーティングパラメーター

下記はRESTfulルートに対してrake routesで出力した一例になります。

 users    GET    /users(.:format)          users#index
          POST   /users(.:format)          users#create
 new_user GET    /users/new(.:format)      users#new
edit_user GET    /users/:id/edit(.:format) users#edit

CONTROLLER環境変数に特定のコントローラーをマッピングすることで、 ルートのリスト一覧表示の制限をすることが可能です。

$ CONTROLLER=users rake routes

rake routesによる出力が見にくいと感じたなら、 行が折り返さない所までターミナルウインドウの幅を広げてみてください。

5.2 ルート(経路)のテスト

ルートはあなたのテスト計画に含まれているべきです。(アプリケーションの他の部分と同じように) Railsはシンプルにルート(経路)のテストが出来るように設計された3つの組み込みアサーションを提供します。

  • assert_generates
  • assert_recognizes
  • assert_routing
5.2.1 assert_generatesアサーション

assert_generatesは、特定のオプションのセットが特定のパスを生成し、 デフォルトルートまたはカスタムルートで使用可能なのかをアサーションします。 例えば、

assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' }
assert_generates '/about', controller: 'pages', action: 'about'
5.2.2 assert_recognizesアサーション

assert_recognizesassert_generatesの正反対のものです。 与えられたパスを認識し、アプリケーション内の特定の場所にルートを通しているかをアサーションします。 例えば、

assert_recognizes({ controller: 'photos', action: 'show', id: '1' }, '/photos/1')

:method引数を提供して、HTTP動詞を指定することが可能です。

assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', method: :post })
5.2.3 assert_routingアサーション

assert_routingアサーションは、パスによるオプションの生成、オプションによるパスの生成の両方からルートの検証を行います。 そのため、これはassert_generatesassert_recognizes:の機能を結合したものになります。

assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' })

 Back to top

© 2010 - 2017 STUDIO KINGDOM