Lets say I create an object a and give it a method .to_i, why can't this object be added to an Integer?
>> a = Object.new
=> #<Object:0x0000000006cfa9d0>
?> def a.to_int
?> 42
>> end
=> :to_int
>> 3 a
(irb):5:in ` ': Object can't be coerced into Integer (TypeError)
from (irb):5:in `<main>'
CodePudding user response:
You still need to call the to_int
method how else does the interpreter know what you want to do?
>> 3 a.to_int
Ruby doesn't do automatic conversion.
>> 3 "5"
This will give the same error, even though "5" has a perfectly fine to_i method. Btw. to int methods in ruby are ususally called to_i
in case you want to keep the consistency.
CodePudding user response:
Adding an integer to an object can be achieved by implementing
, e.g.:
class Foo
def initialize(value)
@value = value
end
def to_i
@value
end
def (other)
Foo.new(to_i other.to_i)
end
end
Foo.new(5) 4
#=> #<Foo:0x00007fbd22050640 @value=9>
In order to add an instance of Foo
to an integer, you have to also implement coerce
which takes the left-hand value as an argument and returns an array with both values converted to Foo
instances, e.g.:
class Foo
# ...
def coerce(other)
[Foo.new(other.to_i), self]
end
end
This gives you:
4 Foo.new(5)
#=> #<Foo:0x00007fba600e3e28 @value=9>
The docs for Numeric
contain another example.
Internally, coerce
is called by Integer#
if the argument is not an integer: (C code)
VALUE
rb_int_plus(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
return fix_plus(x, y);
}
else if (RB_TYPE_P(x, T_BIGNUM)) {
return rb_big_plus(x, y);
}
return rb_num_coerce_bin(x, y, ' ');
}
rb_num_coerce_bin
calls coerce
and then invokes the binary operator
on the returned values.
In Ruby this would be: (simplified)
class Integer
def (other)
if other.is_a?(Integer)
# ...
else
x, y = other.coerce(self)
x y
end
end
end