How to use ActiveSupport Concerns with dynamic relations

Posted . Visible to the public.

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
Last edit
Judith Roth
License
Source code in this card is licensed under the MIT License.
Posted by Judith Roth to makandra dev (2021-04-30 13:06)