When we first start with Rails we learn to create one controller for each resource, e.g. CommentsController to deal with comments. Our routes file might look like:
But soon things get more complicated, suddenly we might want to get all of the comments for a post or all the comments for a user. Initially, what we might attempt to do is add conditional logic in our CommentsController to deal with this (get the comments for a post when given a post, get the comments for a user when given a user).
What if we also want all the comments for the current user and all the comments for everyone? The controller quickly becomes convoluted, unmaintainable and difficult to test.
The solution is simple, use different controllers for different contexts.
To cater for the four use cases above we would have a routes file that looks like:
# route for all comments resources :comments resources :posts do # route for comments for a particular post resources :comments, controller: 'posts/comments' end resources :users do # route for comments for a particular user resources :comments, controller: 'users/comments' end resource :me do # route for comments for the current user resources :comments, controller: 'me/comments' end
Now we have four controllers. Yes, there are more files, but this new structure is easier to test and reason about. Our controllers will be placed in a subfolder for each context e.g.
class Users::CommentsController < ApplicationController def index # retrieve the comments for the user params[:user_id] end end
Different views for different contexts
Another benefit is that you might want to show different views depending on the context, for example
users/4/comments/2 would show the same comment (2) but in a totally different way. You would simply create views in different files and everything would fall into place:
But many times we don't want different views and now we have many routes that may seem redundant like:
We could fix this with partials but we don't need to. We can simplify the routes by only having the ones we need for different context.
- In the context of post we want to list and create comments.
- In the context of user we only want to list comments.
The other actions show, edit, update and destroy could just go to the root comments controller. This is because this actions have all the information they need (the id of the record).
Our routes file would look like:
resources :comments resources :posts do # only respond to the actions we care about # in this case list and create comments for a post resources :comments, only: [:index, :new, :create], controller: 'posts/comments' end resources :users do # only respond to the actions we care about # in this case listing comments for a user resources :comments, only: [:index], controller: 'users/comments end
Searching and filtering
Searching and filtering should be treated differently, they are not contexts like post or user. You should consider searching and filtering as refinements, potentially all our controller above could implement searching and filtering. The most common way to do this is by passing these refinements in the query string:
Get all comments with text 'sam' and created before given time:
Get all comments with text 'sam' and created before given time but only in the context of user 3:
So in conclusion, separating your contexts into multiple controllers can help a lot with making your application easier to understand, maintain and grow.
The Axioms of Software Development – Part 5
Is it worth upgrading your Rails application?
The Axioms of Software Development - Part 4
reinteractive is Australia’s largest dedicated Ruby on Rails development company. We don’t cut corners and we know what we are doing.
We are an organisation made up of amazing individuals and we take pride in our team. We are 100% remote work enabling us to choose the best talent no matter which part of the country they live in. reinteractive is dedicated to making it a great place for any developer to work.
Webinars are our online portal for tips, tricks and lessons learned in everything we do. Make the most of this free resource to help you become a better developer.
The Ruby on Rails Installfest includes a full setup of your development environment and step-by-step instructions on how to build your first app hosted on Heroku. Over 1,800 attendees to date and counting.
The Ruby on Rails Development Hub is a monthly event where you will get the chance to spend time with our team and others in the community to improve and hone your Ruby on Rails skills.