Read more

How to use ActiveSupport Concerns with dynamic relations

Deleted user #4117
April 30, 2021Software engineer

The usual way to build a relation in a ActiveSupport::Concern Show archive.org snapshot is this:

module MyModule
  extend ActiveSupport::Concern

  included do
    scope :disabled, -> { where(disabled: true) }
  end

end
Illustration book lover

Growing Rails Applications in Practice

Check out our e-book. Learn to structure large Ruby on Rails codebases with the tools you already know and love.

  • Introduce design conventions for controllers and user-facing models
  • Create a system for growth
  • Build applications to last
Read more Show archive.org snapshot

However, if you have a association with a polymorphic model, where you have to select based on the kind of record, using included like this will not produce the wanted results:

module MyModule
  extend ActiveSupport::Concern

  included do
    has_many :tasks,
      -> { where concrete_mission_type: self.class.name },
      foreign_key: :mission_id,
      inverse_of: :mission
  end
end
> mission.tasks.to_sql
"SELECT \"tasks\".* FROM \"tasks\" WHERE \"tasks\".\"mission_id\" = 735 AND \"tasks\".\"concrete_mission_type\" = 'ActiveRecord::Relation'"

You can fix that through using self.included(subclass) in your concern:

module MyModule
  extend ActiveSupport::Concern

  def self.included(subclass)
    subclass.class_eval do
      has_many :tasks,
        -> { where concrete_mission_type: subclass.name },
        foreign_key: :mission_id,
        inverse_of: :mission
    end
    super
  end
end
> mission.tasks.to_sql
"SELECT \"tasks\".* FROM \"tasks\" WHERE \"tasks\".\"mission_id\" = 737 AND \"tasks\".\"concrete_mission_type\" = 'ConspirationFinder'"

If you want to know more about self.included, see this card.

Posted to makandra dev (2021-04-30 15:06)