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