Bug in HashWithIndifferentAccess?

102 views
Skip to first unread message

Yves-Eric

unread,
Sep 1, 2015, 11:56:34 AM9/1/15
to Ruby on Rails: Core
Hi all,


I think I have stumbled upon a bug in HashWithIndifferentAccess.
I cannot pinpoint it exactly, but I have written a small test case that exhibits the buggy behavior:


The same code works fine if we use a regular Hash for `@store` instead of a HashWithIndifferentAccess.
Is this a known issue or should I fill a bug report?


Thank you,

Mohamed Wael Khobalatte

unread,
Sep 1, 2015, 12:15:15 PM9/1/15
to rubyonra...@googlegroups.com
You are not storing waldo where you think you are storing it. On first access `@store[:foo]` is already initialized, so it does away with your waldo assignment, then on second and third access you are populating it. `my_hash[:baz]` is `@store[:foo][:baz]`. 

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-co...@googlegroups.com.
To post to this group, send email to rubyonra...@googlegroups.com.
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.



--
Wael Khobalatte

James Coleman

unread,
Sep 1, 2015, 12:21:08 PM9/1/15
to rubyonra...@googlegroups.com
Mohamed,

I don't believe that's correct. And testing it out by replacing the HashWithIndifferentAccess with a regular Ruby Hash gives the expected result, so it's definitely different behavior than expected.

That being said, I just tested it on Rails 4.2.3 and it works as expected, so I'm not sure why Yves is seeing a different result.

Matt Jones

unread,
Sep 1, 2015, 4:21:38 PM9/1/15
to rubyonra...@googlegroups.com
This code in your example:

@store[:foo] ||= {bar: 'BAR'}

does not do what you may be thinking it does. HWIA overrides the `[]=` operator:

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/hash_with_indifferent_access.rb#L96

to convert incoming plain Hash objects into HWIA. So the object that eventually is stored in `@store[:foo]` is NOT the one that was passed to the assignment operator.

You can check this explicitly:


require 'active_support/all'

@store = ActiveSupport::HashWithIndifferentAccess.new

def my_hash
@store[:foo] ||= {bar: 'BAR'}
end

first_time = my_hash # => {:bar =>”BAR”}
second_time = my_hash # => {“bar”=>”BAR”}

Note that `first_time` and `second_time` don’t have matching `inspect` results.

Assignment operators *always* return the value passed on the right-hand side, regardless of what the underlying `[]=` method returns. This means that in cases where the object doesn’t require conversion (coalwater’s modification from your Gist) the object returned from `||=` DOES match, and Waldo is found.

Not sure if there’s a fix for this - the converting-on-[]= behavior is something many applications are likely to depend on, and the behavior of assignment operators is a core Ruby issue.

—Matt Jones
signature.asc

James Coleman

unread,
Sep 1, 2015, 4:29:56 PM9/1/15
to rubyonra...@googlegroups.com
The []= operator should still return whatever it sets, though, which would mean the two different accesses should return the same thing. And I believe this is the case currently. To not do so would be a legitimate bug.

What version of Rails is the example using?

Yves-Eric

unread,
Sep 1, 2015, 8:57:47 PM9/1/15
to Ruby on Rails: Core
I am seeing this with Rails 4.2.4.

Yves-Eric

unread,
Sep 1, 2015, 10:13:16 PM9/1/15
to Ruby on Rails: Core
Thanks Matt, your analysis is spot on. So the root cause actually a core behavior of Ruby. I found this pretty good explanation too on StackOverflow:

Reply all
Reply to author
Forward
0 new messages