diff options
-rw-r--r-- | activerecord/lib/active_record/relation/merger.rb | 39 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/spawn_methods.rb | 7 | ||||
-rw-r--r-- | activerecord/test/cases/relation_test.rb | 17 |
3 files changed, 39 insertions, 24 deletions
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index 52cf0b7077..3f880ce5e9 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -3,32 +3,41 @@ require 'active_support/core_ext/hash/keys' module ActiveRecord class Relation - class Merger - attr_reader :relation, :other + class HashMerger + attr_reader :relation, :hash - def initialize(relation, other) - @relation = relation + def initialize(relation, hash) + hash.assert_valid_keys(*Relation::VALUE_METHODS) - if other.default_scoped? && other.klass != relation.klass - @other = other.with_default_scope - else - @other = other - end + @relation = relation + @hash = hash end def merge - HashMerger.new(relation, other.values).merge + Merger.new(relation, other).merge + end + + # Applying values to a relation has some side effects. E.g. + # interpolation might take place for where values. So we should + # build a relation to merge in rather than directly merging + # the values. + def other + other = Relation.new(relation.klass, relation.table) + hash.each { |k, v| other.send("#{k}!", v) } + other end end - class HashMerger + class Merger attr_reader :relation, :values - def initialize(relation, values) - values.assert_valid_keys(*Relation::VALUE_METHODS) + def initialize(relation, other) + if other.default_scoped? && other.klass != relation.klass + other = other.with_default_scope + end @relation = relation - @values = values + @values = other.values end def normal_values @@ -85,7 +94,7 @@ module ActiveRecord def merged_wheres if values[:where] - merged_wheres = relation.where_values + Array(values[:where]) + merged_wheres = relation.where_values + values[:where] unless relation.where_values.empty? # Remove duplicates, last one wins. diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 1d2151fdb2..41e55dfd0e 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -30,11 +30,8 @@ module ActiveRecord end def merge!(other) - if other.is_a?(Hash) - Relation::HashMerger.new(self, other).merge - else - Relation::Merger.new(self, other).merge - end + klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger + klass.new(self, other).merge end # Removes from the query the condition(s) specified in +skips+. diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index d1fb3e01d2..8a7a2441d4 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -130,9 +130,9 @@ module ActiveRecord test 'merging a hash into a relation' do relation = Relation.new :a, :b - relation = relation.merge where: ['lol'], readonly: true + relation = relation.merge where: :lol, readonly: true - assert_equal ['lol'], relation.where_values + assert_equal [:lol], relation.where_values assert_equal true, relation.readonly_value end @@ -162,6 +162,15 @@ module ActiveRecord relation.merge!(where: :foo) assert_equal [:foo], relation.where_values end + + test 'merging a hash interpolates conditions' do + klass = stub + klass.stubs(:sanitize_sql).with(['foo = ?', 'bar']).returns('foo = bar') + + relation = Relation.new(klass, :b) + relation.merge!(where: ['foo = ?', 'bar']) + assert_equal ['foo = bar'], relation.where_values + end end class RelationMutationTest < ActiveSupport::TestCase @@ -227,8 +236,8 @@ module ActiveRecord end test 'merge!' do - assert relation.merge!(where: ['foo']).equal?(relation) - assert_equal ['foo'], relation.where_values + assert relation.merge!(where: :foo).equal?(relation) + assert_equal [:foo], relation.where_values end end end |