By Sebastian Porto

Different controllers for different contexts

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:

resources :comments

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.

In app/controllers/users/comments_controller.rb

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 posts/1/comments/2 and 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:

app/views/posts/comments/show.html.erb
app/views//users/comments/show.html.erb

Common methods

But many times we don't want different views and now we have many routes that may seem redundant like:

posts/1/comments/2
users/1/comments/2

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: /comments?q=sam&created[lt]=1407458593

Get all comments with text 'sam' and created before given time but only in the context of user 3: /users/3/comments?q=sam&created[lt]=1407458593

So in conclusion, separating your contexts into multiple controllers can help a lot with making your application easier to understand, maintain and grow.

Latest Articles by Our Team

Our expert team of designers and developers love what the do and enjoy sharing their knowledge with the world.

We Hire Only the Best

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.

Free Community Workshops

We created the Ruby on Rails InstallFest and Ruby on Rails Development Hub to help introduce new people to software development and to help existing developers hone their skills. These workshops provide invaluable mentorship to train developers, addressing key skills shortages in the industry. Software development is a great career choice for all ages and these events help you get started and skilled up.

  • Installfest

    Installfest

    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.

    Learn more about Installfest

  • Development Hub

    Development Hub

    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.

    Learn more about Development Hub

  • Webinars

    Webinars

    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.

    Learn more about webinars

Next Community Events

Find more events

Get the “reinteractive Review” Monthly Email