when does 'dog' not equal 'dog'?

This is more a Ruby than a Rails question -- feel free to redirect me as appropriate -- but either this is a Ruby 1.9 bug or I have some serious misunderstanding. Essentially, I've caught the <=> operator returning nil -- not -1, 0, or 1, but nil. Schematically:

class A   attr_accessor :slot   fun matches?(s)     p "s <=> @slot = #{s <=> @slot}"     return (s <=> @slot) == 0   end end

Driven from a script, I get something like this:

# Case 1: Notice that <=> evaluates to nil when it should eval to 0 a.slot = "dog" a.matches?("dog") "s <=> @slot = " => false

# Case 2: When I pass a String.new("dog") everything works a.slot = "dog" a.matches?(String.new("dog")) "s <=> @slot = 0" => true

What's really odd is that called interactively, Case 1 works as expected. When called from a script, it exhibits the oddity above.

Am I missing something fundamental about Ruby and string comparison?

- ff

You’ll want to hit the ruby-talk list with this question.

fearless_fool wrote:

This is more a Ruby than a Rails question -- feel free to redirect me as appropriate -- but either this is a Ruby 1.9 bug or I have some serious misunderstanding. Essentially, I've caught the <=> operator returning nil -- not -1, 0, or 1, but nil. Schematically:

class A   attr_accessor :slot   fun matches?(s)     p "s <=> @slot = #{s <=> @slot}"     return (s <=> @slot) == 0   end end

Driven from a script, I get something like this:

# Case 1: Notice that <=> evaluates to nil when it should eval to 0 a.slot = "dog" a.matches?("dog") "s <=> @slot = " => false

# Case 2: When I pass a String.new("dog") everything works a.slot = "dog" a.matches?(String.new("dog")) "s <=> @slot = 0" => true

What's really odd is that called interactively, Case 1 works as expected. When called from a script, it exhibits the oddity above.

Am I missing something fundamental about Ruby and string comparison?

- ff

Can't reproduce with what you've given.

ruby-1.9.1-p376 > class A ruby-1.9.1-p376 ?> attr_accessor :slot ruby-1.9.1-p376 ?> def matches? s ruby-1.9.1-p376 ?> puts "s <=> @slot = #{s <=> @slot}" ruby-1.9.1-p376 ?> (s <=> @slot) == 0 ruby-1.9.1-p376 ?> end ruby-1.9.1-p376 ?> end => nil ruby-1.9.1-p376 > a = A.new => #<A:0x000000041915b8> ruby-1.9.1-p376 > a.slot = "dog" => "dog" ruby-1.9.1-p376 > a.matches? "dog" s <=> @slot = 0 => true ruby-1.9.1-p376 >

String comparison via <=> to nil will return nil. Are you entirely sure that in your IRB session your object's @slot isn't nil for some reason?

Do you need the <=> operator? (specifically, nothing you're doing seems to care whether the compared string is bigger or smaller, only different. So why not use != instead?

In fact, your matches? method could just as easily be:

def matches?(s)    s == @slot end

which will return true if they're equal, or nil if they're not. If it has to be true/false, then use !!(s == @slot)

Can't reproduce with what you've given.

ruby-1.9.1-p376 > class A ruby-1.9.1-p376 ?> attr_accessor :slot ruby-1.9.1-p376 ?> def matches? s ruby-1.9.1-p376 ?> puts "s <=> @slot = #{s <=> @slot}" ruby-1.9.1-p376 ?> (s <=> @slot) == 0 ruby-1.9.1-p376 ?> end ruby-1.9.1-p376 ?> end => nil ruby-1.9.1-p376 > a = A.new => #<A:0x000000041915b8> ruby-1.9.1-p376 > a.slot = "dog" => "dog" ruby-1.9.1-p376 > a.matches? "dog" s <=> @slot = 0 => true ruby-1.9.1-p376 >

String comparison via <=> to nil will return nil. Are you entirely sure that in your IRB session your object's @slot isn't nil for some reason?

First, yep, 100% sure that @slot wasn't nil -- I printed it out upon entering the matches? method. (FWIW, you have it reversed: irb session worked fine. Driven from a script failed.) Sadly, my attempts to prune down the example to a reproducible losing case have failed to fail, so to speak. As suggested by @Matt Brown, I'll take this over to the ruby-talk list.

Thanks.