173

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?

3
  • 2
    String is a class. The class of a class is Class. Commented Sep 30, 2015 at 8:35
  • 2
    Note that MyClass === obj uses the Module#=== method to check if obj is an instance of MyClass. Commented May 6, 2020 at 6:22
  • None of the answers make sense to me. From my experiments, it just looks like using class objects in a when/case structure actually compare to the value's class rather than the value itself. Commented Nov 20, 2023 at 15:14

5 Answers 5

288

You must use:

case item when MyClass ... 

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

Sign up to request clarification or add additional context in comments.

3 Comments

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).
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. github.com/rails/rails/blob/…
Yes, this is not working with ActiveModel either, a pretty big gotcha!
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

2 Comments

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?
=== 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 ===.
27

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.

1 Comment

"5 is an Integer, but is Integer a 5" is a wonderfully human explanation, thank you.
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.

1 Comment

Ah! This makes sense now that I've learned more about Ruby metaprogramming.
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.

1 Comment

In Ruby, everything is "an instance of an object".

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.