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
-----------------------

But that is a *terrible* hack I hate. Any better suggestion?

Thanks a lot.

--
Iñaki Baz Castillo
<ibc@aliax.net>

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 Rex

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

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 White
http://www.twitter.com/cwgem

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

···

On Aug 14, 2011, at 08:47 , Iñaki Baz Castillo wrote:

klass = String

case klass
when String
   puts "I'm String class"
else
   puts "I'm nothing..."
end

"Iñaki Baz Castillo" <ibc@aliax.net> 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

···

--
Posted via http://www.ruby-forum.com/\.

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

Thanks.

···

2011/8/14 Bartosz Dziewoński <matma.rex@gmail.com>:

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ñaki Baz Castillo
<ibc@aliax.net>

Really great, thanks a lot.

···

2011/8/14 Chris White <cwprogram@live.com>:

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.

--
Iñaki Baz Castillo
<ibc@aliax.net>

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

···

On Aug 14, 2011, at 09:26 , Chris White wrote:

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

The most ellegant way. Thanks.

···

2011/8/15 Ryan Davis <ryand-ruby@zenspider.com>:

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

--
Iñaki Baz Castillo
<ibc@aliax.net>