How to use Ruby on Rails Concerns

A Rails concern is a module that extends the ActiveSupport::Concern module.

You can use Сoncerns to store common code for several classes there, or for refactoring to separate semantically similar code in separate modules.

A concern provides two blocks:

module SampleConcern
  extend ActiveSupport::Concern
  
  included do
    ...
  end
  
  class_methods do
    ...
  end
end

included:

The code inside the included block is evaluated in the context of the including class.

class_methods:

Here you can implement methods that will become methods of the class to which the container is included.

Let’s look at an example:

Concern:

module AuthenticationConcern
  extend ActiveSupport::Concern
  
  included do
    before_action :authenticate
  end
  
  private
    def authenticate
      if authenticated_user = User.find_by(id: cookies.encrypted[:user_id])
        current_user = authenticated_user
      else
        redirect_to new_session_url
      end
    end
end

Controller:

class ApiBaseController < ActionController::Base
  include AuthenticationConcern
  ...
end

By using Сoncerns, we have moved the code responsible for user authorization to a separate module.

Testing

Concerns are also convenient in that they can be tested in isolation instead of covering all classes where the concern is included with tests.

require 'rails_helper'
class FakeController < ApplicationController
  include AuthenticationConcern
  
  def new; end
end
RSpec.describe FakeController, type: :controller do    
  context '#new' do
    context 'valid user'  do
      get :new, headers: {token: 'valid_token'}     
      it { expect(response).to have_http_status(:success) }
    end
    
    context 'invalid user' do 
      get :new, headers: {token: 'invalid_token'}
      it { expect(response).to redirect_to(new_session_url) }
    end
  end
end