May 15, 2024
•Last updated May 15, 2024
Rails Concerns vs. Modules and when to use each
As a novice or professional Ruby on Rails developer, you're likely familiar with the concepts of Concerns and Modules. Both are used to organize and reuse code but have different purposes and distinct use cases.
In this post, I’ll tap into the differences between Rails Concerns and Modules, provide real code examples, and discuss best practices for when to use each.
What are Concerns?
Rails Concerns are a way to extract functionality from models, controllers, and other classes into reusable modules. They were introduced in Rails 4 as a replacement for the old "model concern" pattern. Concerns are typically used to share code between models, controllers, or other classes that have similar functionality.
Example: Using a Concern to Share Validation Logic
Suppose you have two models, User
and Account
, requiring email and password validation. You can extract the validation logic into a Concern and include it modularly.
# app/models/concerns/validation.rb
module Validation
extend ActiveSupport::Concern
included do
validates_presence_of :email
validates_presence_of :password
end
end
# app/models/user.rb
class User < ApplicationRecord
include Validation
end
# app/models/account.rb
class Account < ApplicationRecord
include Validation
end
What’s the convention for naming concerns?
In Rails, the standard convention for naming Concerns is to use a suffix of able
or ing
to indicate that the module is a Concern. This helps to distinguish Concerns from other types of modules or classes. Since we humans author the code, this helps us distinguish the functionality right away.
Here are some examples of commonly used naming conventions for Concerns:
Sortable
(for a Concern that adds sorting functionality)Filterable
(for a Concern that adds filtering functionality)Validatable
(for a Concern that adds validation logic)Paginable
(for a Concern that adds pagination functionality)Serializable
(for a Concern that adds serialization logic)
Where should I put concerns in my Rails app?
This is up to you, but you might notice Rails 7+ ships with concerns
folders inside the app/models
and app/controllers
directories when you create a new app. You can create your own folders all the same, but I’d suggest following conventions so they’re easier to scale and the developer UX is more maintainable.
What is up with the included_do block?
The included_do
block is a special syntax in Rails Concerns that allows you to define code that will be executed when the Concern is included in a host class.
The code within the block is executed in the context of the host class and has access to its methods and attributes.
module MyConcern
extend ActiveSupport::Concern
included do
# This code will be executed when the Concern is included in a class
puts "MyConcern was included in #{self.name}"
end
end
class MyClass
include MyConcern
end
What are Modules in Rails?
Modules are a way to organize namespace-related code. They can be used to group related classes, methods, and constants. Modules are often used to create a namespace for utility methods or to encapsulate more complex logic. If you find your actual models become huge, then extracting them to an independent module might be a good focus.
Example: Using a Module to Encapsulate Utility Methods
Say you have a set of utility methods for working with dates. Those can be grouped into a Module:
# lib/date_formatter.rb
module DateFormatter
def self.format_date(date)
date.strftime("%Y-%m-%d")
end
def self.format_time(time)
time.strftime("%H:%M:%S")
end
end
You can include this in your app for easier reusability.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include DateFormatter
def index
@date = DateFormatter.format_date(Date.today)
@time = DateFormatter.format_time(Time.now)
end
end
Key Differences of Concerns vs. Modules and Best Practices
Keep these things in mind when choosing the concern or module approach.
Use Concerns for shared functionality
Concerns are ideal for sharing code between models, controllers, or other classes with similar functionality. If you repeat code between models or controllers, a concern might be a good extraction.
Use Modules for namespacing and utility methods
Modules are better suited for creating a namespace for utility methods or encapsulating complex logic. You might keep these inside the lib
directory, but they can also be autoloaded anywhere within the app
folder in your Rails app.
Avoid using Concerns for utility methods
If you have a set of utility methods that don't depend on the including class, use a Module instead of a Concern.
Keep concerns focused
I believe concerns should be small and focused on a specific functionality. Avoid cramming too much code into a single Concern.
Test your Concerns and Modules
Write tests for your Concerns and Modules to ensure they're working as expected.
When learning Rails, I brushed off concerns at first. With time and a lot of work, you begin to see their useful utility. It cleans up your app’s logic. I find it satisfying to “declutter” models and controllers of repeated code.
You can write more maintainable, efficient, and scalable code by leveraging Rails concerns, modules, or both.
Keep reading
- Ruby on Rails Routing
- A Beginner's Guide to Ruby on Rails Callbacks
- Should you use Ruby on Rails in 2024?
- New to Rails? Join 400+ developers and take my course Hello Rails to fast-forward your education.
Categories
Collection
Part of the Ruby on Rails collection
Products and courses
-
Hello Hotwire
A course on Hotwire + Ruby on Rails.
-
Hello Rails
A course for newcomers to Ruby on Rails.
-
Rails UI
UI templates and components for Rails.