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 online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
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)