How to use ActiveSupport Concerns with dynamic relations

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

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.

Judith Roth Almost 3 years ago