Organising Rails controllers
I’ve seen many projects that have over 50 controllers in their main controllers directory. This makes finding related code very tedious. In this article I’ll show you a few tips how to organise your code into separate directories.
Namespaces
Namespaces are usually most common method to structure controllers. I’m sure you have seen or used /admin/* namespaces before. Its idea is to separate bigger parts of the application.
# config/routes.rb
namespace :admin do
resources :posts
resources :categories
end
Namespaces should be short and simple (e.g admin, advertising). Abbreviations are also allowed if they are understandable for the site visitors (e.g instead of writing human_resources_management, just use hrm).
The reason why we want namespaces to be short is to keep rails url helpers short. Here are few examples with the admin namespace:
form_for [:admin, Post.new] link_to "post link", [:admin, @post] link_to "post link", admin_post_path(@post)
Modules
Modules are meant for putting related code into separate directories without changing URLs nor url helpers. Lets say we have two different kinds of posts: drafts and published posts. They are related so we might want to put them in the same directory. We can put them into app/controllers/posts/ directory and add following routes:
# config/routes.rb
scope :module => :posts do
resources :drafts
resources :published_posts
end
Drafts controller itself might look something like this:
# app/controllers/posts/drafts_controller.rb
module Posts
class DraftsController < ApplicationController
end
end
Another benefit of using modules is that you can have two controllers with the same name. Lets say we have controllers ongoing_surveys and closed_surveys. Both of them have responders page. In the ongoing survey page you can add and remove responders, but in the closed survey page you can only see statuses of responders. Instead of making one responders controller and adding many conditionals we can separate these into two.
# config/routes.rb
resources :ongoing_surveys do
scope :module => :ongoing_surveys do
resources :responders, :only => [:index, :create, :destroy]
end
end
resources :closed_surveys do
scope :module => :closed_surveys do
resources :responders, :only => [:index]
end
end
end
Now we have:
app/controllers/ongoing_surveys/responders_controller.rb app/controllers/closed_surveys/responders_controller.rb app/views/ongoing_surveys/responders/index.html.erb app/views/closed_surveys/responders/index.html.erb ...
Rails url helpers can be still used like there are no modules present. e.g
ongoing_survey_responders_path(@survey) polymorphic_path([@closed_survey, :responders]) # In the next post I'll show how to separate Survey into # ClosedSurvey and OngoingSurvey without inheritance.
Next time
Next time I’ll show you how to organise Rails models and how to avoid conflicts with Rails polymorphic url helpers.