diff options
Diffstat (limited to 'activerecord/lib/active_record/relation/predicate_builder.rb')
-rw-r--r-- | activerecord/lib/active_record/relation/predicate_builder.rb | 82 |
1 files changed, 50 insertions, 32 deletions
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 2814771002..6a0cdd5917 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -1,7 +1,7 @@ module ActiveRecord class PredicateBuilder # :nodoc: def self.build_from_hash(engine, attributes, default_table) - predicates = attributes.map do |column, value| + attributes.map do |column, value| table = default_table if value.is_a?(Hash) @@ -15,42 +15,60 @@ module ActiveRecord table = Arel::Table.new(table_name, engine) end - attribute = table[column.to_sym] - - case value - when ActiveRecord::Relation - value.select_values = [value.klass.arel_table['id']] if value.select_values.empty? - attribute.in(value.arel.ast) - when Array, ActiveRecord::Associations::CollectionProxy - values = value.to_a.map { |x| - x.is_a?(ActiveRecord::Base) ? x.id : x - } - - if values.include?(nil) - values = values.compact - if values.empty? - attribute.eq nil - else - attribute.in(values.compact).or attribute.eq(nil) - end + build(table[column.to_sym], value) + end + end.flatten + end + + def self.references(attributes) + attributes.map do |key, value| + if value.is_a?(Hash) + key + else + key = key.to_s + key.split('.').first.to_sym if key.include?('.') + end + end.compact + end + + private + def self.build(attribute, value) + case value + when Array, ActiveRecord::Associations::CollectionProxy + values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x} + ranges, values = values.partition {|v| v.is_a?(Range)} + + values_predicate = if values.include?(nil) + values = values.compact + + case values.length + when 0 + attribute.eq(nil) + when 1 + attribute.eq(values.first).or(attribute.eq(nil)) else - attribute.in(values) + attribute.in(values).or(attribute.eq(nil)) end - - when Range, Arel::Relation - attribute.in(value) - when ActiveRecord::Base - attribute.eq(value.id) - when Class - # FIXME: I think we need to deprecate this behavior - attribute.eq(value.name) else - attribute.eq(value) + attribute.in(values) end + + array_predicates = ranges.map { |range| attribute.in(range) } + array_predicates << values_predicate + array_predicates.inject { |composite, predicate| composite.or(predicate) } + when ActiveRecord::Relation + value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? + attribute.in(value.arel.ast) + when Range + attribute.in(value) + when ActiveRecord::Model + attribute.eq(value.id) + when Class + # FIXME: I think we need to deprecate this behavior + attribute.eq(value.name) + else + attribute.eq(value) end end - - predicates.flatten - end end end |