Helping devs understand concerns faster

Yes, concerns are just modules, yet they’re also more than that. They’re also about a style of project organization. So they’re really doing a lot of work, and no wonder it’s confusing! And just like it’s not always obvious that app/models can contain POROs or other non-db backed models, it’s not obvious that app/models/concerns isn’t really where you’d put most concerns.

I’m extracting from DHHs screencast here.

We have an Active Record model that includes a concern, here expressed as just a module, which wraps a PORO for a nicer call API. All namespaced within User:: and app/models/user. This is how the vast majority of models at Basecamp look, for instance.

Note: this is about the organization, not so much the content within, in the real app there’d be more code in these pieces.

# app/models/user.rb
class User < ApplicationRecord
  include Notifiee
end

# app/models/user/notifiee.rb
module User::Notifiee
  # Philosophically a concern, i.e. a role a model can play, but:
  # extend ActiveSupport::Concern is not needed without included/class_methods calls.

  def notifications
    @notifications ||= User::Notifications.new(self)
  end
end

# app/models/user/notifications.rb
class User::Notifications
  attr_reader :user
  delegate :person, to: :user

  def initialize(user)
    @user = user
  end
end

I totally wish we had some more documentation or tooling that could push you in this direction or make it more obvious how this could work — perhaps in guides or generators.

9 Likes