Thursday, October 13, 2011

Ruby Equality And Object Comparison

Ruby has three main equality test methods, ==, eql? and equal?. These methods normally live in the Object class. Inside the Object class all there methods do exactly the same thing, they test if two objects are exactly the same object_id.

Just take this example:-

string1 = "abc"
class MyObject
end
object1 = MyObject.new
object2 = object1
object3 = MyObject.new
 
puts "Object 1 is == to object 2: #{object1 == object2}" #its true
puts "Object 1 is eql? to object 2: #{object1.eql? object2}" #its true
puts "Object 1 is equal? to object 2: #{object1.equal? object2}" #its true
puts "Object 1 is == to object 3: #{object1 == object3}"  #its false
puts "Object 1 is eql? to object 3: #{object1.eql? object3}" #its false
puts "Object 1 is equal? to object 3: #{object1.equal? object3}" #its false
 
Usually with Ruby we will tend to leave the equal? method alone
to always give us the ability to find out if two objects are
exactly the same. The eql? and == methods however are open to
be redefined in any way we like. You can redefine the == method
to give your objects custom behavior when it comes to equality testing.

 
class Shirt
  attr_reader :size
  def initialize size
    @size = size
  end
 
  def ==(another_shirt)
    self.size == another_array.size
  end
end
 
More than that, by defining the == method on your object, 
you also get the != method for free.
 
shirt1 = Shirt.new(10)
shirt2 = Shirt.new(11)
shirt3 = Shirt.new(10)
 
puts "Are shirt1 and shirt2 equal? #{shirt1 == shirt2}" #its false
puts "Are shirt1 and shirt3 equal? #{shirt1 == shirt3}" #its true
puts "Are shirt1 and shirt2 NOT equal? #{shirt1 != shirt2}" #its true
 
 
There is only one case that I see where the eql? method 
might come in handy. If you have to defined the <=> method, 
therefore giving your object an implicit == method, it may no longer 
make sense to redefine == separately. But if for some reason you need to 
keep the <=> equality behavior but still need even more custom 
equality behavior, you can always use the eql? method for this 
purpose. I can't really envisage a situation where you might want to do 
this, but the capability is certainly there.