Concerns in Rails — A Guide and an Example

Merdan Durdiyev
kode-art
Published in
3 min readMar 20, 2023

Welcome

Hello dear friends, coders, and enthusiasts.

Today we are going to dive into the topic of “Concerns” in Ruby on Rails. Concerns are an important topic in RoR but anyway, it’s up to a developer to decide whether to use them or not. And besides that, they need to be used carefully to avoid issues of so-called “circular dependency” and revealing too much info about the model to the Concern.

So, let’s get started…

What does a Concern help with?

A Concern is just a ruby module that extends ActiveSupport::Concern module.

You might have already seen the folders that are created by default when creating a new Rails project. Those folders are ‘app/controllers/concerns’ and ‘app/models/concerns’. These folders are where concerns should be placed.

Using a concern lets you extract the common logic from different classes into a reusable module.

What we have to do to extract some logic into a concern is write a concern module that extends the “ActiveSupport::Concern” module. So, let’s write an Archiveable concern module.

module Archivable
extend ActiveSupport::Concern

included do
scope :visible, -> { where(archived: false) }
scope :archived, -> { where(archived: true) }
end

def archive
update_attribute :archived, true
end
end

So, to use it in our models, we just need to include the above-described module within our model classes.

class Post < ApplicationRecord
include Archivable

has_many :comments

# ...
end

class Comment < ApplicationRecord
include Archivable

belongs_to :post

# ...
end

As you already might have noticed, this code assumes that you have an ‘archived’ attribute in both of the models (‘Post’ and ‘Comment’) to operate on. Otherwise, we won’t be able to use execute the intended code, because the ‘Archivable’ module makes use of the ‘archived’ attribute.

Two important parts of a Concern

A concern can provide two blocks that comprise different types of methods to be used in the included class.

  • included

The code inside this block is evaluated in the context of the including class. For instance, if a class ‘Comment ‘includes a concern, anything inside the ‘included’ block will be evaluated as if it was written inside the “Comment” class.

You can add validations, associations, scopes, or other methods, and all of these become instance methods of the including class.

  • class_methods

All the methods added inside this block become class methods of the including class. As a second option, you can create a nested module called “ClassMethods” where you can put your class methods.

module Visible
extend ActiveSupport::Concern

# This is where you place instance methods
included do
def toggle_visibility
toggle!(:is_visible)
end
end

# This is where you place class methods
class_methods do
def get_all_visible
all.select { |item| item.is_visible? }
end
end
end

Here’s the “Visible” concern where the two above-mentioned blocks reside and we place our instance and class methods accordingly within those blocks.

Good approaches to using Concerns

  • Single Responsibility Principle

A good concern should be able to work in isolation, so it must be dependency-free. It should have a very concrete and limited responsibility.

Bear in mind the Single Responsibility Principle when extracting something to a concern. That being said, you need to extract just a small, reasonable code to a concern so that it stays manageable and includable in several locations in the future.

Construct a concern that, for instance, just manages the Archive’ability, Exportability, and Notifiability of a model and not many of those features.

  • Using Concerns for multiple models

It is a good approach to extract Concerns when they tend to be useful in several models. This way we are obeying the DRY principle and using the same piece of code in several models. It shrinks the code and makes it manageable.

A good example of this might be the “Exportable” module/concern. This concern can be included in several models, like Email, Document, Table, Drawing and etc… This way, it becomes a very useful accessory to use.

Conclusion

We have covered the basics of implementing concerns in Ruby on Rails and it gives you a basic idea of why’s and how to’s about using concerns.

Actually, no code is perfect and it’s up to you to decide how to organize things and make the code perfectly readable. You need to use your own judgment and weigh the pros and cons of either of the options.

Hope to meet you in the next article!

Stay safe, stay healthy, and stay hungry !!! 👋

--

--