Reputation: 187
Using case
/when
statements for checking an instance's class fails. If I compare an Answer
instance's class as follows, it fails:
subject = Answer.new
case subject.class
when Answer
true
else
false
end
# => false
If I convert an Answer
instance's class to string, it works:
subject = Answer.new
case subject.class.to_s
when 'Answer'
true
else
false
end
# => true
This isn't how it's supposed to be, right? Additionally, when I run a simple comparison, it works:
subject.class == Answer
# => true
Edit: Using the newly-learned properties of Ruby's case
and ===
, this code is valid and works.
subject = Answer.new
case subject
when Answer
true
else
false
end
# => true
Upvotes: 2
Views: 106
Reputation: 20033
Answer
returns the generic class definition.
Answer.new.class
returns a hash of an actual instance.
> Answer
=> Answer(id: integer, created_at: datetime, updated_at: datetime)
> Answer.new
=> #<Answer id: nil, created_at: nil, updated_at: nil>
Notice the #
and the different formatting.
As lurker kindly pointed out in his answer, case
uses the ===
operator, not ==
.
Using this information, you can rewrite your case statement like this
> subject = Answer.new
> case subject
?> when Answer
?> true
?> else
?> false
?> end
=> true
The above case
translates to "does subject
belong to Answer
?". And the answer is yes, because subject
is an instance of Answer
.
Upvotes: 1
Reputation: 58294
The case
in Ruby is based upon the ===
operator. When you have:
case x
when foo
...
when bar
...
end
It's behavior is:
if foo === x
...
elsif bar === x
...
end
In a general sense, a === b
means that b
belongs to or is subsumed by what a
represents. See the definition given here:
Case Equality—Returns true if obj is an instance of mod or one of mod’s descendants. Of limited use for modules, but can be used in case statements to classify objects by class.
For example, (2..7) === 3
is true because 3 is contained in the range, 2..7
. Based upon how objects define ===
, it is also true that 2 === 2
Since 2
belongs to the class of 2
. It's not a commutative operator (for example, a === b
true doesn't mean b === a
is true). An expression such as Integer === 3
is true
because a 3
is an Integer
. But it is not true that 3 === Integer
because 3
doesn't represent or subsume the entire class of Integer
. So the ===
operator makes the Ruby case
statement do some powerful and useful matching.
In this particular case
statement, you're checking if Answer === subject.class
is true. The operator ===
is not defined on a class object in such a way as to make this true
because subject.class
does not belong to the class of Answer
. However, s.class == Answer
is true
because s.class
is indeed Answer
. So the case doesn't match. However, Answer === s
is true because s
is a type of Answer
.
Upvotes: 1