無駄に深いネスト

元記事はこちら

翻訳

Bad Smell

map.resources :posts do |post|
  post.resources :comments do |comment|
    comment.resources :favorites
  end
end

<%= link_to post_comment_favorite_path(@post, @comment, @favorite) %>

3階層にネストしたルーティングは本当に必要でしょうか?ルーティングをネストするということは、子階層のリソースが親階層のリソースに依存しているということを意味します。 上記の例では、commentsというリソースがpostに依存しているということです。 しかし、favoritesはcommentに依存するものですから、favoritesのリソースがpostに依存している必要は全くありません。

Refactor

map.resources :post do |post|
  post.resources :comments
end

map.resources :comments do |comment|
  comment.resources :favorites
end

<%= link_to comment_favorite_path(@comment, @favorite) %>

以上の理由から、3階層のネストを2階層に減らしたルーティングにリファクタリングしました。favoritesへのurlもこうすることで短くなります。

Updated by @flyerhzm

無駄に深いネストを回避する新しい方法です。

Refactor 2

map.resources :posts, :shallow => true do |post|
  post.resources :comments do |comment|
    comment.resources :favorites
  end
end

<%= link_to comment_favorite_path(@comment, @favorite) %>

見ての通り、shallow => trueオプションをpostのルーティングに追加することで、1階層か2階層以内に抑えてルーティングを生成します。 以下のように生成されます。

posts GET    /posts(.:format)                                  {:action=>"index", :controller=>"posts"}
            POST   /posts(.:format)                                  {:action=>"create", :controller=>"posts"}
new_post GET    /posts/new(.:format)                              {:action=>"new", :controller=>"posts"}
edit_post GET    /posts/:id/edit(.:format)                         {:action=>"edit", :controller=>"posts"}
post GET    /posts/:id(.:format)                              {:action=>"show", :controller=>"posts"}
         PUT    /posts/:id(.:format)                              {:action=>"update", :controller=>"posts"}
         DELETE /posts/:id(.:format)                              {:action=>"destroy", :controller=>"posts"}
post_comments GET    /posts/:post_id/comments(.:format)                {:action=>"index", :controller=>"comments"}
                           POST   /posts/:post_id/comments(.:format)                {:action=>"create", :controller=>"comments"}
new_post_comment GET    /posts/:post_id/comments/new(.:format)            {:action=>"new", :controller=>"comments"}
edit_comment GET    /comments/:id/edit(.:format)                      {:action=>"edit", :controller=>"comments"}
comment GET    /comments/:id(.:format)                           {:action=>"show", :controller=>"comments"}
                 PUT    /comments/:id(.:format)                           {:action=>"update", :controller=>"comments"}
                 DELETE /comments/:id(.:format)                           {:action=>"destroy", :controller=>"comments"}
comment_favorites GET    /comments/:comment_id/favorites(.:format)         {:action=>"index", :controller=>"favorites"}
                                 POST   /comments/:comment_id/favorites(.:format)         {:action=>"create", :controller=>"favorites"}
new_comment_favorite GET    /comments/:comment_id/favorites/new(.:format)     {:action=>"new", :controller=>"favorites"}
edit_favorite GET    /favorites/:id/edit(.:format)                     {:action=>"edit", :controller=>"favorites"}
favorite GET    /favorites/:id(.:format)                          {:action=>"show", :controller=>"favorites"}
              PUT    /favorites/:id(.:format)                          {:action=>"update", :controller=>"favorites"}
              DELETE /favorites/:id(.:format)                          {:action=>"destroy", :controller=>"favorites"}

:shallowは良い解決策だと思います。このオプションにより、以下のような使い方ができます。

post_comment_path(1, 1)
comment_path(1)
comment_favorite_path(1, 1)
favorite_path(1)

素晴らしい!でもrailsドキュメントに載っていないのが残念です。

感想

railsドキュメントに載っていない 掲載されるようになったみたい。 ActionDispatch::Routing::Mapper::Resources

これも読んで、できる限りRESTfulなルーティングに近づけるようにしていると、1アクションの責務がとても明確になって良い。 postd.cc