Andy from Webcrunch

Subscribe for email updates:

Portrait of Andy Leverenz
Andy Leverenz

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

Link this article
Est. reading time: 5 minutes
Stats: 168 views

Categories

Collection

Part of the Ruby on Rails collection