Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

HashWithIndifferentAccess better respects #to_hash

In particular, `#update`, `#merge`, `#replace` all accept objects which
respond to `#to_hash`, even if those objects are not Hashes directly.
  • Loading branch information...
commit 939f4eaed0b484aab13df3e3b2d1b20925fafa26 1 parent 799a7bc
@Peeja Peeja authored
View
11 activesupport/CHANGELOG.md
@@ -1,3 +1,14 @@
+* `HashWithIndifferentAccess` better respects `#to_hash` on objects it's
+ given. In particular `#update`, `#merge`, `#replace` all accept objects
+ which respond to `#to_hash`, even if those objects are not Hashes directly.
+
+ Currently, if `HashWithIndifferentAccess.new` is given a non-Hash (even if
+ it responds to `#to_hash`) that object is treated as the default value,
+ rather than the initial keys and value. Changing that could break existing
+ code, so it will be updated in the next minor version.
+
+ *Peter Jaros*
+
* Added `Object#presence_in` to simplify value whitelisting.
Before:
View
3  activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -72,6 +72,7 @@ def default(key = nil)
end
def self.new_from_hash_copying_default(hash)
+ hash = hash.to_hash
new(hash).tap do |new_hash|
new_hash.default = hash.default
end
@@ -125,7 +126,7 @@ def update(other_hash)
if other_hash.is_a? HashWithIndifferentAccess
super(other_hash)
else
- other_hash.each_pair do |key, value|
+ other_hash.to_hash.each_pair do |key, value|
if block_given? && key?(key)
value = yield(convert_key(key), self[key], value)
end
View
34 activesupport/test/core_ext/hash_ext_test.rb
@@ -23,6 +23,16 @@ def nested_under_indifferent_access
end
end
+ class HashByConversion
+ def initialize(hash)
+ @hash = hash
+ end
+
+ def to_hash
+ @hash
+ end
+ end
+
def setup
@strings = { 'a' => 1, 'b' => 2 }
@nested_strings = { 'a' => { 'b' => { 'c' => 3 } } }
@@ -411,6 +421,12 @@ def test_indifferent_update
assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 }
end
+ def test_update_with_to_hash_conversion
+ hash = HashWithIndifferentAccess.new
+ hash.update HashByConversion.new({ :a => 1 })
+ assert_equal hash['a'], 1
+ end
+
def test_indifferent_merging
hash = HashWithIndifferentAccess.new
hash[:a] = 'failure'
@@ -430,6 +446,12 @@ def test_indifferent_merging
assert_equal 2, hash['b']
end
+ def test_merge_with_to_hash_conversion
+ hash = HashWithIndifferentAccess.new
+ merged = hash.merge HashByConversion.new({ :a => 1 })
+ assert_equal merged['a'], 1
+ end
+
def test_indifferent_replace
hash = HashWithIndifferentAccess.new
hash[:a] = 42
@@ -442,6 +464,18 @@ def test_indifferent_replace
assert_same hash, replaced
end
+ def test_replace_with_to_hash_conversion
+ hash = HashWithIndifferentAccess.new
+ hash[:a] = 42
+
+ replaced = hash.replace(HashByConversion.new(b: 12))
+
+ assert hash.key?('b')
+ assert !hash.key?(:a)
+ assert_equal 12, hash[:b]
+ assert_same hash, replaced
+ end
+
def test_indifferent_merging_with_block
hash = HashWithIndifferentAccess.new
hash[:a] = 1
Please sign in to comment.
Something went wrong with that request. Please try again.