The fact that TypeOfClass === TypeOfClass
is false
strikes me as counter-intuitive. In the following code, even if field.class
is the same class, it evaluates to false
:
case field.class
when Fixnum, Float
field + other_field
when Date, DateTime, Time
field
else
puts 'WAT?'
end
I did this:
Fixnum === Fixnum # => false
Class === Class # => true
I found another thread:
Integer === 3 # => true
Fixnum === 3 # => true
3.class # => Fixnum
I fail to find a reason for this language design. What were the language designers thinking when they baked in this behavior?
I think this is relevant to the answer provided in another thread. It is not unnatural to assume that Numeric === Integer
since an Integer
is a more specific type of Numeric
. But, it isn't:
Numeric === Integer #=> false
I think case
statements or ===
requires caution. If this operator is what we think it is , then, a Numeric
should be a Numeric
, an Integer
should be a Numeric
, etc.
Does anyone have an explanation of why this feature doesn't extend to classes? It seems like it would be easy enough to return true
if the compared class is a member of the class' ancestors.
Based on an answer submitted below, the code was originally classifying Time
(as extended by ActiveSupport:CoreExtensions::Integer::Time
and ActiveSupport:CoreExtensions::Float::Time
):
Timeframe = Struct.new(:from, :to) do
def end_date
case self.to
when Fixnum, Float
self.from + self.to
when Date, DateTime, Time
self.to
else
raise('InvalidType')
end
end
end
and in the console, I get:
tf = Timeframe.new(Time.now, 5.months)
# => #<struct Timeframe from=Tue Dec 10 11:34:34 -0500 2013, to=5 months>
tf.end_date
# => RuntimeError: InvalidType
# from timeframe.rb:89:in `end_date'
That's Module#===
and its intended behavior:
mod === obj → true or false
Case Equality—Returns
true
if anObject is an instance of mod or one of mod’s descendants. Of limited use for modules, but can be used incase
statements to classify objects by class.
It simply returns obj.kind_of? mod
:
Fixnum === Fixnum #=> false
Fixnum.kind_of? Fixnum #=> false
Class === Class #=> true
Class.kind_of? Class #=> true
String === "foo" #=> true
"foo".kind_of? String #=> true
3
is both, an Integer
and a Fixnum
because of its class hierarchy:
3.kind_of? Integer #=> true
3.kind_of? Fixnum #=> true
3.class.ancestors #=> [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject]
Numeric
is not an Integer
, it's a Class
:
Numeric.kind_of? Integer #=> false
Numeric.kind_of? Class #=> true
But 3
, (2/3)
and 1.23
are all Numeric
:
3.kind_of? Numeric #=> true
Rational(2, 3).kind_of? Numeric #=> true
1.23.kind_of? Numeric #=> true
Bottom line: for case
statements, just use case obj
instead of case obj.class
.
Update
You are getting this error because 5.months
doesn't return an Integer
, but a ActiveSupport::Duration
:
Integer === 5.months #=> false
ActiveSupport::Duration === 5.months #=> true
Calling your method with 5.months.to_i
or adding ActiveSupport::Duration
to your classes should fix it.