Thoughts on rails concerns

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the architecture category.

Last Updated: 2024-04-25

Big picture

Advantages

1. Gets things dry fine for cross-cutting logic used in multiple classes

module Taggable
  extend ActiveSupport::Concern

  included do
    has_many :taggings, as: :taggable, dependent: :destroy
    has_many :tags, through: :taggings 
  end

  def tag_names
    tags.map(&:name)
  end
end

class Post
  include Taggable
end

class Product
  include Taggable
end

2. Gives a readable high-level overview of what roles a class has

class Person
  include Searchable
  include Visible
  include Trashable
end

This means the class is gonna be better organized internally.

3. Prevent ballooning of object inventory

Some of the service classes common in the Rails world become so abstract they are hard to understand. Usually they end with the letters -er - e.g. TagAdder, TagEditer, FileTrasher etc.

4. Leads to nicer, more predictable API

# Extracting to a stand-alone query object
Viewer.visible(current_account.posts, to: current_user)

# vs.
curent_account.posts.visible_to(current_user)

# thanks to following concern
module Visible
  extend ActiveSupport::Concern

  module ClassMethods
    def visible_to(person)
    ...
    end
  end
end

5. Handy for code re-use across projects

I had concerns for user authentication password resets, tracking etc., and these were easily ported to my other Rails apps.

Disadvantages

1. Merely visual organization. Does little for separation of concerns

This means, among other things, mocking will suck.

2. Callbacks in one concern will affect others

If you don't try to avoid this, this could leads to tests that require more stubbing to avoid callback issues that have little to do with that concern.

Henning Koch argues in some code bases you get scared to save a record because "who knows what could happen?" (Though I'd say they failed to be careful with idempotency)

3. Order of concern inclusion could be an issue

4. Worse for searching for methods in the one file

Not an issue, however, with project-wide grep.

Also, can be avoided by in-lining the concern definitions

class User
  module Visible
     ...
  end
  include Visible


end

Advice

References