Ahmed Nadar

July 30, 2024

Understanding Rails Concerns

CleanShot 2024-07-30 at 15.19.28@2x.png


Having a debate about better coding practices is a healthy part of growth for any developer, especially within opinionated frameworks like Ruby on Rails. One feature that often sparks discussion is the use of ActiveSupport::Concern.

To illustrate, 37signals recently released Writebook, showcasing effective use of controller and model concerns. They adhered to Rails conventions without creating different folder structures or combining related classes in one file to minimize file count. Instead, they followed the Rails doctrine of convention over configuration. While it’s not mandatory to mimic their approach, having a consistent convention in place is beneficial, allowing flexibility in how you implement it.

The debate about using ActiveSupport::Concern is not new, and it won't be the last. Opinions on its use often come from personal preferences or company policies rather than understanding why Rails includes it and its merits or drawbacks.

While I set to do further reading about it, to form my own reasoning, I received an email from Hrishi Mittal's Learnetto newsletter. He sent out a quiz for Rails developer and the first question was about concern, what a coincidence.

- What is the purpose of the `ActiveSupport::Concern` module in Rails?
  1.  To handle HTTP requests and responses
  2.  To manage database migrations
  3.  To define model associations
  4. To provide a way to extend classes with reusable functionality

What is the correct answer?
——

To address Hrishi's question directly, let's check Rails' Concerns.

The ActiveSupport::Concern module in Rails serves several important purposes, primarily focused on enhancing the readability and organization of Ruby classes, especially models and modules. It provides a cleaner, more elegant syntax for declaring class-level methods and extending classes with additional functionality. Here are key benefits of using ActiveSupport::Concern:

1. Class-Level Methods

ActiveSupport::Concern allows you to define class-level methods directly within the module or class definition itself, without needing to open the class beforehand. This makes the code more concise and easier to read.

module Commentable
  extend ActiveSupport::Concern

  included do
    # Class-level methods go here
  end

  def comments_count
    # Instance method implementation
  end
end

class Post < ApplicationRecord
  include Commentable
end

2. Mixins Without Opening Classes

ActiveSupport::Concern simplifies including modules in classes by avoiding the need to explicitly open the class. This is particularly useful for adding behaviours to models or other classes without altering their source code directly.

module Logger
  extend ActiveSupport::Concern

  included do
    before_action :log_action
  end

  private

  def log_action
    Rails.logger.info("Action #{action_name} performed on #{model_name.humanize.downcase}")
  end
end

class Post < ApplicationRecord
  include Logger
end

# Creating a post and performing actions on it will now be logged.
post = Post.create!(title: "Ruby on Rails Tutorial", body: "Learn Ruby on Rails the easy way.")
post.update(body: "Updated content.") # This action will be logged.

3. Automatic Inclusion of Dependencies

When you include a module that extends ActiveSupport::Concern, Rails automatically includes any dependencies listed in the :include option. This reduces boilerplate code and keeps your class definitions clean.

module CommentAttributes
  extend ActiveSupport::Concern

  included do
    attribute :content, :string
    attribute :user_id, :integer
  end
end

module Commentable
  extend ActiveSupport::Concern

  included do
    has_many :comments
    include CommentAttributes
  end
end

class Post < ApplicationRecord
  include Commentable
end

# Now, when creating a comment, we can directly assign values to the attributes defined in CommentAttributes.
comment = post.comments.create!(content: "Great tutorial!", user_id: current_user.id)

4. Accessible Instance Methods

ActiveSupport::Concern provides a mechanism to define accessible instance methods that can be called from the parent class. This is useful for encapsulating related methods within a module and making them accessible to the class that includes the module.

module CommentCounter
  extend ActiveSupport::Concern

  included do
    has_many :comments
  end

  def comments_count
    comments.count
  end
end

class Post < ApplicationRecord
  include CommentCounter
end

# Now, we can easily get the total number of comments on a post.
post = Post.create!(title: "Ruby on Rails Tutorial", body: "Learn Ruby on Rails the easy way.")
post.comments.create!(content: "Great tutorial!") # Add a comment to the post.
puts "Total comments on this post: #{post.comments_count}"

5. Cleaner Syntax for Multiple Inclusions

When including multiple modules that extend ActiveSupport::Concern, you can use the :extend option to apply them all at once, resulting in cleaner and more organized code.

class Post < ApplicationRecord
  include Commentable
  include Searchable
end


Final Notes…

Using ActiveSupport::Concern in your application can significantly improve code organization and maintainability. It helps you simplify how you declare class-level methods, manages dependencies, and the inclusion of modules, making your codebase more maintainable and easier to understand.

Now, can you answer Hrishi's question that we saw earlier? 👆🏼

I found myself guilty of cramming everything in a one file, or not having a enough DRY files and folders. I tell myself, it is a learning process. It needs time and consistent work.

~~~~~
I hope you enjoyed this article and learned something new! Your questions and feedback are always welcome—feel free to reply, as I'd love to hear from you.
For more articles like this, subscribe to my blog and get future posts straight to your inbox. Thank you for your support!

~~~~~
Coming to RailsWorld?
Check out my series, Ahmed’s Unofficial RailsWorld Guide to Toronto! This first-of-its-kind guide is perfect for RailsWorld attendees. I write about the history of the amazing venue, Evergreen Brickworks, take you on a ride from the airport to the city, hotels and around, and explore Toronto's best spots to eat, visit, and have fun. Whether you're attending the conference or just visiting the city, you'll find something valuable.


Yours,
Ahmed Nadar

About Ahmed Nadar

Ruby on Rails enthusiast at heart. I run RapidRails agency focus on Rails development & UI stack. Maker of RapidRails UI component for Ruby on Rails.
Find me on Twitter – @ahmednadar