Equality of Active Record Objects
By Chun-wei Kuo onThere was once a caching problem in a project I am working on.
The hash
of some posts is used as cache keys. According to the document, I expect Object#hash
will return different numbers if any attribute changes. But the cache just won’t refresh.
To figure out that problem, let’s start with two posts (in rails console):
[1] pry(main)> p1 = Post.first
Post Load (0.2ms) SELECT "posts".* FROM "posts" LIMIT 1
=> #<Post id: 4, title: "Hello", content: "World.", created_at: "2012-05-20 08:38:46", updated_at: "2012-05-20 08:38:46">
[2] pry(main)> p2 = Post.first
Post Load (0.2ms) SELECT "posts".* FROM "posts" LIMIT 1
=> #<Post id: 4, title: "Hello", content: "World.", created_at: "2012-05-20 08:38:46", updated_at: "2012-05-20 08:38:46">
They are the same post:
[3] pry(main)> p1 == p2
=> true
If we change an attribute of p1
:
[4] pry(main)> p1.title = "hi"
=> "hi"
Then p1
still equals to p2
:
[5] pry(main)> p1 == p2
=> true
They also have the same hash:
[6] pry(main)> p1.hash == p2.hash
=> true
But, why?
Actually, the hash of p1
is the hash of p1.id
:
[7] pry(main)> p1.hash
=> -2154204912980276923
[8] pry(main)> p1.id.hash
=> -2154204912980276923
We can confirm that from activerecord/lib/active_record/core.rb
:
def ==(comparison_object)
super ||
comparison_object.instance_of?(self.class) &&
id.present? &&
comparison_object.id == id
end
alias :eql? :==
def hash
id.hash
end
That is, when we compare two Active Record objects, we are comparing their ids.
For a cache key, you may consider using cache_key
method:
[9] pry(main)> p1.cache_key
=> "posts/5-20120523014730"
It returns a string, which contains the model name, id, and updated_at timestamp of the record.
Comments