168

What is the difference between

case item.class
when MyClass
  # do something here
when Array
  # do something different here
when String
  # do a third thing
end

and

case item.class
when MyClass.class
  # do something here
when Array.class
  # do something different here
when String.class
  # do a third thing
end

For some reason, the first one of these works sometimes and the second doesn't, and other times, the second one works and the first one doesn't. Why? Which one is the "proper" way to do it?

Daisy Sophia Hollman
  • 6,046
  • 6
  • 24
  • 35
  • 2
    String is a class. The class of a class is Class. – Volte Sep 30 '15 at 08:35
  • 1
    Note that `MyClass === obj` uses the [Module#===](https://ruby-doc.org/core-2.5.0/Module.html#method-i-3D-3D-3D) method to check if `obj` is an instance of `MyClass`. – builder-7000 May 06 '20 at 06:22

5 Answers5

281

You must use:

case item
when MyClass
...

I had the same problem: How to catch Errno::ECONNRESET class in "case when"?

Rambatino
  • 4,716
  • 1
  • 33
  • 56
Nakilon
  • 34,866
  • 14
  • 107
  • 142
  • 1
    Thanks! Sorry to dupe (or sort of dupe), but several searches didn't turn up that previous question. It seems that the use of === by the case statement is quite a common problem, now that I see this is the problem. This should probably be pointed out more often in tutorials and such (but I bet that many tutorial writers aren't aware of this either). – Daisy Sophia Hollman Oct 13 '10 at 13:28
  • 7
    A caveat that hasn't been mentioned if using ActiveRecord. The ActiveRecord === method on class comparisons uses .is_a?, which means that subclasses of a class will evaluate to true in the case statement. https://github.com/rails/rails/blob/04cda1848cb847c2bdad0bfc12160dc8d5547775/activerecord/lib/active_record/core.rb#L135 – Jeremy Baker Feb 07 '14 at 23:48
  • Yes, this is not working with ActiveModel either, a pretty big gotcha! – Hackeron Dec 08 '22 at 17:13
78

Yeah, Nakilon is correct, you must know how the threequal === operator works on the object given in the when clause. In Ruby

case item
when MyClass
...
when Array
...
when String
...

is really

if MyClass === item
...
elsif Array === item
...
elsif String === item
...

Understand that case is calling a threequal method (MyClass.===(item) for example), and that method can be defined to do whatever you want, and then you can use the case statement with precisionw

Fred
  • 8,582
  • 1
  • 21
  • 27
  • 1
    If I have `arr = []` then I noticed that `if Array === arr` will evaluate to true but `if arr === Array` will evaluate to false. Can someone please help explain? – DDD Mar 21 '13 at 10:30
  • 6
    === is just a method that can be defined to do whatever the designer of a class wants it to do. Remember, also, that a === b really means a.=== b, so if you switch a and b around, you can get different behavior. There is no guarantee that === is commutative. In fact, Array === Array is false, but Object === Object is true, so Array is redefining the semantics of ===. – Fred Mar 21 '13 at 22:58
24

You can use:

case item.class.to_s
    when 'MyClass'

...when the following twist is not possible:

case item
    when MyClass

The reason for this is that case uses ===, and the relationship the === operator describes is not commutative. For example, 5 is an Integer, but is Integer a 5? This is how you should think of case/when.

Community
  • 1
  • 1
user664833
  • 18,397
  • 19
  • 91
  • 140
  • 2
    "5 is an Integer, but is Integer a 5" is a wonderfully human explanation, thank you. – Matt Jun 16 '21 at 21:09
7

In Ruby, a class name is a constant that refers to an object of type Class that describes a particular class. That means that saying MyClass in Ruby is equivalent to saying MyClass.class in Java.

obj.class is an object of type Class describing the class of obj. If obj.class is MyClass, then obj was created using MyClass.new (roughly speaking). MyClass is an object of type Class that describes any object created using MyClass.new.

MyClass.class is the class of the MyClass object (it's the class of the object of type Class that describes any object created using MyClass.new). In other words, MyClass.class == Class.

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
1

It depends on the nature of your item variable. If it is an instance of an object, e.g.

t = 5

then

t.class == Fixnum

but if it is a class in itself e.g

t = Array

then it will be a Class object, so

t.class == Class

EDIT: please refer to How to catch Errno::ECONNRESET class in "case when"? as stated by Nakilon since my answer could be wrong.

Community
  • 1
  • 1
Jack
  • 131,802
  • 30
  • 241
  • 343