Problem: use case stmt on object's class in simple test

R

RichardOnRails

Hi,

The following is a toy abstract from an app I'm writing.

hash = {:Name=>"Tom"}
hash.each_key { |key|
puts "key %s (class = %s) => %s (class = %s)" %
[key.inspect, key.class, hash[key], hash[key].class]
case hash[key].class
when String; p has[key]
else; puts "hash[key].class not found"
end
}


The output is:
key :Name (class = Symbol) => Tom (class = String)
hash[key].class not found

Why isn't the "when String" statement executed?

Thanks in Advance,
Richard

P.S. I wonder if the time will come when I won't regularly stumble
over apparent anomalies when writing Ruby code.
 
A

Aaron D. Gifford

user@host:/path$ irb
irb(main):001:0> a =3D "Foo"
=3D> "Foo"
irb(main):002:0> a.class =3D=3D=3D String
=3D> false
irb(main):003:0> a =3D=3D=3D String
=3D> false
irb(main):004:0> a.class =3D=3D=3D String.class
=3D> false
irb(main):005:0> a
=3D> "Foo"
irb(main):006:0> a.inspect
=3D> "\"Foo\""
irb(main):007:0> String.inspect
=3D> "String"
irb(main):008:0> case a
irb(main):009:1> when String
irb(main):010:1> puts 'string'
irb(main):011:1> else
irb(main):012:1* puts 'not'
irb(main):013:1> end
string
=3D> nil
irb(main):014:0> String =3D=3D=3D a
=3D> true
irb(main):015:0> a =3D=3D=3D String
=3D> false
irb(main):016:0> root@secure:/usr/ports# irb
irb(main):001:0> str =3D "a string"
=3D> "a string"
irb(main):002:0> str.class
=3D> String
irb(main):003:0> String =3D=3D=3D str.class
=3D> false
irb(main):004:0> String =3D=3D=3D str
=3D> true
irb(main):005:0> case str.class
irb(main):006:1> when String
irb(main):007:1> puts 'it is a string'
irb(main):008:1> else
irb(main):009:1* puts 'else executed'
irb(main):010:1> end
else executed
=3D> nil
irb(main):011:0> case str
irb(main):012:1> when String
irb(main):013:1> puts 'it is a string'
irb(main):014:1> else
irb(main):015:1* puts 'else executed'
irb(main):016:1> end
it is a string
=3D> nil
irb(main):017:0>
=A0Why isn't the "when String" statement executed?

I believe case/when uses the when object's =3D=3D=3D method for matching,
and as you can see from the above irb session, String =3D=3D=3D "a
string".class evaluates to false. Hence your code doesn't work like
you expected.

However, if you change to hash[key] instead of hash[key].class, a
direct comparison to the class object should work (note the String =3D=3D=
=3D
"a string" evaluates to true).

Note that "a string" =3D=3D=3D String evaluates to false even though "a
string" =3D=3D=3D String evaluates to true. That way case String when "a
string" won't match but case "a string" when String will. (Which
totally makes sense in the context of case/when.)

I'm sure I read a great blog post or discussion on this very thing in
the past on the web, one that explains the gory details about exactly
why case/when was designed to work this way. It currently slips my
mind.

Anyone got a good URL for an article/post like that?

Aaron out.
 
R

RichardOnRails

user@host:/path$ irb
irb(main):001:0> a = "Foo"
=> "Foo"
irb(main):002:0> a.class === String
=> false
irb(main):003:0> a === String
=> false
irb(main):004:0> a.class === String.class
=> false
irb(main):005:0> a
=> "Foo"
irb(main):006:0> a.inspect
=> "\"Foo\""
irb(main):007:0> String.inspect
=> "String"
irb(main):008:0> case a
irb(main):009:1> when String
irb(main):010:1> puts 'string'
irb(main):011:1> else
irb(main):012:1* puts 'not'
irb(main):013:1> end
string
=> nil
irb(main):014:0> String === a
=> true
irb(main):015:0> a === String
=> false
irb(main):016:0> root@secure:/usr/ports# irb
irb(main):001:0> str = "a string"
=> "a string"
irb(main):002:0> str.class
=> String
irb(main):003:0> String === str.class
=> false
irb(main):004:0> String === str
=> true
irb(main):005:0> case str.class
irb(main):006:1> when String
irb(main):007:1>   puts 'it is a string'
irb(main):008:1> else
irb(main):009:1*   puts 'else executed'
irb(main):010:1> end
else executed
=> nil
irb(main):011:0> case str
irb(main):012:1> when String
irb(main):013:1>   puts 'it is a string'
irb(main):014:1> else
irb(main):015:1*   puts 'else executed'
irb(main):016:1> end
it is a string
=> nil
irb(main):017:0>
 Why isn't the "when String" statement executed?

I believe case/when uses the when object's === method for matching,
and as you can see from the above irb session, String === "a
string".class evaluates to false.  Hence your code doesn't work like
you expected.

However, if you change to hash[key] instead of hash[key].class, a
direct comparison to the class object should work (note the String ===
"a string" evaluates to true).

Note that "a string" === String evaluates to false even though "a
string" === String evaluates to true.  That way case String when "a
string" won't match but case "a string" when String will.  (Which
totally makes sense in the context of case/when.)

I'm sure I read a great blog post or discussion on this very thing in
the past on the web, one that explains the gory details about exactly
why case/when was designed to work this way.  It currently slips my
mind.

Anyone got a good URL for an article/post like that?

Aaron out.

Hey Aaron,

You scored a triple. I should say you scored 3 homers. In order of
importance on my grading scale:

1. You solved my problem: Lose the ".class"

2. You showed me that using IRB for experimenting like this is far
better than littering one's code with debugging statements.

3. You reminded me to RTFM! Actually, I rotate between books, PDFs
and blogs on Ruby, Rails, CSS, RSpec, etc. all the time. But as I
encountered this unfathomable situation, I didn't do what I've now
done: Consult PickAxe (2nd ed., 2005), which confirmed your belief
that case uses === for a comparison of the case parameter and when
arguments.

Many thanks for your deep and insightful solution to my question.

Best wishes,
Richard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top