hashie/hashie

3.6.0 Hashie::Mash#merge doesn't work for instances with default_proc that raise error

andrew-hai opened this issue · 13 comments

2.5.1 :001 > require 'hashie'
 => true 
2.5.1 :002 > mash = Hashie::Mash.new(foo: 1) { |hash, key| raise StandardError, "Key #{key} does not exist in hash #{hash.inspect}" }
 => #<Hashie::Mash foo=1> 
2.5.1 :003 > mash.merge(bar: 2)
Traceback (most recent call last):
        8: from /home/an/.rvm/rubies/ruby-2.5.1/bin/irb:11:in `<main>'
        7: from (irb):3
        6: from /home/an/.rvm/gems/ruby-2.5.1@test/gems/hashie-3.6.0/lib/hashie/mash.rb:203:in `deep_merge'
        5: from /home/an/.rvm/gems/ruby-2.5.1@test/gems/hashie-3.6.0/lib/hashie/mash.rb:210:in `deep_update'
        4: from /home/an/.rvm/gems/ruby-2.5.1@test/gems/hashie-3.6.0/lib/hashie/mash.rb:210:in `each_pair'
        3: from /home/an/.rvm/gems/ruby-2.5.1@test/gems/hashie-3.6.0/lib/hashie/mash.rb:212:in `block in deep_update'
        2: from /home/an/.rvm/gems/ruby-2.5.1@test/gems/hashie-3.6.0/lib/hashie/mash.rb:212:in `[]'
        1: from (irb):2:in `block in irb_binding'
StandardError (Key bar does not exist in hash #<Hashie::Mash foo=1>)
2.5.1 :004 > 

Thanks for filing this issue. What do you believe the behavior should be here? Could you please submit a test?

I agree with @michaelherold what is your expectation here? Isn't the above the correct behaviour? An exception should be raised since you defined it no?

I think it's not the expected behavior because assignment and merge should behave the same way.

2.5.1 :002 > mash = Hashie::Mash.new(foo: 1) { |hash, key| raise StandardError, "Key #{key} does not exist in hash #{hash.inspect}" }
 => #<Hashie::Mash foo=1> 
2.5.1 :003 > mash[:bar] = 2
 => 2 

Merge doesn't imply needing to check on whether the value exists.

Hi @dblock.

On the default block we override the default value for the getter. Thus any non defined member will raise an exception. Calling mash[:bar] = 2 we send a different message to the object and we override the default value by creating the corresponding member.

So I think that merge is behaving as expected not because of merge method's definition but rather by the intention of the code written.

Color me confused, why does merge call any getters?

Hi,

As for me, I expect the same behavior as for Hash#merge with the same default_proc. Also it works for previous versions. I think it because of changing alias_method definitions to alias.

@dblock You are right. It has nothing to do with merge...

@andrew-hai Care to try to write a spec and, hopefully, a fix?

This introduced the problematic change: https://github.com/intridea/hashie/pull/435/files. The problem is that calling regular_reader (Hash#[]) or custom_readerwill execute the default block that is passed.

I've made a super small change in a related PR where we check the key before calling any getter on the Mash.

@laertispappas and all others, thank you, Hashie::Mash#merge method works fine now.

Closed via #465

Do I presume correctly that this will appear in v3.6.1? Any ETA on v3.6.1? (Not pushing; really just wondering if there are any major blockages preventing its tagging/release.)

No ETA, sorry. My charity says I could make a release with some convincing.