Nested namespaces in RESTful Rails
Using RESTful Rails definitely has it advantages. The clean code was the primary reason why I switched all my projects toward REST. But I found that the nesting of resources broke down when using modules. I’d like to keep my code base nice and logically separated. For example the admin controller should reside in the app/controllers/admin/ directory. This will make your code base clearer and thus easier to maintain. And beside that it will generate pretty URL’s in which it is instantly clear where one is.
In the current project I integrated Beast and thought it was a good idea to keep this in a separate module as well. But the application I was working on required that forums only had relevance in the context of a project(there are many many different projects). So the obvious thing to do is to create a namespace nested in the projects resource.
map.resources :projects do |projects|
projects.resources :members
projects.namespace(:wiki) do |wiki_namespace|
wiki_namespace.resources :pages, :member => {:auto_save => :put}
end
end
And of course I wouldn’t be writing this post if this would work. Somehow the routes generated completely omitted the ‘wiki’ part:
new_project_page GET /projects/:project_id/pages/new {:action=>"new", :controller=>"pages"}
formatted_new_project_page GET /projects/:project_id/pages/new.:format {:action=>"new", :controller=>"pages"}
edit_project_page GET /projects/:project_id/pages/:id/edit {:action=>"edit", :controller=>"pages"}
formatted_edit_project_page GET /projects/:project_id/pages/:id/edit.:format {:action=>"edit", :controller=>"pages"}
auto_save_project_page PUT /projects/:project_id/pages/:id/auto_save {:action=>"auto_save", :controller=>"pages"}
formatted_auto_save_project_page PUT /projects/:project_id/pages/:id/auto_save.:format {:action=>"auto_save", :controller=>"pages"}
project_page GET /projects/:project_id/pages/:id {:action=>"show", :controller=>"pages"}
Weird. Not to mention inconvenient. Eventually I got it to work:
map.resources :projects do |projects|
projects.resources :members
projects.resources :pages, :controller => "Wiki::Pages", :path_prefix => "/projects/:project_id/wiki", :name_prefix => "project_wiki_", :member => {:auto_save => :put}
end
The :path_prefix parameter appears to overwrite the path_prefix defined by the block defining the scope. I would have preferred to write :path_prefix => "wiki". As one can see this solution is very unbecoming. And my RSpec tests started failing… With RoutingErrors. Rails worked just fine but RSpec did not play nice with this hack(because that is what this is, a hack). The RSpec mailinglist nor the Rails mailinglist could help.
This led me to abandon testing. Yes, I know, bad idea. Which I soon found out. This time I dove in the source code and found out that the namespace method takes an argument called namespace. Fiddling around with this parameter I got to the following solution:
map.resources :projects do |projects|
projects.resources :members
projects.namespace(:wiki, :namespace => '') do |wiki_namespace|
wiki_namespace.resources :pages, :member => {:auto_save => :put}
end
end
This generated the correct routes and satisfied RSpec! Don’t ask me for the rational behind this obviously strange behavior.