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 former kind is registered without specifying how it depends on other Units, while the latter kind is registered with doing so.

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.

Methods
Classes and Modules
Class Units::Converter::ExchangeRate
Constants
SI_PREFIXES = { 'yotta' => {:abbrev => 'Y', :multiplier => 1e24}, 'zetta' => {:abbrev => 'Z', :multiplier => 1e21}, 'exa' => {:abbrev => 'E', :multiplier => 1e18}, 'peta' => {:abbrev => 'P', :multiplier => 1e14}, 'tera' => {:abbrev => 'T', :multiplier => 1e12}, 'giga' => {:abbrev => 'G', :multiplier => 1e9}, 'mega' => {:abbrev => 'M', :multiplier => 1e6}, 'kilo' => {:abbrev => 'k', :multiplier => 1e3}, 'hecto' => {:abbrev => 'h', :multiplier => 1e2}, 'deca' => {:abbrev => 'da', :multiplier => 1e1}, 'deci' => {:abbrev => 'd', :multiplier => 1e-1}, 'centi' => {:abbrev => 'c', :multiplier => 1e-2}, 'milli' => {:abbrev => 'm', :multiplier => 1e-3}, 'micro' => {:abbrev => 'u', :multiplier => 1e-6}, 'nano' => {:abbrev => 'n', :multiplier => 1e-9}, 'pico' => {:abbrev => 'p', :multiplier => 1e-12}, 'femto' => {:abbrev => 'f', :multiplier => 1e-15}, 'atto' => {:abbrev => 'a', :multiplier => 1e-18}, 'zepto' => {:abbrev => 'z', :multiplier => 1e-21}, 'yocto' => {:abbrev => 'y', :multiplier => 1e-24}
  The prefixes used for SI units. See also Converter#register_si_unit.
BINARY_PREFIXES = { 'yotta' => {:abbrev => 'Y', :multiplier => 1024.0 ** 8}, 'zetta' => {:abbrev => 'Z', :multiplier => 1024.0 ** 7}, 'exa' => {:abbrev => 'E', :multiplier => 1024.0 ** 6}, 'peta' => {:abbrev => 'P', :multiplier => 1024.0 ** 5}, 'tera' => {:abbrev => 'T', :multiplier => 1024.0 ** 4}, 'giga' => {:abbrev => 'G', :multiplier => 1024.0 ** 3}, 'mega' => {:abbrev => 'M', :multiplier => 1024.0 ** 2}, 'kilo' => {:abbrev => 'k', :multiplier => 1024.0}, }
  The prefixes used for binary units. See also Converter#register_binary_unit.
LENGTH_PREFIXES = { 'square_' => {:abbrev => 'sq_', :power => 2}, 'cubic_' => {:abbrev => 'cu_', :power => 3}
  The prefixes used for length units. See also Converter#register_length_unit.
Public Class methods
converter(name)

Returns the converter with the given name. This name can be a Symbol or a String.

# File lib/facets/more/units.rb, line 692
    def self.converter(name)
      converters[name.to_sym] or raise ArgumentError, "No converter #{name.to_s.dump} found"
    end
current()

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.

# File lib/facets/more/units.rb, line 674
    def self.current
      Thread.current["current_unit_converter"] ||= converter(:default)
    end
from_yaml(file)

Creates a new Converter, where the data is loaded from the file with the given file name.

# File lib/facets/more/units.rb, line 519
    def self.from_yaml(file)
      Units::Converter.new {
        load_yaml(file)
      }
    end
new(&blk)

Creates a new Converter. If a block is given, it is executed in the newly created Converter’s context.

# File lib/facets/more/units.rb, line 527
    def initialize(&blk)
      @loaded_yaml = []
      @conversions = {}
      instance_eval(&blk) if blk
    end
register(name, converter)

Registers the given Converter under the given name. This name can be a Symbol or a String.

# File lib/facets/more/units.rb, line 698
    def self.register(name, converter)
      converters[name.to_sym] = converter
    end
registered_converters()

Returns the list of names of registered converters.

# File lib/facets/more/units.rb, line 703
    def self.registered_converters
      converters.keys
    end
Private Class methods
converters()
# File lib/facets/more/units.rb, line 783
    def self.converters
      @converters ||= {}
    end
Public Instance methods
load_yaml(file)

Loads data from the YAML file with the given name. Returns self.

# File lib/facets/more/units.rb, line 634
    def load_yaml(file)
      return if @loaded_yaml.include? file
      data = YAML.load_file(file)
      @loaded_yaml << file
      old_service = Thread.current['current_currency_exchange_service']
      data.each do |r|
        rule = {}
        r.each_pair do |k, v|
          rule[k.to_sym] = v
        end
        case rule[:type]
        when 'import'
          load_yaml(File.join(Units::Config::DATADIR, rule[:file] + '.yaml'))
        when 'si'
          register_si_unit(rule[:name], rule)
        when 'unit'
          register_unit(rule[:name], rule)
        when 'length'
          register_length_unit(rule[:name], rule)
        when 'binary'
          register_binary_unit(rule[:name], rule)
        when 'currency'
          register_currency(rule[:name], Thread.current['current_currency_exchange_service'])
        when 'service'
          begin
            Thread.current['current_currency_exchange_service'] = Units::Converter::ExchangeRate.const_get(rule[:name])
          rescue NameError
            raise NameError, "Exchange service not found: #{rule[:name].to_s.dump}"
          end
        else
          raise "unknown rule type #{rule[:type].to_s.dump}"
        end
      end
      Thread.current['current_currency_exchange_service'] = old_service
      self
    end
method_missing(m, *args, &blk)
# File lib/facets/more/units.rb, line 624
    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
      super
    end
register_binary_unit(unit, data = {})

Registers a new binary unit. The Unit and its aliases are registered with all available binary prefixes (see BINARY_PREFIXES) as well, while the abbreviations are registered with the abbreviated version of the prefixes.

For the syntax of the options, see register_unit.

# File lib/facets/more/units.rb, line 597
    def register_binary_unit(unit, data = {})
      register_prefixed_unit(unit, BINARY_PREFIXES, data)
    end
register_currency(curr, service = nil)

Registers a given currency, with a given servive to find the exchange rate between the given currency and a given base currency. The service should be a subclass of ExchangeRate. This is not strictly necessary, but ExchangeRate handles all of the magic.

# File lib/facets/more/units.rb, line 615
    def register_currency(curr, service = nil)
      service ||= Units::Config::DEFAULT_CURRENCY_SERVICE
      register_unit(curr, :equals => service.create_conversion(curr, self))
    end
register_length_unit(unit, data = {})

Registers a new length unit. The Unit and its aliases are registered with all available length prefixes (see LENGTH_PREFIXES) as well, while the abbreviations are registered with the abbreviated version of the prefixes.

For the syntax of the options, see register_unit.

# File lib/facets/more/units.rb, line 607
    def register_length_unit(unit, data = {})
      register_prefixed_unit(unit, LENGTH_PREFIXES, data)
    end
register_si_unit(unit, data = {})

Registers a new SI unit. The Unit and its aliases are registered with all available SI prefixes (see SI_PREFIXES) as well, while the abbreviations are registered with the abbreviated version of the prefixes.

For the syntax of the options, see register_unit.

# File lib/facets/more/units.rb, line 587
    def register_si_unit(unit, data = {})
      register_prefixed_unit(unit, SI_PREFIXES, data)
    end
register_unit(name, data = {})

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 and register_binary_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_length_unit and register_currency.

# File lib/facets/more/units.rb, line 569
    def register_unit(name, data = {})
      unit, aliases, abbrevs = extract_data(name, data, :to_sym)
      conversion = data[:equals]
      conversion = decode_conversion(conversion) if conversion
      conversion = convert_conversion(conversion[:unit].units, conversion[:multiplier]) if conversion
      register_unit_internal(unit, conversion)
      conversion = convert_conversion({unit => 1}, 1) if not conversion
      (aliases + abbrevs).each do |u|
        register_unit_internal(u, conversion)
      end
    end
registered?(unit)

Checks whether the unit with the given name is registered. The name can be a symbol or a string.

# File lib/facets/more/units.rb, line 535
    def registered?(unit)
      unit = unit.to_sym
      conversions(unit) != nil
    end
registered_units()

Returns the list of registered unit names as symbols.

# File lib/facets/more/units.rb, line 541
    def registered_units
      @conversions.keys
    end
Private Instance methods
clean_eval(some_name_noone_uses)
# File lib/facets/more/units.rb, line 709
    def clean_eval(some_name_noone_uses)
      eval some_name_noone_uses, binding, __FILE__, __LINE__
    end
conversions(unit)
# File lib/facets/more/units.rb, line 779
    def conversions(unit)
      @conversions[unit] || (unit == ??{--base-currency--} ? :none : nil)
    end
convert_conversion(units, multiplier = nil)
# File lib/facets/more/units.rb, line 765
    def convert_conversion(units, multiplier = nil)
      multiplier ||= 1
      base_units = {}
      other_units = {}
      units.each_pair do |u, e|
        (conversions(u) != :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 *= (conversions(u) ** e)
      end
      result
    end
decode_conversion(data)
# File lib/facets/more/units.rb, line 746
    def decode_conversion(data)
      if not data.is_a? String
        return {:unit => data[:unit] || data['unit'],
                :multiplier => data[:multiplier] || data['multiplier']}
      end
      data.rstrip!
      if m = /^(\d+(\.\d+(?:[eE][-+]?\d+)?)?(?:\s+|$))?(.+)?$/.match(data)
        unit = m[3] ? clean_eval(m[3].gsub(/\b([A-Z]\w*|in)\b/, 'self.\1')) : Units::Unit.new({}, self)
        if m[1]
          multiplier = m[2] ? Float(m[1]) : Integer(m[1])
          {:unit => unit, :multiplier => multiplier}
        else
          {:unit => unit}
        end
      else
        raise ArgumentError, "Wrong unit string"
      end
    end
extract_data(unit, data, conv)
# File lib/facets/more/units.rb, line 736
    def extract_data(unit, data, conv)
      sarray = proc do |k|
        list = data[k] || []
        list = [list] if not list.is_a? Array
        list.map { |a| a.send(conv) }
      end
      unit = unit.send(conv)
      return unit, sarray[:alias], sarray[:abbrev].select { |a| a != unit }
    end
register_prefixed_unit(unit, prefixes, data = {})
# File lib/facets/more/units.rb, line 718
    def register_prefixed_unit(unit, prefixes, data = {})
      unit, aliases, abbrevs = extract_data(unit, data, :to_s)
      register_unit(unit, :equals => data[:equals], :alias => aliases, :abbrev => abbrevs)
      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, :equals => {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier})
        aliases.each do |a|
          register_unit(pre + a, :equals => {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier})
        end
        abbrevs.each do |a|
          register_unit(abbrev + a, :equals => {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier})
        end
      end
    end
register_unit_internal(unit, conversion)
# File lib/facets/more/units.rb, line 713
    def register_unit_internal(unit, conversion)
      raise "unit #{unit.to_s.dump} already registered with #{self}" if registered? unit
      @conversions[unit] = conversion || :none
    end