A “well-behaved” object in Ruby needs to understand the following:
What makes two Ruby objects “equal”?
And which version of “equal” (there are several in Ruby)?
And what makes an object usable as a
And is that the same thing that makes them
Because I can never seem to remember the specifics. And because my searching the Interwebs seems to find related, but not specific help. And because I’ve got this blurgh-thing… I might as well use it to help future-me (and maybe you?).
Gimme the gist
eql? for use as a
Hash key, and then
eql? method to
== for the expected developer ergonomics.
Something like this:
class Message attr_reader :body, :subject def initialize(subject:, body:) @subject = subject @body = body end def eql?(other) other.class == self.class && other.body == body && other.subject == subject end alias == eql? def hash body.hash ^ subject.hash end end
What to know more about the specifics, or how to also make these objects
Read on, friend…
Surely someone else has covered this already
As it turns out, Russ Olsen covers this very topic, in depth and with great examples, in his book, Eloquent Ruby. I should know that because I’ve read that book, and recommend it! However, it always takes me several trips to The Googles to find a snippet which eventually points me back to that book. At which point I have to dig out my copy. All of this only after I’ve tried a dozen other search terms resulting in articles about the difference between the many forms of equality in Ruby.
So yes, to various degrees, it’s been covered before. There is nothing new under the Sun.
Like I said, I have this here blurgh and I’m gonna use it. 🤷
Every-day object equality
For every-day use, the form of object equality we most often use in Ruby is the
==, or double-equals, form.
By default, the
== operator is an alias of the
equal? method, which it inherits from
This form compares the object identity, effectively asking “are these the same object in memory?”
We don’t want this form, so we need to override it to something more meaningful for our object.
Often two objects are
==, or double-equals equal, if they are of the same class, and some set of their internal data is also
In the case above, that means both objects are a
Message, and their
subject are the same.
def ==(other) other.class == self.class && other.body == body && other.subject == subject end
Please go read Mr. Olsen’s excellent coverage of the topic for more details and in-depth examples.
Use as a
When used as a
Hash will call the object’s
hash method to get hash code (an
Integer value) to use when storing and searching for the key.
It then uses the object’s
eql? method to determine if two hash keys are the same key.
From the Ruby docs:
Integerhash value for this object. This function must have the property that
a.hash == b.hash.
The hash value is used along with
Hashclass to determine if two objects reference the same hash key. Any hash value that exceeds the capacity of an
Integerwill be truncated before being used.
The hash value for an object may not be identical across invocations or implementations of Ruby. If you need a stable identifier across Ruby invocations and implementations you will need to generate one with a custom method.
By default, objects inherit their
eql? methods from
Object, which compares the object identity, similar to
As you might guess, we don’t want that for our object.
hash, we can lean on the internals already having their own
hash values, and combine them.
We can use the exclusive-or operator (
^) to take two other hashes (numbers) and smash them together in a consistent manner.
def hash topic.hash ^ body.hash end
To determine if two objects are
eql?, and hence, the same keys in a hash, we want to make sure they’re of the same class, and some set of their internal data is also
def eql?(other) other.class == self.class && other.body == body && other.subject == subject end
It’s the same as the
== from above!
In practice, I often implement
hash since they are a bit of a duo - one requiring the other.
And then I alias
== to be the same as
def eql?(other) other.class == self.class && other.body == body && other.subject == subject end alias == eql?
And that’s it, these objects now know if they are everyday-equal, and can serve as
We’ve not covered
=== here; the links above provide explanations about when, why, and how you might need to reach for that form of equality amongst Ruby objects.
Again, go check out Mr. Olsen’s book; it’s great!
Being able to compare, and thus
sort two Ruby objects is a bit tangential to the forms of equality we’ve discussed here.
Objects with a “natural ordering” will implement the
<=> or spaceship operator.
This operator is used to determine if one object is less than the other, greater than the other, or equal to the other.
Comparable module will use that to implement several comparison operators.
And all of that can be use by other Ruby modules, like
Enmerable to do things like sort a collection.
Again, Mr. Olsen’s book, or any number of other articles, including the Ruby docs for
Comparable, cover this in great detail.
The gist, once more
To recap, implement the
eql? methods to use an object as a
eql? method to
== to cover they typical, “every day”, usage we expect when writing/reading Ruby.