How to use "case" to match class names? (=== not so funny)

Hi, easy example of what I need:


klass = String

case klass
when String
puts “I’m String class”
else
puts “I’m nothing…”
end

It produces “I’m nothing…”. I understand why:

“case” matches the given object using “===”, and:

String === String
=> false

while:

String === “a new string”
=> true

But in my case, klass variable holds a class rather than a class
instance. How could I use it within the above “case” statement? The
only way I’ve found is:


klass = String

case klass.name
when “String”
puts “I’m String class”
else
puts “I’m nothing…”
end

I don’t think there’s a better solution than what you already have.
There are different ones, of course - good ol’ if…elsif…else chain,
a hash with classes being its keys ({String=>“I’m String class”,
Fixnum=>“I’m an integer”}[klass] || “I’m nothing”) - but I wouldn’t
say they are better.

Maybe something else could be redone? Where does that class come from?

– Matma R.

2011/8/14 Bartosz Dziewoński [email protected]:

I don’t think there’s a better solution than what you already have.
There are different ones, of course - good ol’ if…elsif…else chain,
a hash with classes being its keys ({String=>“I’m String class”,
Fixnum=>“I’m an integer”}[klass] || “I’m nothing”) - but I wouldn’t
say they are better.

Maybe something else could be redone? Where does that class come from?

I need “klass” to hold a class name, not an instance. That cannot be
changed.

Thanks.

2011/8/14 Chris W. [email protected]:

end
You could also modify it to accept a variable number of arguments to check
against multiple classes in a single when statement:
false
end

This form works with a single class as well.

Really great, thanks a lot.

end

It produces “I’m nothing…”. I understand why:

“case” matches the given object using “===”, and:

String === String
=> false

Correct, because triple equals checks to see if the value is an instance
or subclass of a class, not if it’s the actual class itself

while:

String === “a new string”
=> true

Which is why this works. One way to potentially get around this is to
have an object that simple holds a class and overrides the === operator
to make case work properly:

class ClassCheck
def initialize(klass)
@holder = klass
end

def ===(other)
@holder == other
end
end

klass = String

case klass
when ClassCheck.new(String)
puts “String”
else
puts “Nothing”
end

You could also modify it to accept a variable number of arguments to
check against multiple classes in a single when statement:

class ClassCheck
def initialize(*klass)
@holder = klass
end

def ===(other)
@holder.each { | klass |
return true if klass == other
}
false
end
end

klass = String

case klass
when ClassCheck.new(Object, Numeric, String)
puts “One of these”
else
puts “Nothing”
end

This form works with a single class as well.

Regards,
Chris W.
http://www.twitter.com/cwgem

On Aug 14, 2011, at 09:26 , Chris W. wrote:

def ===(other)
@holder.each { | klass |
return true if klass == other
}
false
end

def === o
@holder.any? { |k| k == o }
end

On Aug 14, 2011, at 08:47 , Iaki Baz C. wrote:

klass = String

case klass
when String
puts “I’m String class”
else
puts “I’m nothing…”
end

case
when String == klass then

else

end

or to do a much cleaner version of the multi-match example above:

case
when [String, Whatever].include? klass then

else

end

2011/8/15 Ryan D. [email protected]:

case
when [String, Whatever].include? klass then

else

end

The most ellegant way. Thanks.

“Iñaki Baz C.” [email protected] wrote in post #1016614:

But in my case, klass variable holds a class rather than a class
instance. How could I use it within the above “case” statement? The
only way I’ve found is:


klass = String

case klass.name
when “String”
puts “I’m String class”
else
puts “I’m nothing…”
end

As you’ve found, Class#=== tells you if an object in an instance of a
class
(like is_a?), and this is how it’s intended to be used.

You could in some cases make an instance (as long as the initializer
doesn’t need any arguments):

case klass.new
when String
puts “I’m a String”
end

But case is really intended for ‘matches’ tests. If you want ‘equal to’,
then maybe you’re better off with a simple Hash.

KLASS_LOOKUP = {
String => lambda { puts “I’m a String” },
}
KLASS_LOOKUP.default = lambda { puts “Something else” }

klass = String
KLASS_LOOKUP[klass].call