Weird elusive bug using Comparable

I have this class:

class SimpleDate
include Comparable

VALS = [:year, :month, :day]
attr_accessor *VALS

def initialize(year, month, day)
@year = year
@month = month
@day = day
end

#ideas on making this much shorter are welcome
def <=>(other)
VALS.each do |key|
v1 = self.send(key)
v2 = other.send(key)

  result = (v1 <=> v2)
  if result != 0
    return result
  end
end
0

end
end

that represents a date that might have any of the three parts not
existant. For some reason date1 == date2 returns nil when done like
this:

class SimpleDateTest < Test::Unit::TestCase
def test_comparison_with_nils
date1 = SimpleDate.new(1,nil,nil)
date2 = SimpleDate.new(1,nil,nil)

assert_not_equal date1.object_id, date2.object_id
assert date1 == date2

end
end

The code shouldn’t even work because nil doesn’t implement <=>. What
the hell is comparable doing behind my back?

I thought date1 == date2 was the same as (date1 <=> date2> == 0 but
apparently not.

Pedro.

Pedro Côrte-Real wrote:

end
0
end
end

How about something like this (untested):

def <=>(other)
VALS.map{ |key| self.send(key) } <=> VALS.map{ |key| other.send(key)
}
end

Pedro Côrte-Real wrote:

@month = month
if result != 0
return result
end
end
0
end
end

shorter

SimpleDate = Struct.new :year, :month, :day do
include Comparable

def <=> (other)
self.class.members.each do |field|
result = send(field) <=> other.send(field)
return result unless result == 0
end
0
end
end

assert date1 == date2
end
end

The code shouldn’t even work because nil doesn’t implement <=>. What
the hell is comparable doing behind my back?

I thought date1 == date2 was the same as (date1 <=> date2> == 0 but
apparently not.

No, it’s differently implemented.

Kind regards

robert

On 7/31/06, Robert K. [email protected] wrote:

I thought date1 == date2 was the same as (date1 <=> date2> == 0 but
apparently not.

No, it’s differently implemented.

How then? According to the docs if you implement <=> and include
Comparable you get ==. But my second assertion is failing when it
should be blowing up when trying to do nil <=> nil.

Pedro.

“P” == =?ISO-8859-1?Q?Pedro C=F4rte-Real?= writes:

P> The code shouldn’t even work because nil doesn’t implement <=>. What
P> the hell is comparable doing behind my back?

Try it with 1.9

Guy Decoux

On 7/31/06, Robin S. [email protected] wrote:

How about something like this (untested):

def <=>(other)
VALS.map{ |key| self.send(key) } <=> VALS.map{ |key| other.send(key) }
end

Good idea. I’m gonna try it.

Pedro.

On 7/31/06, Pedro Côrte-Real [email protected] wrote:

On 7/31/06, Robin S. [email protected] wrote:

How about something like this (untested):

def <=>(other)
VALS.map{ |key| self.send(key) } <=> VALS.map{ |key| other.send(key) }
end

I tried it like this:

def <=>(other)
p ‘At the start’
result = VALS.map{|key| self.send(key)} <=> VALS.map{|key|
other.send(key)}
p ‘At the end’
result
end

And the strange thing is that ‘At the end’ isn’t printed but ‘At the
start’ is. How can this be?

Pedro.

On 7/31/06, ts [email protected] wrote:

“P” == =?ISO-8859-1?Q?Pedro C=F4rte-Real?= writes:

P> The code shouldn’t even work because nil doesn’t implement <=>. What
P> the hell is comparable doing behind my back?

Try it with 1.9

I don’t have one around. Has this changed? I’d like to make this work
now. I thought implementing <=> and including Comparable gave me ==
but something else seems to be going on.

Pedro.

On 7/31/06, ts [email protected] wrote:

moulon%

moulon% ruby -v -e ‘p NameError.ancestors’
ruby 1.9.0 (2006-07-14) [i686-linux]
[NameError, ScriptError, Exception, Object, Kernel, BasicObject]
moulon%

Ah, right, the exception is being caught. Here’s a working version then:

def <=>(other)
VALS.map{|key| self.send(key)||0} <=> VALS.map{|key|
other.send(key)||0}
end

Much cleaner than my first and this time it actually works.

Pedro.

“P” == =?ISO-8859-1?Q?Pedro C=F4rte-Real?= writes:

P> And the strange thing is that ‘At the end’ isn’t printed but ‘At the
P> start’ is. How can this be?

Comparable#== catch StandardError

moulon% /usr/bin/ruby -v -e ‘p NameError.ancestors’
ruby 1.8.4 (2005-12-24) [i486-linux]
[NameError, StandardError, Exception, Object, Kernel]
moulon%

moulon% ruby -v -e ‘p NameError.ancestors’
ruby 1.9.0 (2006-07-14) [i686-linux]
[NameError, ScriptError, Exception, Object, Kernel, BasicObject]
moulon%

Guy Decoux

Pedro Côrte-Real wrote:

On 7/31/06, Robert K. [email protected] wrote:

I thought date1 == date2 was the same as (date1 <=> date2> == 0 but
apparently not.

No, it’s differently implemented.

How then? According to the docs if you implement <=> and include
Comparable you get ==. But my second assertion is failing when it
should be blowing up when trying to do nil <=> nil.

You get == but if you implement it yourself your version will shadow the
one from Comparable:

class Foo
def <=>(o) 0 end
include Comparable
end
=> Foo
Foo.instance_method :==
=> #<UnboundMethod: Foo(Comparable)#==>

Now we add it to Foo:

class Foo
def ==(o) false end
end
=> nil
Foo.instance_method :==
=> #<UnboundMethod: Foo#==>
Foo.ancestors
=> [Foo, Comparable, Object, Kernel]

Kind regards

robert