require 'eymiha/units/units_system' require 'eymiha/util/methodic_hash' module Eymiha # A UnitsMeasure groups sets of units that measure the same quality. class UnitsMeasure < MethodicHash # A Hash of UnitsMeasures to powers that represent the bases for this # UnitsMeasure if it is derived. attr_reader :derived attr_writer :derived # :nodoc: # A Methodic Hash mapping names of formats to procs that implement them. attr_reader :formats def initialize # :nodoc: @formats = MethodicHash.new end # Defines or extends a UnitsSystem. During definition, if forward # references between entities may occur and when encountered, are stored. # Upon completion of the definition, if any existing unresolved still # exist, an attempt is made to resolve them. def system(name,&block) Units.defining self system = (self[name] ||= UnitsSystem.new(self,name)) block.call system if block_given? Units.resolve_forward_references Units.defining nil system end # Returns the names by which this UnitsMeasure is known. def names Units.names_of self end # Returns a new UnitsMeasure equal to the instance raised to the given # power. This is typically used to derive UnitsMeasures - for example, # if length is a UnitsMeasure, then length**3 could be used as the target # to derive volume. def **(exponent) UnitsMeasure.new.merge_derivation(self => exponent) end # Returns a new UnitsMeasure equal to the instance divided by another # UnitsMeasure. This is typically used to derive UnitsMeasures - for # example, if mass and volume are UnitsMeasures, then mass/volume could be # used as the target to derive density. def /(divisor) UnitsMeasure.new.merge_derivation(self).merge_derivation(divisor,-1) end # Returns a new UnitsMeasure equal to the instance multiplied by another # UnitsMeasure. This is typically used to derive UnitsMeasures - for # example, if length is a UnitsMeasure, then length*length could be # used as the target to derive area. def *(factor) UnitsMeasure.new.merge_derivation(self).merge_derivation(factor) end def merge_derivation derivation, multiplier=1 # :nodoc: current = (self.derived ||= {}) if derivation.kind_of? UnitsMeasure if derivation.derived derivation.derived.each {|key,value| current[key] = (current[key] || 0) + multiplier*value } else current[derivation] = (current[derivation] || 0) + multiplier end elsif derivation.kind_of? Hash derivation.each {|key,value| current[key] = ((current[key] || 0 ) + multiplier*value) } else raise UnitsException, "Cannot add #{derivation.class_name} to derivation" end self end # Returns a String containing the names of this UnitsMeasure and the # namse of the UnitsSystems defined within it. def to_s "#{class_name} #{names} #{keys}" end # Associates the name of a format with an output formatter. def format(options) @formats[options[:name]] = options[:format] end end end