このガイドでは、開発者に向けてRailsのルーティング機能を解説します(訳注: routeとrootを区別するため、訳文ではrouteを基本的に「ルーティング」と訳します)。
このガイドの内容:
config/routes.rbのコードの読み方matchメソッドによるルーティングも可能です)Railsのルーターは受け取ったURLを認識し、適切なコントローラ内アクションやRackアプリケーションに振り分けます。ルーターはパスやURLも生成できるので、ビューでこれらのパスやURLを直接ハードコードする必要はありません。
Railsアプリケーションが以下のHTTPリクエストを受け取ったとします。
GET /patients/17
このリクエストは、特定のコントローラ内アクションにマッチさせるようルーターに要求しています。最初にマッチしたのが以下のルーティングだとします。
get '/patients/:id', to: 'patients#show'
このリクエストはpatientsコントローラのshowアクションに割り当てられ、paramsには{ id: '17' }ハッシュが含まれています。
Railsではコントローラ名にスネークケースを使います。たとえばMonsterTrucksControllerのような複合語のコントローラを使う場合は、monster_trucks#showのように指定します。
パスやURLを生成することもできます。たとえば、上のルーティングが以下のように変更されたとします。
get '/patients/:id', to: 'patients#show', as: 'patient'
そして、アプリケーションのコントローラに以下のコードがあるとします。
@patient = Patient.find(params[:id])
上記に対応するビューは以下です。
<%= link_to 'Patient Record', patient_path(@patient) %>
すると、ルーターによって/patients/17というパスが生成されます。これを利用することでビューが改修しやすくなり、コードも読みやすくなります。このidはルーティングヘルパーで指定する必要がない点にご注目ください。
アプリケーションやエンジンのルーティングはconfig/routes.rbファイルの中に存在し、通常以下のような感じになっています。
Rails.application.routes.draw do resources :brands, only: [:index, :show] do resources :products, only: [:index, :show] end resource :basket, only: [:show, :update, :destroy] resolve("Basket") { route_for(:basket) } end
これは通常のRubyソースファイルなので、Rubyのあらゆる機能を用いてルーティングを定義できます。ただしルーターのDSLメソッド名と変数名と衝突する可能性があるので、変数名には注意が必要です。
ルーティング定義をラップするRails.application.routes.draw do ... endブロックは、ルーターDSLのスコープを確定するのに不可欠なので、削除してはいけません。
リソースベースのルーティング (以下リソースルーティング) を使うことで、リソースベースで構成されたコントローラに対応する共通のルーティングを手軽に宣言できます。resourcesを宣言するだけで、コントローラのindex、show、new、edit、create、update、destroyアクションを個別に宣言しなくても1行で宣言が完了します。
ブラウザはRailsに対してリクエストを送信する際に、特定のHTTPメソッド(GET、POST、PATCH、PUT、DELETEなど)を使って、URLに対するリクエストを作成します。上に述べたHTTPメソッドは、いずれもリソースに対して特定の操作の実行を指示するリクエストです。リソースルーティングでは、関連するさまざまなリクエストを1つのコントローラ内のアクションに割り当てます。
Railsアプリケーションが以下のHTTPリクエストを受け取ったとします。
DELETE /photos/17
このリクエストは、特定のコントローラ内アクションにマッピングさせるようルーターに要求しています。最初にマッチしたのが以下のルーティングだとします。
resources :photos
Railsはこのリクエストをphotosコントローラ内のdestroyアクションに割り当て、paramsハッシュに{ id: '17' }を含めます。
Railsのリソースフルルーティングでは、(GET、PUTなどの)各種HTTP verb(動詞、HTTPメソッドとも呼ばれます) と、コントローラ内アクションを指すURLが対応付けられます。1つのアクションは、データベース上での特定のCRUD(Create/Read/Update/Delete)操作に対応付けられるルールになっています。たとえば、以下のようなルーティングが1つあるとします。
resources :photos
上の記述により、アプリケーション内に以下の7つのルーティングが作成され、いずれもPhotosコントローラに対応付けられます。
| HTTP verb | パス | コントローラ#アクション | 目的 |
|---|---|---|---|
| GET | /photos | photos#index | すべての写真の一覧を表示 |
| GET | /photos/new | photos#new | 写真を1つ作成するためのHTMLフォームを返す |
| POST | /photos | photos#create | 写真を1つ作成する |
| GET | /photos/:id | photos#show | 特定の写真を表示する |
| GET | /photos/:id/edit | photos#edit | 写真編集用のHTMLフォームを1つ返す |
| PATCH/PUT | /photos/:id | photos#update | 特定の写真を更新する |
| DELETE | /photos/:id | photos#destroy | 特定の写真を削除する |
Railsのルーターでは、サーバーへのリクエストをマッチさせる際にHTTP verbとURLを使っているため、4種類のURL(/photos、/photos/new、/photos/:id、/photos/:id/edit)が7種類の異なるアクション(index、show、new、create、edit、update、destroy)に割り当てられています。
Railsのルーティングは、ルーティングファイルの「上からの記載順に」マッチします。このため、たとえばresources :photosというルーティングがget 'photos/poll'よりも上の行にあれば、resources行のshowアクションがget行の記述よりも優先されるので、get行のルーティングは有効になりません。これを修正するには、get行をresources行 よりも上 の行に移動してください。これにより、get行がマッチするようになります。
リソースフルなルーティングを作成すると、アプリケーションのコントローラで多くのヘルパーが利用できるようになります。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を返します)これらの_pathヘルパーには、それぞれに対応する_urlヘルパー(photos_urlなど)もあります。_urlヘルパーは、同じパスの前に「現在のホスト名」「ポート番号」「パスのプレフィックス」を追加して返します。
自分のルーティングで利用できるルーティングヘルパーを見つけるには、後述の既存のルールを一覧表示するを参照してください。
リソースをいくつも定義しなければならない場合は、以下のような略記法で一度に定義することでタイプ量を節約できます。
resources :photos, :books, :videos
上の記法は以下と完全に同一です。
resources :photos resources :books resources :videos
ユーザーがページを表示する際にidを一切参照しないリソースが使われることがあります。たとえば、/profileでは常に「現在ログインしているユーザー自身」のプロファイルを表示し、他のユーザーidを参照する必要がないとします。このような場合には、単数形リソース(singular resource)を使って以下のようにshowアクションに(/profile/:idではなく)/profileを割り当てられます。
get 'profile', to: 'users#show'
to:の引数にStringを渡す場合はコントローラ#アクション形式であることが前提ですが、Symbolを使う場合は、to:オプションをaction:に置き換えるべきです。#なしのStringを使う場合は、to:オプションをcontroller:に置き換えるべきです。
get 'profile', action: :show, controller: 'users'
以下のリソースフルなルーティングは
resource :geocoder resolve('Geocoder') { [:geocoder] }
Geocodersコントローラに割り当てられた以下の6つのルーティングを作成します。
| HTTP verb | パス | コントローラ#アクション | 目的 |
|---|---|---|---|
| GET | /geocoder/new | geocoders#new | geocoder作成用のHTMLフォームを返す |
| POST | /geocoder | geocoders#create | geocoderを作成する |
| GET | /geocoder | geocoders#show | 1つしかないgeocoderリソースを表示する |
| GET | /geocoder/edit | geocoders#edit | geocoder編集用のHTMLフォームを返す |
| PATCH/PUT | /geocoder | geocoders#update | 1つしかないgeocoderリソースを更新する |
| DELETE | /geocoder | geocoders#destroy | geocoderリソースを削除する |
単数形リソースは複数形のコントローラに対応付けられます。これは、同じコントローラで単数形のルーティング(/account)と複数形のルーティング(/accounts/45)を両方使いたい場合を想定しているためです。従って、resource :photoとresources :photosのどちらも、単数形ルーティングと複数形ルーティングを両方作成し、同一のコントローラ(PhotosController)に割り当てられます。
単数形のリソースフルなルーティングを使うと、以下のヘルパーメソッドが生成されます。
new_geocoder_pathは/geocoder/newを返しますedit_geocoder_pathは/geocoder/editを返しますgeocoder_pathは/geocoderを返します。Geocoderのインスタンスをレコード識別でルーティングするにはresolveの呼び出しが必要です。
複数形リソースの場合と同様に、同じヘルパー名の末尾を_urlにすると「現在のホスト名」「ポート番号」「パスのプレフィックス」も含まれます。
コントローラを名前空間でグループ化することもできます。最もよく使われる名前空間といえば、多数の管理用コントローラ群をまとめるAdmin::名前空間でしょう。これらのコントローラをapp/controllers/adminディレクトリに配置し、ルーティングでこれらをグループ化できます。namespaceブロックを使うと、このようなグループへルーティングできます。
namespace :admin do resources :articles, :comments end
上のルーティングにより、articlesコントローラやcommentsコントローラへのルーティングが多数生成されます。たとえば、Admin::ArticlesController向けに作成されるルーティングは以下のとおりです。
| HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
|---|---|---|---|
| GET | /admin/articles | admin/articles#index | admin_articles_path |
| GET | /admin/articles/new | admin/articles#new | new_admin_article_path |
| POST | /admin/articles | admin/articles#create | admin_articles_path |
| GET | /admin/articles/:id | admin/articles#show | admin_article_path(:id) |
| GET | /admin/articles/:id/edit | admin/articles#edit | edit_admin_article_path(:id) |
| PATCH/PUT | /admin/articles/:id | admin/articles#update | admin_article_path(:id) |
| DELETE | /admin/articles/:id | admin/articles#destroy | admin_article_path(:id) |
例外的に、(/adminが前についていない) /articlesをAdmin::ArticlesControllerにルーティングしたい場合は、以下のようにすることもできます。
scope module: 'admin' do resources :articles, :comments end
以下のようにブロックを使わない記述も可能です。
resources :articles, module: 'admin'
逆に、/admin/articlesを (Admin::なしの) ArticlesControllerにルーティングしたい場合は、以下のようにscopeブロックでパスを指定できます。
scope '/admin' do resources :articles, :comments end
以下のように単数形ルーティングでもできます。
resources :articles, path: '/admin/articles'
いずれの場合も、名前付きルーティング(named route)は、scopeを使わなかった場合と同じになることにご注目ください。最後の例の場合は、以下のパスがArticlesControllerに割り当てられます。
| HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
|---|---|---|---|
| GET | /admin/articles | articles#index | articles_path |
| GET | /admin/articles/new | articles#new | new_article_path |
| POST | /admin/articles | articles#create | articles_path |
| GET | /admin/articles/:id | articles#show | article_path(:id) |
| GET | /admin/articles/:id/edit | articles#edit | edit_article_path(:id) |
| PATCH/PUT | /admin/articles/:id | articles#update | article_path(:id) |
| DELETE | /admin/articles/:id | articles#destroy | article_path(:id) |
namespaceブロックの内部で異なるコントローラ名前空間を使いたい場合、「get '/foo', to: '/foo#index'」のような絶対コントローラパスを指定することもできます。
他のリソースの配下に論理的な子リソースを配置することはよくあります。たとえば、Railsアプリケーションに以下のモデルがあるとします。
class Magazine < ApplicationRecord has_many :ads end class Ad < ApplicationRecord belongs_to :magazine end
ルーティングをネストする(入れ子にする)ことで、この親子関係をルーティングで表せるようになります。上の例の場合、以下のようにルーティングを宣言できます。
resources :magazines do resources :ads end
上のルーティングによって、雑誌(magazines)へのルーティングに加えて、広告(ads)をAdsControllerにもルーティングできるようになりました。adへのURLにはmagazineも必要です。
| HTTP verb | パス | コントローラ#アクション | 目的 |
|---|---|---|---|
| GET | /magazines/:magazine_id/ads | ads#index | ある雑誌1冊に含まれる広告をすべて表示する |
| GET | /magazines/:magazine_id/ads/new | ads#new | ある1冊の雑誌用の広告を1つ作成するHTMLフォームを返す |
| POST | /magazines/:magazine_id/ads | ads#create | ある1冊の雑誌用の広告を1つ作成する |
| GET | /magazines/:magazine_id/ads/:id | ads#show | ある雑誌1冊に含まれる広告を1つ表示する |
| GET | /magazines/:magazine_id/ads/:id/edit | ads#edit | ある雑誌1冊に含まれる広告1つを編集するHTMLフォームを返す |
| PATCH/PUT | /magazines/:magazine_id/ads/:id | ads#update | ある雑誌1冊に含まれる広告を1つ更新する |
| DELETE | /magazines/:magazine_id/ads/:id | ads#destroy | ある雑誌1冊に含まれる広告を1つ削除する |
ルーティングを作成すると、ルーティングヘルパーも作成されます。ヘルパーはmagazine_ads_urlやedit_magazine_ad_pathのような名前になります。これらのヘルパーは、最初のパラメータとしてMagazineモデルのインスタンスを1つ取ります(magazine_ads_url(@magazine))。
次のように、ネストしたリソースの中で別のリソースをネストできます。
resources :publishers do resources :magazines do resources :photos end end
ただしリソースのネストが深くなるとたちまち扱いにくくなります。たとえば、上のルーティングはアプリケーションで以下のようなパスとして認識されます。
/publishers/1/magazines/2/photos/3
このURLに対応するルーティングヘルパーはpublisher_magazine_photo_urlとなります。このヘルパーを使うには、毎回3つの階層すべてでオブジェクトを指定する必要があります。ネスティングが深くなるとルーティングが扱いにくくなる問題については、Jamis Buckの有名な記事を参照してください。JamisはRailsアプリケーション設計上の優れた経験則を提案しています。
リソースのネスティングは、ぜひとも1回にとどめて下さい。決して2回以上ネストするべきではありません。
前述したような深いネストを避ける1つの方法として、コレクション(index/new/createのような、idを持たないもの)のアクションだけを親のスコープの下で生成するという手法があります。このとき、メンバー(show/edit/update/destroyのような、idを必要とするもの)のアクションをネストに含めないようにするのがポイントです。これによりコレクションだけが階層化のメリットを受けられます。つまり、以下のように最小限の情報でリソースを一意に指定できるルーティングを作成するということです。
resources :articles do resources :comments, only: [:index, :new, :create] end resources :comments, only: [:show, :edit, :update, :destroy]
この方法は、ルーティングの記述を複雑にせず、かつ深いネストを作らないという絶妙なバランスを保っています。:shallowオプションを使うことで、上と同じ内容をさらに簡単に記述できます。
resources :articles do resources :comments, shallow: true end
これによって生成されるルーティングは、最初の例と完全に同じです。親リソースで:shallowオプションを指定すると、すべてのネストしたリソースが浅くなります。
resources :articles, shallow: true do resources :comments resources :quotes resources :drafts end
このarticlesリソースでは以下のルーティングが生成されます。
| HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
|---|---|---|---|
| GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
| POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
| GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
| GET | /comments/:id/edit(.:format) | comments#edit | edit_comment_path |
| GET | /comments/:id(.:format) | comments#show | comment_path |
| PATCH/PUT | /comments/:id(.:format) | comments#update | comment_path |
| DELETE | /comments/:id(.:format) | comments#destroy | comment_path |
| GET | /articles/:article_id/quotes(.:format) | quotes#index | article_quotes_path |
| POST | /articles/:article_id/quotes(.:format) | quotes#create | article_quotes_path |
| GET | /articles/:article_id/quotes/new(.:format) | quotes#new | new_article_quote_path |
| GET | /quotes/:id/edit(.:format) | quotes#edit | edit_quote_path |
| GET | /quotes/:id(.:format) | quotes#show | quote_path |
| PATCH/PUT | /quotes/:id(.:format) | quotes#update | quote_path |
| DELETE | /quotes/:id(.:format) | quotes#destroy | quote_path |
| GET | /articles/:article_id/drafts(.:format) | drafts#index | article_drafts_path |
| POST | /articles/:article_id/drafts(.:format) | drafts#create | article_drafts_path |
| GET | /articles/:article_id/drafts/new(.:format) | drafts#new | new_article_draft_path |
| GET | /drafts/:id/edit(.:format) | drafts#edit | edit_draft_path |
| GET | /drafts/:id(.:format) | drafts#show | draft_path |
| PATCH/PUT | /drafts/:id(.:format) | drafts#update | draft_path |
| DELETE | /drafts/:id(.:format) | drafts#destroy | draft_path |
| GET | /articles(.:format) | articles#index | articles_path |
| POST | /articles(.:format) | articles#create | articles_path |
| GET | /articles/new(.:format) | articles#new | new_article_path |
| GET | /articles/:id/edit(.:format) | articles#edit | edit_article_path |
| GET | /articles/:id(.:format) | articles#show | article_path |
| PATCH/PUT | /articles/:id(.:format) | articles#update | article_path |
| DELETE | /articles/:id(.:format) | articles#destroy | article_path |
DSL(ドメイン固有言語)であるshallowメソッドをルーティングで使うと、すべてのネストが浅くなるように内側にスコープを1つ作成します。これによって生成されるルーティングは、最初の例と完全に同じです。
shallow do resources :articles do resources :comments resources :quotes resources :drafts end end
scopeメソッドには、「浅い」ルーティングをカスタマイズするためのオプションが2つあります。:shallow_pathオプションは、指定されたパラメータをメンバーのパスの冒頭に追加します。
scope shallow_path: "sekret" do resources :articles do resources :comments, shallow: true end end
上の場合、commentsリソースのルーティングは以下のようになります。
| HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
|---|---|---|---|
| GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
| POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
| GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
| GET | /sekret/comments/:id/edit(.:format) | comments#edit | edit_comment_path |
| GET | /sekret/comments/:id(.:format) | comments#show | comment_path |
| PATCH/PUT | /sekret/comments/:id(.:format) | comments#update | comment_path |
| DELETE | /sekret/comments/:id(.:format) | comments#destroy | comment_path |
:shallow_prefixオプションを使うと、指定されたパラメータを(パスではなく)名前付きルーティングヘルパー名の冒頭に追加します。
scope shallow_prefix: "sekret" do resources :articles do resources :comments, shallow: true end end
上の場合、commentsリソースのルーティングは以下のようになります。
| HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
|---|---|---|---|
| GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
| POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
| GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
| GET | /comments/:id/edit(.:format) | comments#edit | edit_sekret_comment_path |
| GET | /comments/:id(.:format) | comments#show | sekret_comment_path |
| PATCH/PUT | /comments/:id(.:format) | comments#update | sekret_comment_path |
| DELETE | /comments/:id(.:format) | comments#destroy | sekret_comment_path |
concern(関心)を使うことで、他のリソースやルーティング内で使いまわせる共通のルーティングを宣言できます。concernは以下のようにconcernブロックで定義します。
concern :commentable do resources :comments end concern :image_attachable do resources :images, only: :index end
concernを利用すると、同じようなルーティングを繰り返し記述せずに済み、複数のルーティング間で同じ振る舞いを共有できます。
resources :messages, concerns: :commentable resources :articles, concerns: [:commentable, :image_attachable]
上のコードは以下と同等です。
resources :messages do resources :comments end resources :articles do resources :comments resources :images, only: :index end
複数形のconcerns呼び出しはルーティング内のどの場所にでも配置できます。scopeやnamespaceブロックでは以下のように利用できます。
namespace :articles do concerns :commentable end
ルーティングヘルパーを使う方法の他に、パラメータの配列からパスやURLを作成することもできます。例として、以下のようなルーティングがあるとします。
resources :magazines do resources :ads end
magazine_ad_pathを使うと、idを数字で渡す代わりにMagazineとAdのインスタンスを引数として渡せます。
<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %>
複数のオブジェクトが集まったセットに対してurl_forを使うこともできます。複数のオブジェクトを渡しても、適切なルーティングが自動的に決定されます。
<%= link_to 'Ad details', url_for([@magazine, @ad]) %>
上の場合、Railsは@magazineがMagazineであり、@adがAdであることを認識し、それに基づいてmagazine_ad_pathヘルパーを呼び出します。link_toなどのヘルパーでも、完全なurl_for呼び出しの代わりに単にオブジェクトを渡せます。
<%= link_to 'Ad details', [@magazine, @ad] %>
1冊の雑誌にだけリンクしたい場合は、以下のように書きます。
<%= link_to 'Magazine details', @magazine %>
それ以外のアクションについては、配列の最初の要素にアクション名を挿入するだけで済みます。
<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>
これにより、モデルのインスタンスをURLとして扱えるようになります。これはリソースフルなスタイルを採用する大きなメリットの1つです。
デフォルトで作成されるRESTfulなルーティングは7つですが、7つと決まっているわけではありません。必要であれば、コレクションやコレクションの各メンバーに対して適用されるリソースを追加することも可能です。
メンバー(member)ルーティングを追加したい場合は、memberブロックをリソースブロックに1つ追加します。
resources :photos do member do get 'preview' end end
上のルーティングはGETリクエストとそれに伴う/photos/1/previewを認識し、リクエストをPhotosコントローラのpreviewアクションにルーティングし、リソースid値をparams[:id]に渡します。同時に、preview_photo_urlヘルパーとpreview_photo_pathヘルパーも作成されます。
memberルーティングブロックの内側では、認識させるHTTP verbをルーティング名ごとに指定します。指定可能なHTTP verbはget、patch、put、post、deleteです。memberルーティングが1つしかない場合は、以下のようにルーティングで:onオプションを指定することでブロックを省略できます。
resources :photos do get 'preview', on: :member end
:onオプションを省略しても同様のmemberルーティングが生成されます。この場合リソースidの値の取得にparams[:id]ではなくparams[:photo_id]を使う点が異なります。ルーティングヘルパーも、preview_photo_urlがphoto_preview_urlに、preview_photo_pathがphoto_preview_pathにそれぞれリネームされます。
ルーティングにコレクション(collection)を追加するには以下のようにcollectionブロックを使います。
resources :photos do collection do get 'search' end end
上のルーティングは、GETリクエスト + /photos/searchなどの(idを伴わない)パスを認識し、リクエストをPhotosコントローラのsearchアクションにルーティングします。このときsearch_photos_urlやsearch_photos_pathルーティングヘルパーも同時に作成されます。
collectionルーティングでもmemberルーティングのときと同様に:onオプションを使えます。
resources :photos do get 'search', on: :collection end
追加のresourceルーティングをシンボルで第1引数として定義する場合は、文字列で定義した場合と振る舞いが同等ではなくなる点にご注意ください。文字列はパスとして推測されますが、シンボルはコントローラのアクションとして推測されます。
:onオプションを使って、たとえば以下のように別のnewアクションを追加できます。
resources :comments do get 'preview', on: :new end
上のようにすることで、GET + /comments/new/previewのようなパスが認識され、Commentsコントローラのpreviewアクションにルーティングされます。preview_new_comment_urlやpreview_new_comment_pathルーティングヘルパーも同時に作成されます。
リソースフルなルーティングにアクションが多数追加されていることに気付いたら、それ以上アクションを追加するのをやめて、そこに別のリソースが隠されているのではないかと疑ってみる方がよいでしょう。
Railsではリソースルーティングを行なう他に、任意のURLをアクションにルーティングすることもできます。この方式を使う場合、リソースフルルーティングのような自動的なルーティンググループの生成は行われません。従って、アプリケーションで必要なルーティングを個別に設定することになります。
基本的にはリソースフルルーティングを使うべきではありますが、このような単純なルーティングの方が適している箇所も多数あるはずです。リソースフルルーティングでは大袈裟すぎる場合に、アプリケーションを無理にリソースフルなフレームワークに押し込める必要はありません。
シンプルなルーティングは、特に従来形式のURLを新しいRailsのアクションに割り当てる場合にはるかに簡単に行えるようになります。
通常のルーティングを設定する場合は、RailsがルーティングをブラウザからのHTTPリクエストに割り当てるためのシンボルをいくつか渡します。以下のルーティングを例にとってみましょう。
get 'photos(/:id)', to: 'photos#display'
ブラウザからの/photos/1リクエストが上のルーティングで処理される(ファイル内でそれより上の行のルーティング設定にはマッチしなかったとします)と、PhotosControllerのdisplayアクションが呼び出され、URL末尾のパラメータ"1"へのアクセスはparams[:id]で行なえます。:idが必須パラメータではないことが丸かっこ()で示されているので、このルーティングは/photosをPhotosController#displayにルーティングすることもできます。
通常のルーティングの一部として、文字列を固定しない動的なセグメントを自由に使えます。あらゆるセグメントはparamsの一部に含めてアクションに渡せます。以下のルーティングを設定したとします。
get 'photos/:id/:user_id', to: 'photos#show'
ブラウザからの/photos/1/2パスはPhotosControllerのshowアクションに割り当てられます。params[:id]には"1"、params[:user_id]には"2"がそれぞれ保存されます。
デフォルトでは動的なセグメント分割にドット.を渡せません。ドットはフォーマット済みルーティングでは区切り文字として使われるためです。どうしても動的セグメント内でドットを使いたい場合は、デフォルト設定を上書きする制限を与えます。たとえばid: /[^\/]+/とすると、スラッシュ以外のすべての文字が使えます。
ルーティング作成時にコロンを付けなかった部分は、静的なセグメントとして固定文字列が指定されます。
get 'photos/:id/with_user/:user_id', to: 'photos#show'
上のルーティングは、/photos/1/with_user/2のようなパスにマッチします。このときアクションで使えるparamsは { controller: 'photos', action: 'show', id: '1', user_id: '2' }となります。
クエリ文字列(訳注: URLの末尾に?パラメータ名=値の形式で置かれるパラメータ)で指定されているパラメータもすべてparamsに含まれます。以下のルーティングを例にとってみましょう。
get 'photos/:id', to: 'photos#show'
ブラウザからのリクエストで/photos/1?user_id=2というパスが渡されると、Photosコントローラのshowアクションに割り当てられます。このときのparamsは{ controller: 'photos', action: 'show', id: '1', user_id: '2' }となります。
:defaultsオプションにハッシュを1つ渡すことで、ルーティング内にデフォルト値を定義できます。このとき、動的なセグメントとして指定する必要のないパラメータを次のように適用することも可能です。
get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }
上のルーティングはブラウザからの/photos/12パスにマッチし、Photosコントローラのshowアクションに割り当てられます。params[:format]は"jpg"に設定されます。
ブロック形式のdefaultsを使うと、複数の項目についてデフォルト値を設定することもできます。
defaults format: :json do resources :photos end
セキュリティ上の理由により、クエリパラメータでデフォルト値をオーバーライドすることはできません。オーバーライド可能なデフォルト値は、URLパスの置き換えによる動的なセグメントのみです。
:asオプションを使うと、任意のルーティングに名前を指定できます。
get 'exit', to: 'sessions#destroy', as: :logout
上のルーティングではlogout_pathとlogout_urlがアプリケーションの名前付きルーティングヘルパーとして作成されます。logout_pathを呼び出すと/exitが返されます。
この方法を使って、リソースを定義する前にカスタムルーティングを配置することで、リソースで定義されたルーティングメソッドをオーバーライドすることも可能です。
get ':username', to: 'users#show', as: :user resources :users
上のルーティングではuser_pathメソッドが生成され、コントローラ・ヘルパー・ビューでそれぞれ使えるようになります。このメソッドは、/bobのようなユーザー名を持つルーティングに移動します。Usersコントローラのshowアクションの内部でparams[:username]にアクセスすると、ユーザー名を取り出せます。パラメータ名を:usernameにしたくない場合は、ルーティング定義の:usernameの部分を変更してください。
あるルーティングを特定のHTTP verbに割り当てるために、通常はget、patch、put、post、deleteメソッドのいずれかを使う必要があります。matchメソッドと:viaオプションを使うことで、複数のHTTP verbに同時にマッチするルーティングを作成できます。
match 'photos', to: 'photos#show', via: [:get, :post]
via: :allを指定すると、すべてのHTTP verbにマッチする特別なルーティングを作成できます。
match 'photos', to: 'photos#show', via: :all
1つのアクションにGETリクエストとPOSTリクエストを両方ルーティングすると、セキュリティに影響する可能性があります。どうしても必要な理由がない限り、1つのアクションにすべてのHTTP verbをルーティングしないでください。
RailsではGETのCSRFトークンをチェックしません。決してGETリクエストでデータベースに書き込んではいけません。詳しくはセキュリティガイドのCSRF対策を参照してください。
:constraintsオプションを使って、動的セグメントのURLフォーマットを特定の形式に制限できます。
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: 'articles#show', constraints: { id: /^\d/ }
対象となるすべてのルーティングは冒頭と末尾がアンカーされているので、このようなアンカー表現を使う必要はないはずです。
たとえば以下のルーティングでは、ルート(root)名前空間を共有する際にarticlesのto_paramでは1-hello-worldのように数字で始まる値だけが使えるようになっており、usersのto_paramではdavidのように数字で始まらない値だけが使えるようになっています。
get '/:id', to: 'articles#show', constraints: { id: /\d.+/ } get '/:username', to: 'users#show'
また、Stringを返すRequestオブジェクトの任意のメソッドに基いてルーティングを制限することもできます。
リクエストに応じた制限は、セグメントを制限するときと同様の方法で指定できます。
get 'photos', to: 'photos#index', constraints: { subdomain: 'admin' }
constraintsブロックで制限を指定することもできます。
namespace :admin do constraints subdomain: 'admin' do resources :photos end end
リクエストベースの制限は、Requestオブジェクトに対してあるメソッドを呼び出すことで実行されます。ハッシュキーと同じ名前のメソッドを呼び出し、返された値をハッシュの値と比較します。従って、制限された値は、対応するRequestオブジェクトのメソッドが返す型と一致する必要があります。たとえば、constraints: { subdomain: 'api' }という制限はapiサブドメインに期待どおりマッチしますが、constraints: { subdomain: :api }のようにシンボルを使った場合はapiサブドメインに一致しません。request.subdomainが返す'api'は文字列型であるためです。
formatの制限には例外があります。これはRequestオブジェクトのメソッドですが、すべてのパスに含まれる暗黙的なオプションのパラメータでもあります。formatの制限よりセグメント制限が優先されます。たとえば、get 'foo'、constraints: { format: 'json' }はGET /fooと一致します。これはデフォルトでformatがオプションであるためです。しかし、次のようにlambdaを使うことが可能です。get 'foo', constraints: lambda { |req| req.format == :json } というルーティング指定は明示的なJSONリクエストにのみ一致します。
より高度な制限を使いたい場合、Railsで必要なmatches?に応答できるオブジェクトを渡す方法があります。例として、制限リストに記載されているすべてのユーザーをRestrictedListControllerにルーティングしたいとします。この場合、以下のように設定します。
class RestrictedListConstraint def initialize @ips = RestrictedList.retrieve_ips end def matches?(request) @ips.include?(request.remote_ip) end end Rails.application.routes.draw do get '*path', to: 'restricted_list#index', constraints: RestrictedListConstraint.new end
制限をlambdaとして指定することもできます。
Rails.application.routes.draw do get '*path', to: 'restricted_list#index', constraints: lambda { |request| RestrictedList.retrieve_ips.include?(request.remote_ip) } end
matches?メソッドおよびlambdaはいずれも引数としてrequestオブジェクトを受け取ります。
ルーティンググロブ(route globbing)とはワイルドカード(*)展開のことであり、以下のようにルーティングのある位置から下のすべての部分に特定のパラメータをマッチさせるときに使います。
get 'photos/*other', to: 'photos#unknown'
上のルーティングはphotos/12や/photos/long/path/to/12(long/path/toは長いパス)にマッチし、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つのルーティングに2つ以上のワイルドカードセグメントを含めることは可能です。マッチャがセグメントをパラメータに割り当てる方法は、以下のように直感的です。
get '*a/foo/*b', to: 'test#index'
上のルーティングはzoo/woo/foo/bar/bazにマッチし、params[:a]には'zoo/woo'が保存され、params[:b]には'bar/baz'が保存されます。
'/foo/bar.json'をリクエストするとparams[:pages]には'foo/bar'がJSONリクエストフォーマットで保存されます。Rails 3.0.xの動作に戻したい場合は、以下のようにformat: falseを指定できます。
get '*pages', to: 'pages#show', format: false
このセグメントフォーマットを必須にしたい場合は、以下のようにformat: trueを指定します。
get '*pages', to: 'pages#show', format: true
ルーティングでredirectを使うと、任意のパスを他のパスにリダイレクトできます。
get '/stories', to: redirect('/articles')
パスにマッチする動的セグメントを再利用してリダイレクトすることもできます。
get '/stories/:name', to: redirect('/articles/%{name}')
リダイレクトにブロックを渡すこともできます。このリダイレクトは、シンボル化されたパスパラメータとrequestオブジェクトを受け取ります。
get '/stories/:name', to: redirect { |path_params, req| "/articles/#{path_params[:name].pluralize}" } get '/stories', to: redirect { |path_params, req| "/articles/#{req.subdomain}" }
デフォルトのリダイレクトは、HTTPステータス「301 "Moved Permanently"」のリダイレクトになる点にご注意ください。一部のWebブラウザやプロキシサーバーはこの種のリダイレクトをキャッシュすることがあり、その場合リダイレクト前の古いページにはアクセスできなくなります。次のように:statusオプションを使うことでレスポンスのステータスを変更できます。
get '/stories/:name', to: redirect('/articles/%{name}', status: 302)
どの場合であっても、ホスト(http://www.example.comなど)がURLの冒頭で指定されていない場合は、Railsは(以前のリクエストではなく)現在のリクエストから詳細を取得します。
Postコントローラのindexアクションに対応する'articles#index'のような文字列の代わりに、任意のRackアプリケーションをマッチャーのエンドポイントとして指定できます。
match '/application.js', to: MyRackApp, via: :all
Railsルーターから見れば、MyRackAppがcallに応答して[status, headers, body]を返す限り、ルーティング先がRackアプリケーションであるかアクションであるかを区別しません。これによって、適切と考えられるすべてのHTTP verbをRackアプリケーションで扱えるようになるので、これはvia: :allの適切な利用法です。
参考までに、'articles#index'は実際にはArticlesController.action(:index)という形に展開されます。これは有効なRackアプリケーションを返します。
procやlambdaはcallに応答するオブジェクトなので、たとえばヘルスチェックで用いるルーティングを以下のように極めてシンプルにインライン実装できます。 get '/health', to: ->(env) { [204, {}, ['']] }
Rackアプリケーションをマッチャーのエンドポイントとして指定すると、それを受け取るRackアプリケーションのルーティングは変更されない点にご留意ください。以下のルーティングでは、Rackアプリケーションは/adminへのルーティングを期待するべきです。
match '/admin', to: AdminApp, via: :all
Rackアプリケーションがrootパスでリクエストを受け取るようにしたい場合は、mountを使います。
mount AdminApp, at: '/admin'
rootを使うrootメソッドを使うことで、'/'によるルーティング先を指定できます。
root to: 'pages#main' root 'pages#main' # 上の省略形
rootルーティングは、ルーティングファイルの冒頭に記述してください。rootは最もよく使われるルーティングであり、最初にマッチする必要があるからです。
rootルーティングがアクションに渡せるのはGETリクエストだけです。
以下のように、名前空間やスコープの内側でもrootを指定できます。
namespace :admin do root to: "admin#index" end root to: "home#index"
Unicode文字列を以下のようにルーティングで直接使うこともできます。
get 'こんにちは', to: 'welcome#index'
directを呼び出すことで、カスタムURLヘルパーを次のように作成できます。
direct :homepage do "http://www.rubyonrails.org" end # >> homepage_url # => "http://www.rubyonrails.org"
このブロックの戻り値は、必ずurl_forメソッドで有効な1個の引数にならなければなりません。これによって、有効な「文字列URL」「ハッシュ」「配列」「Active Modelインスタンス」「Active Modelクラス」のいずれか1つを渡せるようになります。
direct :commentable do |model| [ model, anchor: model.dom_id ] end direct :main do { controller: 'pages', action: 'index', subdomain: 'www' } end
resolveを使うresolveメソッドを使うと、モデルのポリモーフィックなマッピングを次のようにカスタマイズできます。
resource :basket resolve("Basket") { [:basket] }
<%= form_with model: @basket do |form| %> <!-- basket form --> <% end %>
上のコードは、通常の/baskets/:idではなく、単数形の/basketというURLを生成します。
ほとんどの場合、resourcesで生成されるデフォルトのルーティングやヘルパーで用は足りますが、もう少しルーティングをカスタマイズしたくなることもあります。Railsでは、リソースフルなヘルパーの一般的などの部分であっても事実上自由にカスタマイズ可能です。
:controllerオプションは、リソースで使うコントローラを以下のように明示的に指定します。
resources :photos, controller: 'images'
上のルーティングは、/photosで始まるパスを認識しますが、ルーティング先をImagesコントローラにします。
| HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
|---|---|---|---|
| GET | /photos | images#index | photos_path |
| GET | /photos/new | images#new | new_photo_path |
| POST | /photos | images#create | photos_path |
| GET | /photos/:id | images#show | photo_path(:id) |
| GET | /photos/:id/edit | images#edit | edit_photo_path(:id) |
| PATCH/PUT | /photos/:id | images#update | photo_path(:id) |
| DELETE | /photos/:id | images#destroy | photo_path(:id) |
このリソースへのパスを生成するにはphotos_pathやnew_photo_pathなどをお使いください。
名前空間内のコントローラは以下のように直接指定できます。
resources :user_permissions, controller: 'admin/user_permissions'
上はAdmin::UserPermissionsにルーティングされます。
ここでサポートされている記法は、/で区切る「ディレクトリ記法」のみです。Rubyの定数表記法(controller: 'Admin::UserPermissions'など)をコントローラに対して使うと、ルーティングで問題が生じ、警告が出力される可能性があります。
:constraintsオプションを使うと、暗黙で使われるidに対して以下のように必須のフォーマットを指定できます。
resources :photos, constraints: { id: /[A-Z][A-Z][0-9]+/ }
上の宣言は:idパラメータに制限を加え、指定した正規表現にのみマッチするようにします。従って、上の例では/photos/1のようなパスにはマッチしなくなり、代わりに/photos/RR27のようなパスにマッチするようになります。
以下のようにブロック形式を使うことで、1つの制限を多数のルーティングに対してまとめて指定することも可能です。
constraints(id: /[A-Z][A-Z][0-9]+/) do resources :photos resources :accounts end
もちろん、このコンテキストでは「リソースフルでない」ルーティングに適用可能な、より高度な制限を加えることもできます。
デフォルトでは:idパラメータにドット.を渡せません。ドットはフォーマット済みルーティングでは区切り文字として使われるためです。どうしても:id内でドットを使いたい場合は、デフォルト設定を上書きする制限を与えます。たとえばid: /[^\/]+/とすると、スラッシュ以外のすべての文字が使えます。
:asオプションを使うと、名前付きルーティングヘルパーを以下のように上書きしてルーティングヘルパー名を変えられます。
resources :photos, as: 'images'
上のルーティングでは、/photosで始まるブラウザからのパスを認識し、このリクエストをPhotosコントローラにルーティングしますが、ヘルパーの命名には:asオプションの値が使われます。
| HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
|---|---|---|---|
| GET | /photos | photos#index | images_path |
| GET | /photos/new | photos#new | new_image_path |
| POST | /photos | photos#create | images_path |
| GET | /photos/:id | photos#show | image_path(:id) |
| GET | /photos/:id/edit | photos#edit | edit_image_path(:id) |
| PATCH/PUT | /photos/:id | photos#update | image_path(:id) |
| DELETE | /photos/:id | photos#destroy | image_path(:id) |
newセグメントやeditセグメントをオーバーライドする:path_namesオプションを使うと、パスに含まれている、自動生成された"new"セグメントや"edit"セグメントをオーバーライドできます。
resources :photos, path_names: { new: 'make', edit: 'change' }
これにより、ルーティングで以下のようなパスが認識できるようになります。
/photos/make /photos/1/change
このオプションを指定しても、実際のアクション名が変更されるわけではありません。変更後のパスを使っても、ルーティング先は依然としてnewアクションとeditアクションのままです。
このオプションによる変更をすべてのルーティングに統一的に適用したい場合は、スコープを使えます。
scope path_names: { new: 'make' } do # 残りすべてのルーティング end
以下のように:asオプションを使うことで、Railsがルーティングに対して生成する名前付きルーティングヘルパー名の冒頭に文字を追加できます(プレフィックス)。パススコープを使うルーティング同士での名前の衝突を避けたい場合にお使いください。
scope 'admin' do resources :photos, as: 'admin_photos' end resources :photos
上のルーティングでは、admin_photos_pathやnew_admin_photo_pathなどのルーティングヘルパーが生成されます。
ルーティングヘルパーのグループにまとめてプレフィックスを追加するには、以下のようにscopeメソッドで:asオプションを使います。
scope 'admin', as: 'admin' do resources :photos, :accounts end resources :photos, :accounts
上によって、admin_photos_pathとadmin_accounts_pathなどのルーティングが生成されます。これらは/admin/photosと/admin/accountsにそれぞれ割り当てられます。
namespaceスコープを使うと、:moduleや:pathプレフィックスに加えて:asも自動的に追加されます。
名前付きパラメータを持つルーティングにプレフィックスを追加できます。
scope ':account_id', as: 'account', constraints: { account_id: /\d+/ } do resources :articles end
上のルーティングは/1/articles/9のようなパスを提供します。パスのaccount_id部分をparams[:account_id]の形でコントローラ、ヘルパー、ビューで参照できるようになります。
また、account_をプレフィックスとするパスヘルパーやURLヘルパーも生成され、これらにオブジェクトを渡すこともできます。
account_article_path(@account, @article) # => /1/article/9 url_for([@account, @article]) # => /1/article/9 form_with(model: [@account, @article]) # => <form action="/1/article/9" ...>
セグメントの制限を用いて、IDのような文字列にのみマッチするようにスコープを制限しています。この制限は、必要に応じて変更することも完全に省略することも可能です。asオプションも厳密には必須ではありませんが、これがないとurl_for([@account, @article])や form_withのようなurl_forに依存するヘルパーの評価時にエラーが発生します。
Railsは、アプリケーション内のすべてのRESTfulルーティングに対してデフォルトで7つのアクション(index、show、new、create、edit、update、destroy)へのルーティングを作成します。この振る舞いは、:onlyオプションや:exceptオプションで微調整できます。
:onlyオプションは、指定したルーティングだけを生成するよう指示します。
resources :photos, only: [:index, :show]
これで、/photosへのGETリクエストは成功し、/photos へのPOSTリクエスト(通常はcreateアクションにルーティングされる)は失敗します。
:exceptオプションは逆に、生成しないルーティング(またはルーティングのリスト)を指定します。
resources :photos, except: :destroy
この場合、destroy(/photos/:idへのDELETEリクエスト)を除いた通常のルーティングが生成されます。
アプリケーションでRESTfulルーティングが多数使われている場合は、それらに適宜:onlyや:exceptを使って、本当に必要なルーティングのみを生成することで、メモリ使用量の節約とルーティング処理の速度向上が見込めます。
scopeメソッドを使うことで、resourcesによって生成されるデフォルトのパス名を変更できます。
scope(path_names: { new: 'neu', edit: 'bearbeiten' }) do resources :categories, path: 'kategorien' end
上のようにすることで、以下のようなCategoriesコントローラへのルーティングが作成されます。
| HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
|---|---|---|---|
| GET | /kategorien | categories#index | categories_path |
| GET | /kategorien/neu | categories#new | new_category_path |
| POST | /kategorien | categories#create | categories_path |
| GET | /kategorien/:id | categories#show | category_path(:id) |
| GET | /kategorien/:id/bearbeiten | categories#edit | edit_category_path(:id) |
| PATCH/PUT | /kategorien/:id | categories#update | category_path(:id) |
| DELETE | /kategorien/:id | categories#destroy | category_path(:id) |
単数形リソースの名前をオーバーライドしたい場合、以下のようにinflectionsで活用形ルールを追加します。
ActiveSupport::Inflector.inflections do |inflect| inflect.irregular 'tooth', 'teeth' end
:asを使う:asを使うと、ネストしたルーティングヘルパー内のリソース用に自動生成された名前を以下のようにオーバーライドできます。
resources :magazines do resources :ads, as: 'periodical_ads' end
上のルーティングによって、magazine_periodical_ads_urlやedit_magazine_periodical_ad_pathなどのルーティングヘルパーが生成されます。
:paramオプションは、デフォルトのリソース識別子:id (ルーティングの生成に使われる動的なセグメントの名前) をオーバーライドします。params[<:パラメータ>]を使って、コントローラからそのセグメントにアクセスできます。
resources :videos, param: :identifier
videos GET /videos(.:format) videos#index
POST /videos(.:format) videos#create
new_video GET /videos/new(.:format) videos#new
edit_video GET /videos/:identifier/edit(.:format) videos#edit
Video.find_by(identifier: params[:identifier])
関連するモデルの ActiveRecord::Base#to_param をオーバーライドしてURLを作成できます。
class Video < ApplicationRecord def to_param identifier end end
video = Video.find_by(identifier: "Roman-Holiday") edit_video_path(video) # => "/videos/Roman-Holiday/edit"
ルーティングが数千にもおよび大規模アプリケーションでは、複雑なconfig/routes.rbファイル1個だけでは読みづらくなります。
Railsでは、このような巨大routes.rbファイルをdrawマクロで小さなルーティングファイルに分割する方法が提供されています。
たとえばadmin.rbにはadmin関連のルーティングをすべて含め、API関連リソースのルーティングはapi.rbファイルで記述するといったことが可能です。
# config/routes.rb Rails.application.routes.draw do get 'foo', to: 'foo#bar' draw(:admin) # `config/routes/admin.rb`にある別のルーティングファイルを読み込む end
# config/routes/admin.rb namespace :admin do resources :comments end
Rails.application.routes.draw自身の中でdraw(:admin)を呼び出すと、指定の引数と同じ名前のルーティングファイル(この例ではadmin.rb)の読み込みを試行します。
このファイルは、config/routesディレクトリの下か、任意のサブディレクトリ(config/routes/admin.rbやconfig/routes/external/admin.rbなど)に存在する必要があります。
admin.rbルーティングファイル内でも通常のルーティングDSLを利用できますが、メインのconfig/routes.rbファイルのようなRails.application.routes.drawブロックで囲んではいけません。
ルーティングファイルを複数に分けると、ルーティングを探すのも理解するのも難しくなります。ほとんどのアプリケーションでは、たとえルーティングが数百個になっていたとしても、ルーティングファイルを1つのままにしておくほうが開発者にとっては楽です。Railsのルーティングには、namespaceやscopeを用いてルーティングを分割整理する方法が既に用意されています。
Railsには、ルーティングを調べる機能(inspection)とテスト機能が備わっています。
現在のアプリケーションで利用可能なルーティングをすべて表示するには、サーバーがdevelopment環境で動作している状態でhttp://localhost:3000/rails/info/routesをブラウザで開きます。ターミナルでbin/rails routesコマンドを実行しても同じ結果を得られます。
どちらの方法を使った場合でも、config/routes.rbファイルに記載された順にルーティングが表示されます。1つのルーティングについて以下の情報が表示されます。
以下は、あるRESTfulルーティングに対してbin/rails 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
--expandedオプションを用いて、ルーティングテーブルのフォーマットを以下のような詳細モードに切り替えることもできます。
$ bin/rails routes --expanded --[ Route 1 ]---------------------------------------------------- Prefix | users Verb | GET URI | /users(.:format) Controller#Action | users#index --[ Route 2 ]---------------------------------------------------- Prefix | Verb | POST URI | /users(.:format) Controller#Action | users#create --[ Route 3 ]---------------------------------------------------- Prefix | new_user Verb | GET URI | /users/new(.:format) Controller#Action | users#new --[ Route 4 ]---------------------------------------------------- Prefix | edit_user Verb | GET URI | /users/:id/edit(.:format) Controller#Action | users#edit
-g(grepオプション)でルーティングを絞り込めます。URLヘルパー名、HTTP verb、URLパスのいずれかに部分マッチするルーティングが出力されます。
$ bin/rails routes -g new_comment $ bin/rails routes -g POST $ bin/rails routes -g admin
特定のコントローラに対応するルーティングだけを表示したい場合は、-cオプションを使います。
$ bin/rails routes -c users $ bin/rails routes -c admin/users $ bin/rails routes -c Comments $ bin/rails routes -c Articles::CommentsController
テキストの折り返しが発生しないぐらいに十分大きなターミナル画面では、おそらくbin/rails routesコマンド出力の方が読みやすいでしょう。
アプリケーションの他の部分と同様、ルーティング部分もテスト戦略に含めておくべきでしょう。Railsでは、テストを楽にするために3つの組み込みアサーションが用意されています。
assert_generatesアサーションassert_generatesは、特定のオプションの組み合わせを使った場合に特定のパスが生成されること、そしてそれらがデフォルトのルーティングでもカスタムルーティングでも使えることをテストするアサーション(assertion: 主張)です。
assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' } assert_generates '/about', controller: 'pages', action: 'about'
assert_recognizesアサーションassert_recognizesはassert_generatesと逆方向のテスティングを行います。与えられたパスが認識可能であること、アプリケーションの特定の場所にルーティングされることをテストするアサーションです。
assert_recognizes({ controller: 'photos', action: 'show', id: '1' }, '/photos/1')
:method引数でHTTP verbを指定することもできます。
assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', method: :post })
assert_routingアサーションassert_routingアサーションは、ルーティングを2つの観点(与えられたパスによってオプションが生成されること、そのオプションによって元のパスが生成されること)からチェックします。つまり、assert_generatesとassert_recognizesの機能を組み合わせたものになります。
assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' })
Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。
原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨
本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。
Railsガイド運営チーム (@RailsGuidesJP)
Railsガイドは下記の協賛企業から継続的な支援を受けています。支援・協賛にご興味あれば協賛プランからお問い合わせいただけると嬉しいです。