【Rails】 ルーティングのnamespace, module, scopeの違い
この話題は結論から書いてもわかりにくくなると思ったので、順を追って整理する。
たとえば、こんなルーティングがあったとする。
resources :books, only: :show do
resources :reviews, only: :index
end
これは、以下のURLとコントローラー#アクションになる。(only: :index
の書き方の流派などはプロジェクトごとに変わると思うので気にせず)
- URL:
/books/:book_id/reviews
- Controller#Action:
reviews#index
だが、これでは「すべてのreviewsを表示する」という仕様があったとき、reviews#index
が使われてるので被ってしまいそうだ。
ここでnamespace
を使うとどうなるか?
resources :books, only: :show do
namespace :books do
resources :reviews, only: :index
end
end
コントローラーとアクションはbooks/reviews#index
となり、ファイル構成もbooks/reviews_controller.rb
と名前空間を分けられる。
しかし、肝心のURLが/books/:book_id/books/reviews
とおかしくなってしまう。
namespace
を使った場合:
- URL:
/books/:book_id/books/reviews
(※意味不明なURL) - Controller#Action:
books/reviews#index
このように、namespace
はURLでも、Controller#Action
(つまりファイル構成)でもスコープを分ける。
「URLは変えずに、Controller#Actionは分けたい(ファイル構成だけでスコープを分けたい)」というときに使えるのがmodule
オプションだ。
resources :books, only: :show do
- resources :reviews, only: :index
+ resources :reviews, only: :index, module: :books
end
こうすれば、
- URL:
/books/:book_id/reviews
- Controller#Action:
books/reviews#index
となり、よい感じになる。
ちなみに、module
オプションはscope module
ブロックを使っても書ける。
resources :books, only: :show do
scope module: 'books' do
resources :reviews, only: :index
#他のリソースもDRYに書いていける
end
end
こうすることで、reviews
以外のリソースがあったときもいちいちmodule: :books
を書かなくてよくなる。
Rubyでmodule
を使って名前空間を作るように、routes.rb
内でmodule
オプションを使って名前空間を作れるというイメージで覚えるのが個人的にはしっくりくる。
ただ、scope
とscope module:
は似たような記述だが違う挙動になるので注意が必要だ。
resources :books, only: :show do
scope 'books' do # scope moduleではなくscopeだけにしたら...
resources :reviews, only: :index
end
end
- URL:
/books/books/:book_id/reviews
(URLとしておかしい) - Controller#Action:
reviews#index
(他のと被っちゃいそう)
scope
はmodule
オプションの反対で、Controller#Actionに影響はないが、URLが変わる。
namespce, module, scopeの使い方まとめ
それぞれの用途をまとめると、
-
namespace
: URLとファイル構成でスコープを分けたい -
module(scope module:)
:ファイル構成だけでスコープを分けたい -
scope
: URLだけでスコープを分けたい
となる。
Rails のルーティング - Railsガイドにはもっとわかりやすい説明があるので、全体に目を通しておくことをおすすめします。
Discussion