Rails: Organizing Controllers with Modules

One of my Ruby on Rails apps has lots of models and a few models that each have lots of has_many associations. For instance, the Company model has 27 has_many associations. Naturally, I have routes and controllers for most of these associations, so it can be pretty overwhelming when you do an ls in the app/controllers directory. Unless you're intimately familiar with how all the models relate to each other, nothing will clue you in to the fact that a bunch of the controllers are for managing relations of Company and that incoming requests reach those controllers via nested routes. This is because the default behavior of the Rails router is to point nested routes to a non-namespaced controller.

So, if you have routes defined like this:

resources :companies do
  resources :addresses
end

The route /companies/1/address/2 will point to the AddressesController, instead of, say, the Companies::AddressesController. To me, this is confusing and unintuitive, but it is not a huge problem for smaller apps. It gets to be difficult once a routes file starts to look more like this:

resources :companies do
  resources :addresses
  resources :people
  resources :orders
end

resources :products
resources :industries
resources :salespeople

Now, when you look in your controllers directory, it will look like this:

# ls app/controllers
addresses_controller.rb
companies_controller.rb
industries_controller.rb
orders_controller.rb
people_controller.rb
products_controller.rb
salespeople_controller.rb

To figure out that the AddressesController, PeopleController, and OrdersController all handle nested routes requires either 1) opening each one up and reading it, or 2) perusing the routes file. Essentially, the app's routes, which are hierarchical in nature, have been flattened into a single level of hierarchy. Okay, maybe you don't think it's that big of a deal, but imagine trying to make sense of this when you have dozens of controllers and many of them handle nested routes. Luckily, Rails has some tools to help organize your controllers.

What we really want to do is namespace all the controllers that handle nested routes for Company associations in a module called Companies. So, the AddressesController from above would become the Companies::AddressesController and so on. When we do this, because of the way Rails looks for classes we have defined in our app, we will end up with a companies directory in app/controllers that neatly contains all of our namespaced controllers.

Some code will explain this better. Here's how you tell the router that a set of routes should be directed to controllers namespaced under a certain module:

resources :companies do
  scope module: "companies" do
    resources :addresses
    resources :people
    resources :orders
  end
end

resources :products
resources :industries
resources :salespeople

And now, when you look in your controllers directory, it should look like this (with the companies directory containing the namespaced controllers):

# ls app/controllers
companies_controller.rb
companies/
industries_controller.rb
products_controller.rb
salespeople_controller.rb

This is much more comprehensible. You know that all the controllers you see in the root controllers directory stand on their own, so to speak. And all the controllers in the companies directory are for handling nested Company routes (just remember to prefix the all of the controller class names with Companies:: when you define them). Plus, now there is a shared hierarchy between your routes file, the names of your controller classes, and the names and layout of the files on disk.