Railsで新しいActiveRecordをキーにしたあと、saveするとfetchできなかった
class Item < ActiveRecord; end
item = Item.new
item_hash = { item => 'fetchできたよ' }
item.save!
# fetchできない
p item_hash[item]
# => nil
どうしてこうなったか
ActiveRecordはhashを再定義している
def hash
if id
self.class.hash ^ id.hash
else
super
end
end
以下の現象が起る
class Item
attr_accessor :id
def hash
id
end
end
item = Item.new
item.id = 1
items = []
item_hash = {}
items << item
item_hash[item] = 'fetchできたよ'
p item_hash[items[0]]
# => 'fetchできたよ'
# hash methodに依存している値を変える
item.id = 2
# fetchできない
p item_hash[items[0]]
# nil
# keys経由なら何故か一致する
p item_hash.keys[0] == items[0]
# => true
# 同様にhashも一致する
p item_hash.keys[0].hash == items[0].hash
# => true
# 何がなんだかわからない
p item_hash[item_hash.keys[0]]
# => nil
p item_hash.has_key?(item_hash.keys[0])
# => false
なんでこうなるの?(予測)
key側のhash値は代入のタイミングでしか計算されないので、その後hashが変わると一致しなくなる。
item_hash[item] = 'fetchできたよ'
.hashや.eql?で比較するとhashが再計算されるためtrueになる
object_idで比較するようにすれば回避できる
追記
コメントでHash#rehashを教えていただきました
リファレンスにがっつり書いてありますね。