Namespacing nested resources

Before I start, I would just like to say that I do really like the rails routing interface and am super appreciative of all the hard work that has gone into its current state.

Over the years, I’ve discovered some patterns that I find useful and am curious what others think.

resources :foos, :only => [:index, :create] do
  resources :bars, :only => [:index]
end

This code expects you to define FoosController and BarsController. Anymore, I always do the following:

resources :foos, :only => [:index, :create] do
  resources :bars, :only => [:index], :module => :foos
end

The difference is that it expects FoosController and Foos::BarsController. In other words, you now have a bars controller that’s specifically for bars in the context of a foo.

This frees you up for the eventual stand-alone bars controller or even a Quxes::BarsController a la:

resources :foos, :only => [:index, :create] do
  resources :bars, :only => [:index], :module => :foos
end
resources :quxes, :only => [:index] do
  resources :bars, :only => [:index], :module => :quxes
end
resources :bars, :only => [:index]

Given this organization pattern extends to the controller’s corresponding views, I’ve found it to provide lots of flexibility. IMO, it’s easy for folks to fall into the trap of "I have a Foo model, I must cram all perspectives of Foo into a single FooController".

Does anyone else do this? Could it be the default for nested routes? Or is it better as-is allowing you to opt in? :smiley:

5 Likes

I do this too, and I feel that the need to add module: :foos is a bit unintuitive. I’d expect the controller for a resource named bars nested within a resource named foos to follow module nesting by default.

I used to specify controller: 'foos/bars' everywhere before discovering module: :foos too

1 Like

Perhaps resources could benefit from a module: true (or namespace: true) to remove the repetition on the nested controllers?

Or maybe there is something that you think would help make the guides clearer on that matter?

This feels related to the discussion in Easier modular/component architecture or "Make the Monolith Majestic" !

If you have the following model:

class Blog < ApplicationRecord
  has_many :posts
end

then Post is generally the expected model name and the same pattern follows for controllers. However we do some magic with models where both Blog::Post and Post work - it looks for the nested model first before looking for the top level model. In principle we could use a similar pattern for finding the controller of a nested resource but my gut instinct is that it would have a lot of edge cases so would need to be opt-in for upgrading apps.

2 Likes

I think it would be more effective to present this organization pattern first. How to do it would then naturally follow.

It’s definitely one of the strategies I employ :slight_smile:

I’ll take a look at the Routing guide and see if there’s any quick wins.

Any thoughts on where the best venue for that would be?

I also run into this regularly. While a new resources statmement like sub_resources :bars, only: [:index] or sub_resources :foos, :bars, only: [:index] might make sense to shortcut/circumvent the extra parameters and default to sensible defaults, it also would add a bit of complexity (and … naming is hard).

I think my stance would be that it is worth it (to add another routing ‘function’ like sub_resource/nested_resource/has_resource).

1 Like

Perhaps implementing this pattern could be noted under Nested Resources in the existing Routing guide?

Think the extended reasoning and problems solved by this pattern needs a different home though, which seems to be the common theme to the other areas @Betsy_Haibel highlighted.

1 Like

I think the question is whether it makes sense for Rails to adopt this pattern by default (a nested resource is a distinct resource with a separate controller nested under the parent’s namespace), or stick with status quo (all resources with the same name go to the same controller regardless of nesting).

If this pattern is desirable but requires opt-in due to technical constraints, then I feel the existing module: :foos option is good enough. The tricky part is communicating the availability of this pattern and rationale behind whether people should adopt it.

1 Like

Even if no code changes come from this May of WTFs, I am learning a lot! I’m changing my code now to use modules on nested resources.

It was worth doing the exercise just for the learning opportunities.

2 Likes

That makes me really happy! Curious to see how you like it :slight_smile:

The payout for me is when you need a new perspective on a resource and now you have a nice little home for it to grow irrespective of its brethren :sunflower:

1 Like

Wrote a little blog post expanding on this technique: Embracing Perspectives with the Rails Router | webhaus

1 Like