Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shallow routes with path_prefix?

Recently I changed a few nested resources in one of my applications to use shallow routing. It's working great and I've been able to simplify my views and controllers.

However, I've been using a path_prefix before:

map.with_options :path_prefix => "blog" do |blog|
  blog.resources :posts do |posts|
    posts.resources :comments
  end
end

Notice, that all routes are prefixed with "/blog" as expected.

# $ rake routes
#             posts GET    /blog/posts(.:format)                            {:controller=>"posts", :action=>"index"}
#                   POST   /blog/posts(.:format)                            {:controller=>"posts", :action=>"create"}
#          new_post GET    /blog/posts/new(.:format)                        {:controller=>"posts", :action=>"new"}
#         edit_post GET    /blog/posts/:id/edit(.:format)                   {:controller=>"posts", :action=>"edit"}
#              post GET    /blog/posts/:id(.:format)                        {:controller=>"posts", :action=>"show"}
#                   PUT    /blog/posts/:id(.:format)                        {:controller=>"posts", :action=>"update"}
#                   DELETE /blog/posts/:id(.:format)                        {:controller=>"posts", :action=>"destroy"}
#     post_comments GET    /blog/posts/:post_id/comments(.:format)          {:controller=>"comments", :action=>"index"}
#                   POST   /blog/posts/:post_id/comments(.:format)          {:controller=>"comments", :action=>"create"}
#  new_post_comment GET    /blog/posts/:post_id/comments/new(.:format)      {:controller=>"comments", :action=>"new"}
# edit_post_comment GET    /blog/posts/:post_id/comments/:id/edit(.:format) {:controller=>"comments", :action=>"edit"}
#      post_comment GET    /blog/posts/:post_id/comments/:id(.:format)      {:controller=>"comments", :action=>"show"}
#                   PUT    /blog/posts/:post_id/comments/:id(.:format)      {:controller=>"comments", :action=>"update"}
#                   DELETE /blog/posts/:post_id/comments/:id(.:format)      {:controller=>"comments", :action=>"destroy"}

The new routing configuration looks like this:

map.with_options :path_prefix => "blog", :shallow => true do |blog|
  blog.resources :posts do |posts|
    posts.resources :comments
  end
end

Now, the "/blog" prefix is missing in some of my routes.

# $ rake routes
#            posts GET    /blog/posts(.:format)                  {:controller=>"posts", :action=>"index"}
#                  POST   /blog/posts(.:format)                  {:controller=>"posts", :action=>"create"}
#         new_post GET    /blog/posts/new(.:format)              {:controller=>"posts", :action=>"new"}
#        edit_post GET    /posts/:id/edit(.:format)              {:controller=>"posts", :action=>"edit"}
#             post GET    /posts/:id(.:format)                   {:controller=>"posts", :action=>"show"}
#                  PUT    /posts/:id(.:format)                   {:controller=>"posts", :action=>"update"}
#                  DELETE /posts/:id(.:format)                   {:controller=>"posts", :action=>"destroy"}
#    post_comments GET    /posts/:post_id/comments(.:format)     {:controller=>"comments", :action=>"index"}
#                  POST   /posts/:post_id/comments(.:format)     {:controller=>"comments", :action=>"create"}
# new_post_comment GET    /posts/:post_id/comments/new(.:format) {:controller=>"comments", :action=>"new"}
#     edit_comment GET    /comments/:id/edit(.:format)           {:controller=>"comments", :action=>"edit"}
#          comment GET    /comments/:id(.:format)                {:controller=>"comments", :action=>"show"}
#                  PUT    /comments/:id(.:format)                {:controller=>"comments", :action=>"update"}
#                  DELETE /comments/:id(.:format)                {:controller=>"comments", :action=>"destroy"}

I'm looking for a solution to get the prefixes back for all routes. I know that it's working with namespaces (map.namespace :blog do), but I want to prevent refactoring all my controllers/views/tests to actually use namespaces.

All code samples are tested with Rails version 2.3.2 and Ruby 1.8.7.

like image 227
Christoph Schiessl Avatar asked Dec 23 '22 12:12

Christoph Schiessl


2 Answers

the documentation seems to indicate this exact behavior is by design:

:shallow - If true, paths for nested resources which reference a specific member (ie. those with an :id parameter) will not use the parent path prefix or name prefix.

(from http://api.rubyonrails.org/classes/ActionController/Resources.html#M000501)

Since using the :shallow option will cause your :path_prefix to be ignored in some cases, if you must always have this prefix you should consider removing the :shallow option. Here's an alternate solution that appears to do what you need:

map.with_options :path_prefix => "blog" do |blog|
  blog.resources :posts do |posts|
    posts.resources :comments, :only => [:index, :create, :new]
  end
  blog.resources :comments, :except => [:index, :create, :new]
end

Resulting in these routes:

#             posts GET    /blog/posts                               {:controller=>"posts", :action=>"index"}
#                   POST   /blog/posts                               {:controller=>"posts", :action=>"create"}
#          new_post GET    /blog/posts/new                           {:controller=>"posts", :action=>"new"}
#         edit_post GET    /blog/posts/:id/edit                      {:controller=>"posts", :action=>"edit"}
#              post GET    /blog/posts/:id                           {:controller=>"posts", :action=>"show"}
#                   PUT    /blog/posts/:id                           {:controller=>"posts", :action=>"update"}
#                   DELETE /blog/posts/:id                           {:controller=>"posts", :action=>"destroy"}
#     post_comments GET    /blog/posts/:post_id/comments             {:controller=>"comments", :action=>"index"}
#                   POST   /blog/posts/:post_id/comments             {:controller=>"comments", :action=>"create"}
#  new_post_comment GET    /blog/posts/:post_id/comments/new         {:controller=>"comments", :action=>"new"}
#      edit_comment GET    /blog/comments/:id/edit                   {:controller=>"comments", :action=>"edit"}
#           comment GET    /blog/comments/:id                        {:controller=>"comments", :action=>"show"}
#                   PUT    /blog/comments/:id                        {:controller=>"comments", :action=>"update"}
#                   DELETE /blog/comments/:id                        {:controller=>"comments", :action=>"destroy"}

Hope this helps!

like image 108
Adam Alexander Avatar answered Jan 02 '23 03:01

Adam Alexander


Probably the easiest solution would not be to use :shallow option but create the same routes with additional resource definition:

map.with_options :path_prefix => "blog" do |blog|
  blog.resources :posts do |posts|
    posts.resources :comments, :only => [:index, :create, :new]
  end
end
map.resources :comments, :path_prefix => "blog",
              :except => [:index, :create, :new]

which gives the following routes definitions:

# $ rake routes
#            posts GET    /blog/posts(.:format)                       {:action=>"index", :controller=>"posts"}
#                  POST   /blog/posts(.:format)                       {:action=>"create", :controller=>"posts"}
#         new_post GET    /blog/posts/new(.:format)                   {:action=>"new", :controller=>"posts"}
#        edit_post GET    /blog/posts/:id/edit(.:format)              {:action=>"edit", :controller=>"posts"}
#             post GET    /blog/posts/:id(.:format)                   {:action=>"show", :controller=>"posts"}
#                  PUT    /blog/posts/:id(.:format)                   {:action=>"update", :controller=>"posts"}
#                  DELETE /blog/posts/:id(.:format)                   {:action=>"destroy", :controller=>"posts"}
#    post_comments GET    /blog/posts/:post_id/comments(.:format)     {:action=>"index", :controller=>"comments"}
#                  POST   /blog/posts/:post_id/comments(.:format)     {:action=>"create", :controller=>"comments"}
# new_post_comment GET    /blog/posts/:post_id/comments/new(.:format) {:action=>"new", :controller=>"comments"}
#     edit_comment GET    /blog/comments/:id/edit(.:format)           {:action=>"edit", :controller=>"comments"}
#          comment GET    /blog/comments/:id(.:format)                {:action=>"show", :controller=>"comments"}
#                  PUT    /blog/comments/:id(.:format)                {:action=>"update", :controller=>"comments"}
#                  DELETE /blog/comments/:id(.:format)                {:action=>"destroy", :controller=>"comments"}
like image 34
Raimonds Simanovskis Avatar answered Jan 02 '23 01:01

Raimonds Simanovskis