I've previously written about my approach to namespacing models in Rails. However, as I mentioned in that article, often times namespacing your models will run you up against the edges of the magic in Rails. That's definitely true for routing. As part of my work on a premium scaffolding tool for Rails, I've had to test every combination of namespacing possible and, among other things, ensure the routes generated work as expected. Well, now that I've done that, I wanted to put all the examples down in writing.

* This article assumes you're using shallow nesting for your nested resources. If you're not already using shallow routes or aren't familiar with the feature, check out the related documentation. I think it's the best way to keep the routing helpers for your nested resources sane! (I use shallow routing 100% of the time now.1)

The Easy Ones

Site and Page

resources :sites do
  resources :page
end

Hosting::Site and Hosting::Page

namespace :hosting do
  resources :sites do
    resources :page
  end
end

Site and Hosting::Page

resources :site do
  namespace :hosting do
    resources :page
  end
end

Those are all the easy ones. Sorry.

The Less Easy Ones

Project and Projects::Deliverable

collection_actions = [:index, :new, :create]

resources :projects do
  scope module: 'projects' do
    resources :deliverables, only: collection_actions
  end
end

namespace :projects do
  resources :deliverables, except: collection_actions
end

Using scope module: 'projects' means the router will look for DeliverablesController in the Projects namespace, but will only expect projects/1/deliverables in the URL instead of the redundant projects/1/projects/deliverables.

However, when addressing actions that don't required the parent resource referenced in the URL (e.g. shallow routing), you still want /projects/ as a "namespace" in the URL, so that's why you're establishing two resources: One for collection actions and one for member actions.

We now have two definitions of resources :deliverables, so one question remains: If we have resources nested under resources :deliverables, which of these two definitions do we define the nested resources under? The answer is the second one, the one for member actions.

Projects::Deliverable and Objective Under It

namespace :projects do
  resources :deliverables
end

resources :projects_deliverables, path: 'projects/deliverables' do
  resources :objectives
end

This one is kind of crazy, but to understand how it works you can imagine the various URLs that are enabled by these definitions:

namespace :projects do
  resources :deliverables
end

This enables /projects/deliverables/10 and defines the routing helper projects_deliverables_path. It routes requests to Projects::DeliverablesController (as we want it to).

resources :projects_deliverables, path: 'projects/deliverables' do
  # ...
end

This would also enable /projects/deliverables/10, except it would try to use ProjectsDeliverablesController to handle the request. That controller doesn't exist, but that doesn't matter because this URL is already being routed to the correct controller by the previous definition. However, when we use it as a container for the nested resources :objectives as seen here:

resources :projects_deliverables, path: 'projects/deliverables' do
  resources :objectives
end

... this enables /projects/deliverables/10/objectives to be routed to the un-namespaced ObjectivesController and ensures that controller receives params[:projects_deliverable_id] as well. Pretty great!

Abstract::Concept and Concrete::Thing

namespace :abstract do
  resources :concepts
end

resources :abstract_concepts, path: 'abstract/concepts' do
  namespace :concrete do
    resources :things
  end
end

This is very similar to the previous example, but the child resource isn't only not in the parent namespace, but it's in a different namespace. No problem, that's only an incremental change once you understand the last example.

That's It

If you know of a better way to do any of these things, please let me know and I'll update the article. I can't think of any other examples of complicated namespacing relationships that aren't either covered above or can't be covered through some combination of the examples and techniques described above. If you think of one, please send me a DM on Twitter so I can chew on it and update this article!



1 Shallow nesting might seem unideal for SEO in situations where you'd prefer the URL to represent more of a breadcrumb or hierarchy for the resource someone is looking at. That's true, but in those situations where an application's public URL structure needs to be in a particular way for SEO purposes, I don't use Rails' routing DSL at all, but instead use a separate library I've never released to sidestep Rails' routing and route arbitrarily structured URLs to specific controllers and resources. I started doing this because, for SEO purposes, a URL like /los-angeles/family-photographers is more desirable to me than the vanilla Rails + FriendlyId result of /areas/los-angeles/categories/family-photographers. I'm sorry I don't have a better resource to link to for this, but feel free to reach out to me via DM on Twitter if you need something like this.