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.

Reply via email to