Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

hash.delete_if { |key, value| true } doesn't delete ... why?

I am working on the acts_as_taggable_on plugin, but there is something I can't really understand (even if it is just a very simple code line).

puts "before: " + cache.inspect
# cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class } # original code line
cache.delete_if { |key, value| true } # my test code
puts "after: " + cache.inspect

# output
before: {#<TaggableUser id: 1, name: nil>=>["dog"]}
after: {# TaggableUser id: 1, name: nil>=>["dog"]}

My problem is that the cache.delete_if doesn't delete anything even if it always evaluates to true. I simply don't get why ... and really tried much. It's only the problem with that cache hash. But I really couldn't find anything special about that particular hash.

The cache is created in that method:

def cached_owned_tag_list_on(context)
  variable_name = "@owned_#{context}_list"
  cache = instance_variable_get(variable_name) || instance_variable_set(variable_name, {})
end

The full code can be viewed here (see line 60): http://github.com/mbleigh/acts-as-taggable-on/blob/master/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb#L60

One step further

When I do a rehash before the delete_if it works. What can "corrupt" a hash in a way that rehash is needed before any deletions works?

like image 675
Zardoz Avatar asked Nov 22 '25 04:11

Zardoz


1 Answers

From the documentation of rehash:

 call-seq:
   hsh.rehash -> hsh

 Rebuilds the hash based on the current hash values for each key. If
 values of key objects have changed since they were inserted, this
 method will reindex <i>hsh</i>. 

So your keys (which are regular ActiveRecord instances) had their hashes changed from the time they were created. Looking into the AR sources:

# File activerecord/lib/active_record/base.rb, line 1613
1613:       def hash
1614:         id.hash
1615:       end

So, their id's were changed. Why can that happen? Well, the obvious cause is that the object was created, then put into hash, and after that saved (which assigned it an id and changed its hash).

Also, this has another bad consequence: as the hash of all those newly-created objects is that of nil, if there were multiple unsaved objects added to the hash, they all will occupy the same slot, and trying to index the hash with some other unsaved object will return nonsense.

like image 65
whitequark Avatar answered Nov 23 '25 18:11

whitequark