DEV Community

Cover image for Concerns in Rails — A Guide and an Example
Merdan Durdyyev
Merdan Durdyyev

Posted on • Updated on

Concerns in Rails — A Guide and an Example

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 Ruby on Rails 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 of 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 “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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

As you already might have notice, this code assumes that you have ‘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 ‘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
Enter fullscreen mode Exit fullscreen mode

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 include’able into several locations in the future.

Construct a concern that, for instance, just manages Archive’ability, Exportability, 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 “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, stay hungry !!! 👋

Top comments (0)