From 6e574e8a11635ec4e579e5b247f6492b9bdc9279 Mon Sep 17 00:00:00 2001 From: Gordon Chan Date: Thu, 31 Jul 2014 19:14:12 +1200 Subject: [PATCH] `HashWithIndifferentAccess.new` respects the default value or proc on objects that respond to `#to_hash`. Builds on the work of #12550 where `.new` will convert the object (that respond to `#to_hash`) to a hash and add that hash's keys and values to itself. This change will also make `.new` respect the default value or proc of objects that respond to `#to_hash`. In other words, this `.new` behaves exactly like `.new_from_hash_copying_default`. `.new_from_hash_copying_default` now simply invokes `.new` and any references to `.new_from_hash_copying_default` are replaced with `.new`. Added tests confirm behavior. --- activesupport/CHANGELOG.md | 6 ++++++ .../core_ext/hash/indifferent_access.rb | 2 +- .../hash_with_indifferent_access.rb | 14 +++++++------- activesupport/test/core_ext/hash_ext_test.rb | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 61e9927a07..3e68ca84d8 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,9 @@ +* `HashWithIndifferentAccess.new` respects the default value or proc on objects + that respond to `#to_hash`. `.new_from_hash_copying_default` simply invokes `.new`. + All calls to `.new_from_hash_copying_default` are replaced with `.new`. + + *Gordon Chan* + * `Object#with_options` executes block in merging option context when explicit receiver in not passed. diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb index 28cb3e2a3b..6df7b4121b 100644 --- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb +++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb @@ -6,7 +6,7 @@ class Hash # # { a: 1 }.with_indifferent_access['a'] # => 1 def with_indifferent_access - ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self) + ActiveSupport::HashWithIndifferentAccess.new(self) end # Called when object is nested under an object that receives diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 30e27267a0..a400e71aa8 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -58,6 +58,10 @@ module ActiveSupport if constructor.respond_to?(:to_hash) super() update(constructor) + + hash = constructor.to_hash + self.default = hash.default if hash.default + self.default_proc = hash.default_proc if hash.default_proc else super(constructor) end @@ -72,11 +76,7 @@ module ActiveSupport end def self.new_from_hash_copying_default(hash) - hash = hash.to_hash - new(hash).tap do |new_hash| - new_hash.default = hash.default - new_hash.default_proc = hash.default_proc if hash.default_proc - end + new(hash) end def self.[](*args) @@ -205,7 +205,7 @@ module ActiveSupport # hash['a'] = nil # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1} def reverse_merge(other_hash) - super(self.class.new_from_hash_copying_default(other_hash)) + super(self.class.new(other_hash)) end # Same semantics as +reverse_merge+ but modifies the receiver in-place. @@ -218,7 +218,7 @@ module ActiveSupport # h = { "a" => 100, "b" => 200 } # h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400} def replace(other_hash) - super(self.class.new_from_hash_copying_default(other_hash)) + super(self.class.new(other_hash)) end # Removes the specified key from the hash. diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index dbbb2d77da..b71206d2e3 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -996,6 +996,24 @@ class HashExtTest < ActiveSupport::TestCase assert hash.key?('a') assert_equal 1, hash[:a] end + + def test_new_with_to_hash_conversion_copies_default + normal_hash = Hash.new(3) + normal_hash[:a] = 1 + + hash = HashWithIndifferentAccess.new(HashByConversion.new(normal_hash)) + assert_equal 1, hash[:a] + assert_equal 3, hash[:b] + end + + def test_new_with_to_hash_conversion_copies_default_proc + normal_hash = Hash.new { 1 + 2 } + normal_hash[:a] = 1 + + hash = HashWithIndifferentAccess.new(HashByConversion.new(normal_hash)) + assert_equal 1, hash[:a] + assert_equal 3, hash[:b] + end end class IWriteMyOwnXML