Enhancing Code Reusability with Rails Concerns
Write cleaner, modular, and maintainable Rails code using Concerns
Introduction
As a Rails application grows, duplicated code and bloated models become a problem. Rails Concerns offer a structured way to extract reusable logic into modular components, keeping your code clean, maintainable, and DRY (Don’t Repeat Yourself).
In this guide, we will cover:
✔️ What are Rails Concerns?
✔️ How to use Concerns effectively
✔️ Best practices for modular design
✔️ Avoiding common pitfalls
1. What Are Rails Concerns?
Concerns are modules designed to extract shared behavior from models, controllers, and other parts of a Rails application.
They follow Ruby’s mixin pattern, allowing you to extend ActiveRecord models or controllers without cluttering them.
📌 Where to Place Concerns?
- For Models →
app/models/concerns/
- For Controllers →
app/controllers/concerns/
2. Creating a Model Concern
Example: Extracting Soft Deletion Logic
Instead of repeating soft delete logic in multiple models, extract it into a Concern.
📌 Step 1: Create the Concern file
# app/models/concerns/soft_deletable.rb
module SoftDeletable
extend ActiveSupport::Concern
included do
scope :active, -> { where(deleted_at: nil) }
end
def soft_delete
update(deleted_at: Time.current)
end
def restore
update(deleted_at: nil)
end
def deleted?
deleted_at.present?
end
end
📌 Step 2: Include the Concern in Models
class User < ApplicationRecord
include SoftDeletable
end
class Post < ApplicationRecord
include SoftDeletable
end
🚀 Why?
- Eliminates code duplication
- Keeps models focused on core functionality
3. Creating a Controller Concern
Example: Extracting Authentication Logic
Instead of writing authentication logic in every controller, move it to a Concern.
📌 Step 1: Create the Concern file
# app/controllers/concerns/authenticatable.rb
module Authenticatable
extend ActiveSupport::Concern
included do
before_action :authenticate_user!
end
private
def authenticate_user!
redirect_to login_path, alert: "Please log in" unless session[:user_id]
end
end
📌 Step 2: Include in Controllers
class DashboardController < ApplicationController
include Authenticatable
def index
# Dashboard logic
end
end
🚀 Why?
- Keeps controllers lightweight
- Ensures authentication logic is consistent across all controllers
4. Best Practices for Using Concerns
✅ Keep Concerns focused – Each Concern should only handle one responsibility.
✅ Use included do
for ActiveRecord hooks and scopes to ensure proper execution.
✅ Name Concerns descriptively – e.g., Loggable
, Archivable
, Trackable
.
✅ Avoid deep interdependencies between Concerns to keep them modular.
❌ Avoid Fat Concerns – If a Concern grows too large, split it into multiple modules.
5. Common Pitfalls and How to Avoid Them
❌ Overusing Concerns for Business Logic
- If a Concern becomes too complex, consider using Service Objects instead.
❌ Polluting the Global Namespace
- Use descriptive names to prevent conflicts.
❌ Adding Too Many Responsibilities to a Single Concern
- A Concern should do one thing well. If it has multiple roles, split it into separate modules.
Conclusion
🚀 Key Takeaways:
✔️ Concerns help extract reusable code and keep Rails models and controllers clean.
✔️ Model Concerns can handle scopes, callbacks, and shared logic.
✔️ Controller Concerns improve modularity by encapsulating common filters and methods.
✔️ Follow best practices to keep Concerns maintainable and scalable.
🔗 Next Steps: Explore Service Objects for even more organized business logic!