I have an Angle class that I want to behave like a float, with additional behavior. I have created the class to contain a float and proxy all unknown methods to it:
class Angle
include Math
def initialize(angle=0.0)
@angle = Float(angle)
# Normalize the angle
@angle = @angle.modulo(PI*2)
end
def to_f
@angle.to_f
end
# Other functionality...
def method_missing(message, *args, &block)
if block_given?
@angle.public_send(message, *args, &block)
else
@angle.public_send(message, *args)
end
end
end
It works fine. However when I try to use it with trig operations, e.g. Math.cos, I get:
> a = Angle.new(0.0)
=> #<Angle:0x00000000cdb220 @angle=0.0>
@angle=0.0
> Math.cos(a)
TypeError: can't convert Angle into Float
I know I can use Float(a) to convert to a float, but it’s inconvenient since I want this class to behave like a float. Is there a way to automatically convert Angle to float in these cases?
Looking at the implementation of Math.cos, you can see it calls a macro called Need_Float, which then calls a function rb_to_float. Line 2441 of rb_to_float checks to see if the object passed in is of type Numeric. So it seems the only way to have your own class act as a float in the Math family of functions is to have it inherit from Numeric or a descendant of Numeric. Thus, this modification of your code works as expected:
I’m not sure what side effects inheriting from Numeric will have, but unfortunately this looks like the only way to have your code work the way you want it to.