# Title: # # Units # # Summary: # # SI Units system, integrated into Ruby's method call system. # # Copyright: # # Copyright (c) 2005 Peter Vanbroekhoven, Thomas Sawyer # # License: # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. # # Created: # # 2005.08.01 # # Authors: # # - Peter Vanbroekhoven # - Thomas Sawyer require 'rbconfig' require 'van/units/loaders' class Exception def clean_backtrace(regex) regex = /^#{::Regexp.escape(__FILE__)}:\d+:in `#{::Regexp.escape(regex)}'$/ if regex.is_a? ::String set_backtrace(backtrace.reject { |a| regex =~ a }) self end def self.with_clean_backtrace(regex) begin yield rescue ::Exception $!.clean_backtrace(regex).clean_backtrace("with_clean_backtrace") if not $DEBUG raise end end end module Van # The namespace for all unit related classes. Mixing this in has the additional effect # of making Units.with_unit_converter available without the Units. prefix, # as well as the shortcuts for creating Units (see Units#method_missing). # #-- # http://en.wikipedia.org/wiki/Celsius_scale # # degrees_Celsius vs Celsius_degrees # Kelvin # # Kelvin Celsius Fahrenheit Rankine Delisle Newton Réaumur Rømer # # K C F R D N R R # # http://en.wikipedia.org/wiki/Conversion_of_units #++ module Units def method_missing(m, *args, &blk) if args.length == 1 args[0] = (Units::Converter.converter(args[0]) rescue nil) if not args[0].is_a? Units::Converter return Units::Unit.new({m => 1}, args[0]) if args[0] && args[0].registered?(m) elsif (Units::Converter.current.registered?(m) rescue false) raise ::ArgumentError, "Wrong number of arguments" if args.length != 0 return Units::Unit.new({m => 1}, Units::Converter.current) end ::Exception.with_clean_backtrace("method_missing") { super } end def const_missing(c) if (Units::Converter.current.registered?(c) rescue false) return Units::Unit.new({c => 1}, Units::Converter.current) else ::Exception.with_clean_backtrace("const_missing") { super } end end def self.append_features(m) m.send(:extend, Units) super end # Executes the block with the current Converter changed to # the given Converter. This allows to temporarily change the # Converter used by default. A Converter object can be given, # or the name of a registered Converter. # # Example: # # with_unit_converter(:uk) { # puts 1.cwt.to(lb) # => 112.0 lb # } # # with_unit_converter(:us) { # puts 1.cwt.to(lb) # => 100.0 lb # } # # See also Converter.current. def with_unit_converter(name, &blk) # :yields: Units::Converter.with_converter(name, &blk) end module_function :with_unit_converter class Regexps NUMBER_REGEXP = /(\-?\d+((?:\.\d+)?(?:[eE][-+]?\d+)?))/ SINGLE_UNIT_REGEXP = /([a-zA-Z_]+)(?::([a-zA-Z_]+))?(?:\s*\*\*\s*([+-]?\d+))?/ SINGLE_UNIT_NOC_REGEXP = /[a-zA-Z_]+(?::[a-zA-Z_]+)?(?:\s*\*\*\s*[+-]?\d+)?/ # Violates DRY principle MULTIPLE_UNIT_REGEXP = /#{SINGLE_UNIT_NOC_REGEXP}(?:(?:\s+|\s*\*\s*)#{SINGLE_UNIT_NOC_REGEXP})*/ TOTAL_UNIT_REGEXP = /^\s*(?:1|(#{MULTIPLE_UNIT_REGEXP}))\s*(?:\/\s*(#{MULTIPLE_UNIT_REGEXP})\s*)?$/ TOTAL_UNIT_NOC_REGEXP = /\s*(?:1|(?:#{MULTIPLE_UNIT_REGEXP}))\s*(?:\/\s*(?:#{MULTIPLE_UNIT_REGEXP})\s*)?/ # Violates DRY principle VALUE_REGEXP = /^\s*#{NUMBER_REGEXP}\s*\*?\s*(#{TOTAL_UNIT_NOC_REGEXP})$/ end class BaseUnit attr_reader :name, :converter def initialize(name, converter = Units::Converter.current) name = name.to_sym raise ::ArgumentError, "unit #{name.to_s.dump} not registered with #{converter}" if not converter.registered? name @name = name @converter = converter end def conversion @converter.send(:conversions, @name) end def ==(other) other.is_a?(Units::BaseUnit) && other.name == @name && other.converter == @converter end alias eql? == def hash @name.hash ^ @converter.hash end def to_s if Units::Converter.current.includes?(converter) @name.to_s else "#{@converter}:#{@name}" end end alias inspect to_s end # This class represents a Unit. A Unit uses a given Converter with # a number of registered units in which it can be expressed. # A Unit is the product of the powers of other units. In principle, these # need not be integer powers, but this may cause problems with rounding. # The following code for example returns +false+: # Unit.new(:m => 0.1) * Unit.new(:m => 0.2) == Unit.new(:m => 0.3) # # Units can be multiplied, divided, and raised to a given power. # As an extra, 1 can be divided by a Unit. # # Examples: # # Unit.new(:mi => 1, :s => -1) ** 2 # => mi**2/s**2 # Unit.new(:mi => 1, :s => -1) * Unit.new(:s => 1, :usd => -1) # => mi/usd # Unit.new(:mi => 1, :s => -1, Converter.converter(:uk)) * # Unit.new(:s => 1, :usd => -1, Converter.converter(:us)) # => TypeError # 1 / Unit.new(:mi => 1, :s => -1) # => s/mi class Unit attr_reader :units # Creates a new (composite) Unit. # It is passed a hash of the form {:unit => exponent}, and the # Converter to use. # # Examples: # # Unit.new(:m => 1, :s => -1, Converter.converter(:uk)) # => m/s # Unit.new(:mi => 1, :s => -2) # => mi/s**2 # # See also Converter, Converter.converter def initialize(units = {}, converter = nil) conv = proc { converter ||= Units::Converter.current } if units.is_a? ::String @units = decode_string(units, conv) else @units = {} units.each_pair do |k, v| k = conv[].base_unit(k.to_sym) if not k.is_a? Units::BaseUnit @units[k] = v end end normalize_units end # Raises to the given power. def **(p) result = {} @units.each_pair do |u, e| result[u] = e * p end Units::Unit.new(result) end # Multiplies with the given Unit. def *(other) do_op(:*, :+, other) end # Divides by the given Unit. def /(other) do_op(:/, :-, other) end # Returns +true+ iff this Unit has all exponents equal to 0. def unitless? @units.empty? end def coerce(other) # :nodoc: return [Units::Unit.new({}), self] if other == 1 Units::Converter.coerce_units(self, other) end def simplify Units::Converter.simplify_unit(self) end # Returns +true+ iff the two units are equals, i.e., iff they have # the same exponent for all units, and they both use the same Converter. def ==(other) other.is_a?(Units::Unit) && other.units == units end alias eql? == def hash @units.to_a.map { |e| e.hash }.hash end # Returns +true+ iff this Unit is compatible with the given # Unit. This is less strict than equality because for example hours are # compatible with seconds ("we can add them"), but hours are not seconds. def compatible_with?(other) conv1, conv2 = Units::Converter.coerce_units(self, other) conv1.units == conv2.units end # Returns a human readable string representation. def to_s numerator = "" denominator = "" @units.each_pair do |u, e| e_abs = e > 0 ? e : -e # This works with Complex too (e > 0 ? numerator : denominator) << " #{u.to_s}#{"**#{e_abs}" if e_abs != 1}" end "#{numerator.lstrip}#{"/" + denominator.lstrip if not denominator.empty?}" end # Returns +self+. def to_unit(converter = nil) self end alias inspect to_s def method_missing(m, *args, &blk) if args.length == 1 args[0] = (Units::Converter.converter(args[0]) rescue nil) if not args[0].is_a? Units::Converter return self * Units::Unit.new({m => 1}, args[0]) if args[0] && args[0].registered?(m) elsif (Units::Converter.current.registered?(m) rescue false) raise ::ArgumentError, "Wrong number of arguments" if args.length != 0 return self * Units::Unit.new({m => 1}, Units::Converter.current) end ::Exception.with_clean_backtrace("method_missing") { super } end private def decode_string(s, converter) if Units::Regexps::TOTAL_UNIT_REGEXP =~ s numerator, denominator = $1, $2 units = {} decode_multiplicative_string(numerator, 1, converter, units) if numerator decode_multiplicative_string(denominator, -1, converter, units) if denominator units elsif /^\s*$/ =~ s {} else raise ::ArgumentError, "Illegal unit string #{s.dump}" end end def decode_multiplicative_string(s, multiplier, converter, result) s.scan(Units::Regexps::SINGLE_UNIT_REGEXP) do |conv, unit, exp| if unit conv = Units::Converter.converter(conv) else conv, unit = converter[], conv end exp ||= '1' unit = conv.base_unit(unit) result[unit] = (result[unit] || 0) + Integer(exp) * multiplier end end def normalize_units @units.keys.each do |unit| @units.delete(unit) if @units[unit] == 0 end end def do_op(op, dual_op, other) other = other.to_unit raise TypeError, "cannot convert to Unit" unless Units::Unit === other result = @units.dup other.units.each_pair do |u, e| result[u] = 0 if not result[u] result[u] = result[u].send(dual_op, e) end Units::Unit.new(result) end end # This class represents a Value with a numeric value and a Unit. # The numeric value can be any Numeric, though it is not recommended # to use Values. # # A Value can be added to, subtracted from and multiplied with another value, # though only when both Values are using the same Converter. While multiplication # is always possible, adding or subtracting values with incompatible units # results in a TypeError. When two units are compatible but not the same, # the Value with the larger of the units is converted to the smaller of the units. # For example adding 100 seconds and 1 minute, the latter is converted to 60 seconds # because a second is smaller than a minute. The result is 160 seconds. class Value < Numeric # The numeric value of this Value. attr_reader :value # The Unit of this value. attr_reader :unit include Comparable class << self alias old_new new private :old_new # Creates a new Value with the given numeric value and the given unit. # Simply returns the given value if the given unit is unitless, # i.e., when unit.unitless? is true. def new(value, *args) res = new!(value, *args) return res.value if res.unit.unitless? res end def new!(value, *args) if ::String === value str = *args converter = case args.length when 0 when 1 conv = args[0] else raise ArgumentError, "wrong number of arguments" end value, unit = decode_string(value, converter) else if args.length == 1 unit = args[0] else raise ArgumentError, "wrong number of arguments" end end unit = Unit.new(unit) unless unit.is_a?(Unit) old_new(value, unit) end end def initialize(value, unit) # :nodoc: @value, @unit = value, unit end %w{ * / }.each do |op| eval %{ def #{op}(other) Units::Value.new(*do_multiplicative_op(:#{op}, other)) end } end %w{ + - modulo remainder % }.each do |op| eval %{ def #{op}(other) Units::Value.new(*do_additive_op(:#{op}, other)) end } end def divmod(other) (q, r), unit = *do_additive_op(:divmod, other) [q, Units::Value.new(r, unit)] end def div(other) do_additive_op(:div, other)[0] end def -@ # :nodoc: Value.new(-@value, @unit) end def +@ # :nodoc: self end def **(other) # :nodoc: Units::Value.new(@value ** other, @unit ** other) end def <=>(other) # :nodoc: if other == 0 @value <=> 0 else (self - other).value <=> 0 end rescue TypeError nil end alias eql? == def hash @value.hash ^ @unit.hash end %w{ abs ceil floor next round succ truncate }.each do |op| eval %{ def #{op} Units::Value.new(@value.#{op}, @unit) end } end %w{ finite? infinite? integer? nan? nonzero? zero? }.each do |op| eval %{ def #{op} @value.#{op} end } end # Converts this Value to the given Unit. # This only works if the Converters used by this Value's Unit # and the given Unit are the same. It obviously fails if the # Units are not compatible (can't add apples and oranges). def to(to_unit, converter = nil) raise ArgumentError, "Wrong number of arguments" if converter && !(::String === to_unit) to_unit = to_unit.to_unit(converter) raise TypeError, "cannot convert to Unit" unless Units::Unit === to_unit conv1, conv2 = unit.coerce(to_unit) raise TypeError, "incompatible units for operation" if conv1.units != conv2.units mult = conv1.multiplier / conv2.multiplier Units::Value.new(value * mult, to_unit) end def coerce(other) # :nodoc: if ::Numeric === other [Units::Value.new!(other, Units::Unit.new), self] else super end end # Returns a human readable string representation. def to_s "#{@value} #{@unit}" end # Returns a float if this Value is unitless, and raises an # exception otherwise. def to_f val = simplify if Units::Value === val raise TypeError, "Cannot convert to float" else val.to_f end end # Returns an int if this Value is unitless, and raises an # exception otherwise. def to_i val = simplify if Units::Value === val raise TypeError, "Cannot convert to integer" else val.to_i end end # Returns an int if this Value is unitless, and raises an # exception otherwise. def to_int val = simplify if Units::Value === val raise TypeError, "Cannot convert to integer" else val.to_int end end # Forces simplification of the Unit part of this Value. Returns # a new Value or a Float. def simplify mul, new_unit = *@unit.simplify if new_unit.unitless? @value * mul else Units::Value.new(@value * mul, new_unit) end end # Returns +self+. def to_value(converter = nil) self end alias inspect to_s def method_missing(m, *args, &blk) if args.length == 1 args[0] = (Units::Converter.converter(args[0]) rescue nil) if not args[0].is_a? Units::Converter return self * Units::Value.new(1, Units::Unit.new({m => 1}, args[0])) if args[0] && args[0].registered?(m) elsif (Units::Converter.current.registered?(m) rescue false) raise ::ArgumentError, "Wrong number of arguments" if args.length != 0 return self * Units::Value.new(1, Units::Unit.new({m => 1}, Units::Converter.current)) end ::Exception.with_clean_backtrace("method_missing") { super } end private def self.decode_string(s, converter) if m = Units::Regexps::VALUE_REGEXP.match(s) value = m[2].empty? ? Integer(m[1]) : Float(m[1]) unit = Units::Unit.new(m[3], converter) [value, unit] else raise ::ArgumentError, "Illegal value string #{s.dump}" end end def do_additive_op(op, other) other = other.to_value raise TypeError, "cannot convert to Value" unless Units::Value === other if other.is_a? Units::Value conv1, conv2 = unit.coerce(other.unit) raise TypeError, "incompatible units for #{op}" if conv1.units != conv2.units mult = conv2.multiplier / conv1.multiplier if mult > 1 [value.send(op, other.value * mult), unit] else mult = conv1.multiplier / conv2.multiplier [(value * mult).send(op, other.value), other.unit] end else raise TypeError, "incompatible operands for #{op}" end end def do_multiplicative_op(op, other) case other when Units::Value [value.send(op, other.value), unit.send(op, other.unit)] when Units::Unit [value, unit.send(op, other)] when ::Numeric [value.send(op, other), unit] else # TODO: How to make this work for Units as well? # Problem : how check whether to_value failed without # masking all exceptions? other = other.to_value raise TypeError, "cannot convert to Value" unless Units::Value === other do_multiplicative_op(op, other) end end end # This class handles all conversions between units. # # There are two kinds of units; those that are not expressed as a function # of other units --called base units-- and those that are expressed # as a function of other units --called derived units. The latter kind is # registered specifying how it depends on other units, while the former # kind is not. # # This class also registers a list of Converters that are generally useable. # The default Converter which is used when none is specified, can be retrieved # with Converter.current. Converters can be registered with Converter.register. # # Converters can be loaded from YAML. This allows Converters to be specified in # configuration files. class Converter Conversion = Struct.new(:units, :multiplier) # :nodoc: class Conversion # :nodoc: def **(p) Conversion.new(units ** p, multiplier ** p) end def *(m) Conversion.new(units * m.units, multiplier * m.multiplier) end def /(m) Conversion.new(units / m.units, multiplier / m.multiplier) end end ShiftedConversion = Struct.new(:delta_type, :offset) # Returns the name of this Converter, or nil if the # Converter is not registered. attr_accessor :name private :name= # Creates a new Converter. If a block is given, # it is executed in the newly created Converter's context. def initialize(name) @conversions = {} @included = [] @name = name end # Included the given converter in the receiver, unless it # was already included. def include(conv) conv = Units::Converter.converter(conv) if not conv.is_a?(Units::Converter) raise "Circular include" if conv.includes?(self) @included << conv if not includes? conv self end # Returns whether the given converter was included in the # receiver. def includes?(conv) conv = Units::Converter.converter(conv) if not conv.is_a?(Units::Converter) return true if conv == self @included.each do |c| return true if conv == c || c.includes?(conv) end false end # Returns the list of all included converters. This list may # contain duplicates in some cases. def included_converters(result = []) result << self @included.reverse_each { |c| c.included_converters(result) } result end # Checks whether the unit with the given name is registered. # The name can be a symbol or a string. def registered?(unit) unit = unit.to_sym return self if registered_here?(unit) @included.reverse_each do |c| if res = c.registered?(unit) return res end end nil end # Returns the base unit with this name def base_unit(name) if conv = registered?(name) return Units::BaseUnit.new(name, conv) end raise "unit #{name.to_s.dump} not registered with #{self}" end # Returns the list of registered unit names as symbols. def registered_units @conversions.keys end # def method_missing(m, *args, &blk) if registered?(m) raise ::ArgumentError, "Wrong number of arguments" if args.length != 0 return Units::Unit.new({m => 1}, self) end ::Exception.with_clean_backtrace("method_missing") { super } end # Returns a human readable string representation of this Converter. def to_s (@name.to_s if @name) || "#" end alias inspect to_s private # Registers a new Unit with the given name. The +data+ parameter # is a Hash with some extra parameters (can be Strings or Symbols): # +alias+:: Specifies possible aliases for the Unit. # +abbrev+:: Specifies possible abbreviations or symbols for the Unit. # The differences with aliases is that prefixes work differently; # see +register_si_unit+, +register_binary_unit+ and # +register_binary_iec_unit+. # +equals+:: If present, specifies how the Unit depends on other units. # The value for this key can either be a Hash with +unit+ mapping to # a Unit and +multiplier+ mapping to a numeric multiplier, or # a String containing the multiplier, followed by a space, followed by # a representation of the Unit as returned by Unit#to_s. # # Examples: # # converter.register_unit(:pint, :alias => :pints, :abbrev => [:pt, :pts])) # converter.register_unit(:quart, 'alias' => :quarts, :abbrev => ['qt', :qts], :equals => '2.0 pt')) # converter.register_unit(:gallon, :alias => :gallons, :abbrev => :gal, 'equals' => {:unit => Unit.new('qt' => 1, converter), 'multiplier' => 4.0)) # # Note that Symbols and Strings are generally exchangeable within this # library (internally they are converted to Symbols). The number one reason # for this is that String look better in YAML. # # See also +register_si_unit+, +register_binary_unit+, +register_binary_iec_unit+, # +register_length_unit+, and +register_currency+ in currency.rb. def register_unit(unit, abbrevs=[], aliases=[], &conversion) unit = unit.to_sym abbrevs = [abbrevs].flatten.map{ |a| a.to_sym } aliases = [aliases].flatten.map{ |a| a.to_sym } #aliases = ["#{unit}s".to_sym] if aliases.empty? # TRANS: hmm... not here? #unit, aliases, abbrevs = extract_data(name, data, :to_sym) #conversion = data[:equals] # TRANS: this can be imporved now that conversion is a block? conversion = conversion.call if conversion conversion = decode_conversion(conversion) if conversion conversion = self.class.convert_conversion(conversion[:unit].units, conversion[:multiplier]) if conversion register_unit_internal(unit, conversion) conversion = self.class.convert_conversion({base_unit(unit) => 1}, 1) if not conversion (aliases + abbrevs).each do |u| register_unit_internal(u, conversion) end end def register_unit_internal(unit, conversion) raise "unit #{unit.to_s.dump} already registered with #{self}" if registered_here? unit @conversions[unit] = conversion || :none end def register_prefixed_unit(unit, prefixes, abbrevs=[], aliases=[], &conversion) unit = unit.to_s abbrevs = [abbrevs].flatten.map{ |a| a.to_s } aliases = [aliases].flatten.map{ |a| a.to_s } aliases = ["#{unit}s"] if aliases.empty? #aliases, abbrevs = extract_data(unit, data, :to_s) register_unit(unit, abbrevs, aliases, &conversion) unit_sym = unit.to_sym prefixes.each_pair do |pre,info| abbrev = info[:abbrev] multiplier = info[:multiplier] || 1 power = info[:power] || 1 register_unit(pre + unit) do {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier} end aliases.each do |a| register_unit(pre + a) do {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier} end end abbrevs.each do |a| register_unit(abbrev + a) do {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier} end end end end def decode_conversion(data) if not data.is_a? ::String return {:unit => data[:unit] || data['unit'], :multiplier => data[:multiplier] || data['multiplier']} end if /^\s*1\s*\// =~ data {:unit => Units::Unit.new(data, self)} elsif m = /^\s*#{Units::Regexps::NUMBER_REGEXP}(?:\s+(\S.*)$|\s*$)/.match(data) unit = m[3] ? Units::Unit.new(m[3], self) : Units::Unit.new({}, self) if m[1] multiplier = m[2].empty? ? Integer(m[1]) : Float(m[1]) {:unit => unit, :multiplier => multiplier} else {:unit => unit} end else {:unit => Units::Unit.new(data, self)} end end # Checks whether the unit with the given name is registered. # The name can be a symbol or a string. def registered_here?(unit) unit = unit.to_sym conversions(unit) != nil end def conversions(unit) @conversions[unit] #|| (unit == :'--base-currency--' ? :none : nil) end class << self THREAD_REFERENCE = 'Units::converter'.to_sym private :new # Returns the current Converter in the current Thread. # The default converter is the one returned by converter(:default). # See also Units#with_converter and Converter.converter. def current Thread.current[THREAD_REFERENCE] ||= converter(:default) end def with_converter(conv) # :nodoc: conv = converter(conv) if not conv.is_a? Units::Converter raise ::ArgumentError, "Converter expected" if not conv.is_a? Units::Converter begin old_conv = Thread.current[THREAD_REFERENCE] if old_conv new_conv = Converter.send(:new, nil) new_conv.include(old_conv) new_conv.include(conv) else new_conv = conv end Thread.current[THREAD_REFERENCE] = new_conv yield ensure Thread.current[THREAD_REFERENCE] = old_conv end end # Returns the converter with the given name. # This name can be a Symbol or a String. def converter(name, &blk) if blk (converters[name.to_sym] ||= new(name.to_sym)).instance_eval(&blk) else converters[name.to_sym] or raise ::ArgumentError, "No converter #{name.to_s.dump} found" end end # # Registers the given Converter under the given name. # # This name can be a Symbol or a String. A Converter # # can be registered under one name only, and only one # # Converter can be registered under a given name. # def register(name, converter) # name = name.to_sym # return if converters[name] == converter # raise ArgumentError, "This converter is already registered under anoher name" if converter.name # raise ArgumentError, "A converter with this name already exists" if converters[name] # converters[name] = converter # converter.name ||= name # end # Returns the list of names of registered converters. def registered_converters converters.keys end def coerce_units(unit1, unit2) # :nodoc: [convert_conversion(unit1.units), convert_conversion(unit2.units)] end def simplify_unit(unit) # :nodoc: conv = convert_conversion(unit.units) [conv[:multiplier], conv[:units]] end def convert_conversion(units, multiplier = nil) multiplier ||= 1 base_units = {} other_units = {} units.each_pair do |u, e| (u.conversion != :none ? other_units : base_units)[u] = e end result = Conversion.new(Units::Unit.new(base_units, self), multiplier) other_units.each_pair do |u, e| result *= (u.conversion ** e) end result end def converters @converters ||= {} end end end # Contains some configuration related constants. # TODO: Use plugin manager to find units. module Config # The directory in which the data files are searched for #DATADIR = 'data/van/units' #DATADIR = File.join(::Config::CONFIG['DATADIR'], 'van', 'units') #CONFIGDIR = 'lib/van/data' #DATADIR = File.join(::Config::CONFIG['DATADIR'], 'van', 'units') SYSTEMDIR = File.dirname(__FILE__) CONFIGDIR = File.join(SYSTEMDIR, 'data') end end end class Numeric # def method_missing(m, *args, &blk) if args.length == 1 args[0] = (Van::Units::Converter.converter(args[0]) rescue nil) if not args[0].is_a?(Van::Units::Converter) return Van::Units::Value.new(self, Van::Units::Unit.new({m => 1}, args[0])) if args[0] && args[0].registered?(m) elsif Van::Units::Converter.current.registered?(m) raise ::ArgumentError, "Wrong number of arguments" if args.length != 0 return Van::Units::Value.new(self, Van::Units::Unit.new({m => 1}, Van::Units::Converter.current)) end ::Exception.with_clean_backtrace("method_missing") { super } end # def to_value(unit) Van::Units::Value.new(self, unit) end end class String # def to_unit(converter = nil) Van::Units::Unit.new(self, converter) end # def to_value(converter = nil) Van::Units::Value.new(self, converter) end end