aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation/predicate_builder
diff options
context:
space:
mode:
authorPhilippe Huibonhoa <phuibonhoa@gmail.com>2015-11-21 01:58:17 -0800
committerPhilippe Huibonhoa <phuibonhoa@gmail.com>2016-02-16 10:41:26 -0800
commit359adaedd95293af3abde954279ad51e11856d57 (patch)
treec8b8ada1a07783f0e049f8b94fa283c9d83db19c /activerecord/lib/active_record/relation/predicate_builder
parentb0b61b62f9c27eff5b9e0797a0de0d35d64d1620 (diff)
downloadrails-359adaedd95293af3abde954279ad51e11856d57.tar.gz
rails-359adaedd95293af3abde954279ad51e11856d57.tar.bz2
rails-359adaedd95293af3abde954279ad51e11856d57.zip
Fixed `where` for polymorphic associations when passed an array containing different types.
When passing in an array of different types of objects to `where`, it would only take into account the class of the first object in the array. PriceEstimate.where(estimate_of: [Treasure.find(1), Car.find(2)]) # => SELECT "price_estimates".* FROM "price_estimates" WHERE ("price_estimates"."estimate_of_type" = 'Treasure' AND "price_estimates"."estimate_of_id" IN (1, 2)) This is fixed to properly look for any records matching both type and id: PriceEstimate.where(estimate_of: [Treasure.find(1), Car.find(2)]) # => SELECT "price_estimates".* FROM "price_estimates" WHERE (("price_estimates"."estimate_of_type" = 'Treasure' AND "price_estimates"."estimate_of_id" = 1) OR ("price_estimates"."estimate_of_type" = 'Car' AND "price_estimates"."estimate_of_id" = 2))
Diffstat (limited to 'activerecord/lib/active_record/relation/predicate_builder')
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb10
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb57
2 files changed, 67 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
index e81be63cd3..d7fd878265 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
@@ -1,6 +1,16 @@
module ActiveRecord
class PredicateBuilder
class AssociationQueryHandler # :nodoc:
+ def self.value_for(table, column, value)
+ klass = if table.associated_table(column).polymorphic_association? && ::Array === value && value.first.is_a?(Base)
+ PolymorphicArrayValue
+ else
+ AssociationQueryValue
+ end
+
+ klass.new(table.associated_table(column), value)
+ end
+
def initialize(predicate_builder)
@predicate_builder = predicate_builder
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
new file mode 100644
index 0000000000..b6c6240343
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
@@ -0,0 +1,57 @@
+module ActiveRecord
+ class PredicateBuilder
+ class PolymorphicArrayHandler # :nodoc:
+ def initialize(predicate_builder)
+ @predicate_builder = predicate_builder
+ end
+
+ def call(attribute, value)
+ table = value.associated_table
+ queries = value.type_to_ids_mapping.map do |type, ids|
+ { table.association_foreign_type.to_s => type, table.association_foreign_key.to_s => ids }
+ end
+
+ predicates = queries.map { |query| predicate_builder.build_from_hash(query) }
+
+ if predicates.size > 1
+ type_and_ids_predicates = predicates.map { |type_predicate, id_predicate| Arel::Nodes::Grouping.new(type_predicate.and(id_predicate)) }
+ type_and_ids_predicates.inject(&:or)
+ else
+ predicates.first
+ end
+ end
+
+ protected
+
+ attr_reader :predicate_builder
+ end
+
+ class PolymorphicArrayValue # :nodoc:
+ attr_reader :associated_table, :values
+
+ def initialize(associated_table, values)
+ @associated_table = associated_table
+ @values = values
+ end
+
+ def type_to_ids_mapping
+ default_hash = Hash.new { |hsh, key| hsh[key] = [] }
+ values.each_with_object(default_hash) { |value, hash| hash[base_class(value).name] << convert_to_id(value) }
+ end
+
+ private
+
+ def primary_key(value)
+ associated_table.association_primary_key(base_class(value))
+ end
+
+ def base_class(value)
+ value.class.base_class
+ end
+
+ def convert_to_id(value)
+ value._read_attribute(primary_key(value))
+ end
+ end
+ end
+end