Yes! Totally!
I propose we have a new class, Angle, which stores a number internally
in whatever is a convenient measurement — I propose 0.0 .. 1.0 — and
works like this totally untested code:
class Angle
def initialize(numeric)
@finished = false;
@numeric = numeric.to_f
end
attr_accessor :numeric
def finished?; @finished; end
# relative stuff
def relative= booly
@relative = !!booly
end
def relative!; @relative = true; end
def relative?; @realtive; end
# absolute stuff
def absolute= booly
@relative = !booly
end
def absolute!; @relative = false; end
def absolute?; [EMAIL PROTECTED]; end
# direction stuff
def clockwise!
relative!
@numeric = - @numeric if @numeric < 0
end
def anticlockwise!
relative!
@numeric = - @numeric if @numeric > 0
end
# measurement stuff
def degrees!
@numeric /= 360
@finished = true;
end
def radians!
@numeric = - ( @numeric * ( Math.PI * 2 ) )
@finished = true;
end
# to_thingys
def to_degrees
raise UnfinishedError.new unless finished?
@numeric * 360
end
def to_radians
raise UnfinishedError.new unless finished?
( 0 - @numeric ) * ( Math.PI * 2 )
end
end
class Angle::UnfinishedError < StandardError
def initialize
super('need to call .degrees or .radians (or their ! versions)
to finish this Angle first')
end
end
# mmmm, magical.
Angle.public_methods(false).each do |meth|
case meth
when /!$/
Angle.define_method(meth.sub(/!/, '')) do
ang = self.dup
ang.send meth
ang
end
when /=|?$/
next
else
Numeric.define_method(meth) do |*args|
angle = Angle.new(self)
angle.send(meth, *args) or angle
end
end
then when you want to accept an Angle, you can just be all ducktypey
and take that arg and arg.to_degrees that thing or whatever.
Again I want to emphasize that I haven't tested this code at all, but
you get the idea! :)
Something I forgot to add which would be good would be in that last
magical method createy part, to also alias all the methods ending in
's' methods to ones without an s, allowing for stuff like 1.degree and
all that.. would get complex, would need to be in a second loop before
the existing public_methods loop to make sure they're copied to the
Numeric class.