require "active_record" # ActiveRecord::Associations::AssociationScope assumes that values # for Owner's fields will be concrete values that need to be type-cast. # # But our AbstractOwner returns field references (Arel::Attributes::Attribute) # and we need them to bypass type-casting. # module PluckMap module AssociationScope def self.[](version) const_get "Rails#{version.to_s.delete(".")}" end def self.create case ActiveRecord.version.segments.take(2) when [4,2] then self[4.2].create when [5,0] then self[5.0].create else self::Current.create end end # Rails 5.1+ class Current < ActiveRecord::Associations::AssociationScope def apply_scope(scope, table, key, value) if value.is_a?(Arel::Attributes::Attribute) scope.where!(table[key].eq(value)) else super end end def scope(association) if ActiveRecord.version.version < "5.2" super(association, association.reflection.active_record.connection) else super end end end # In Rails 5.0, `apply_scope` isn't extracted from `last_chain_scope` # and `next_chain_scope` so we have to override the entire methods to # extract `apply_scope` and bypass type-casting. # # Refer to https://github.com/rails/rails/blob/v5.0.7.2/activerecord/lib/active_record/associations/association_scope.rb#L61-L94 # class Rails50 < Current def last_chain_scope(scope, table, reflection, owner, association_klass) join_keys = reflection.join_keys(association_klass) key = join_keys.key foreign_key = join_keys.foreign_key value = transform_value(owner[foreign_key]) scope = apply_scope(scope, table, key, value) if reflection.type polymorphic_type = transform_value(owner.class.base_class.name) scope = scope.where(table.name => { reflection.type => polymorphic_type }) end scope end def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection) join_keys = reflection.join_keys(association_klass) key = join_keys.key foreign_key = join_keys.foreign_key constraint = table[key].eq(foreign_table[foreign_key]) if reflection.type value = transform_value(next_reflection.klass.base_class.name) scope = apply_scope(scope, table, reflection.type, value) end scope = scope.joins(join(foreign_table, constraint)) end def apply_scope(scope, table, key, value) if value.is_a?(Arel::Attributes::Attribute) scope.where(table[key].eq(value)) else scope.where(table.name => { key => value }) end end end class Rails42 < Current def bind(_, _, _, value, _) if value.is_a?(Arel::Attributes::Attribute) value else super end end end end end