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.
- clean_eval
- conversions
- convert_conversion
- converter
- converters
- current
- decode_conversion
- extract_data
- from_yaml
- load_yaml
- method_missing
- new
- register
- register_binary_unit
- register_currency
- register_length_unit
- register_prefixed_unit
- register_si_unit
- register_unit
- register_unit_internal
- registered?
- registered_converters
- registered_units
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. |
[ show source ]
# 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
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.
[ show source ]
# File lib/facets/more/units.rb, line 674 def self.current Thread.current["current_unit_converter"] ||= converter(:default) end
Creates a new Converter, where the data is loaded from the file with the given file name.
[ show source ]
# File lib/facets/more/units.rb, line 519 def self.from_yaml(file) Units::Converter.new { load_yaml(file) } end
Creates a new Converter. If a block is given, it is executed in the newly created Converter’s context.
[ show source ]
# File lib/facets/more/units.rb, line 527 def initialize(&blk) @loaded_yaml = [] @conversions = {} instance_eval(&blk) if blk end
[ show source ]
# File lib/facets/more/units.rb, line 698 def self.register(name, converter) converters[name.to_sym] = converter end
Returns the list of names of registered converters.
[ show source ]
# File lib/facets/more/units.rb, line 703 def self.registered_converters converters.keys end
[ show source ]
# File lib/facets/more/units.rb, line 783 def self.converters @converters ||= {} end
Loads data from the YAML file with the given name. Returns self.
[ show source ]
# 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
[ show source ]
# 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
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.
[ show source ]
# File lib/facets/more/units.rb, line 597 def register_binary_unit(unit, data = {}) register_prefixed_unit(unit, BINARY_PREFIXES, data) end
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.
[ show source ]
# 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
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.
[ show source ]
# File lib/facets/more/units.rb, line 607 def register_length_unit(unit, data = {}) register_prefixed_unit(unit, LENGTH_PREFIXES, data) end
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.
[ show source ]
# File lib/facets/more/units.rb, line 587 def register_si_unit(unit, data = {}) register_prefixed_unit(unit, SI_PREFIXES, data) end
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.
[ show source ]
# 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
Checks whether the unit with the given name is registered. The name can be a symbol or a string.
[ show source ]
# File lib/facets/more/units.rb, line 535 def registered?(unit) unit = unit.to_sym conversions(unit) != nil end
Returns the list of registered unit names as symbols.
[ show source ]
# File lib/facets/more/units.rb, line 541 def registered_units @conversions.keys end
[ show source ]
# File lib/facets/more/units.rb, line 709 def clean_eval(some_name_noone_uses) eval some_name_noone_uses, binding, __FILE__, __LINE__ end
[ show source ]
# File lib/facets/more/units.rb, line 779 def conversions(unit) @conversions[unit] || (unit == ??{--base-currency--} ? :none : nil) end
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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