Class: Stick::Units::Converter

Inherits:
Object
  • Object
show all
Defined in:
lib/stick/units.rb,
lib/stick/currency.rb,
lib/stick/units/base.rb,
lib/stick/units/loaders.rb,
lib/stick/units/currency.rb

Overview

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.

Defined Under Namespace

Classes: Conversion, ExchangeRate, ShiftedConversion

Constant Summary collapse

THREAD_REFERENCE =
'Units::converter'.to_sym

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Converter

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



668
669
670
671
672
# File 'lib/stick/units/base.rb', line 668

def initialize(name)
  @conversions = {}
  @included = []
  @name = name
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args, &blk) ⇒ Object



729
730
731
732
733
734
735
736
737
# File 'lib/stick/units/base.rb', line 729

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

Instance Attribute Details

#nameObject

Returns the name of this Converter, or nil if the Converter is not registered.



663
664
665
# File 'lib/stick/units/base.rb', line 663

def name
  @name
end

Class Method Details

.coerce_units(unit1, unit2) ⇒ Object

:nodoc:



909
910
911
# File 'lib/stick/units/base.rb', line 909

def coerce_units(unit1, unit2) # :nodoc:
  [convert_conversion(unit1.units), convert_conversion(unit2.units)]
end

.convert_conversion(units, multiplier = nil) ⇒ Object



918
919
920
921
922
923
924
925
926
927
928
929
930
# File 'lib/stick/units/base.rb', line 918

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

.converter(name, &blk) ⇒ Object

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



883
884
885
886
887
888
889
# File 'lib/stick/units/base.rb', line 883

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

.convertersObject



932
933
934
# File 'lib/stick/units/base.rb', line 932

def converters
  @converters ||= {}
end

.currentObject

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.



858
859
860
# File 'lib/stick/units/base.rb', line 858

def current
  Thread.current[THREAD_REFERENCE] ||= converter(:default)
end

.register_loader(loader) ⇒ Object

def load(file)

  load_config(file, self)
end


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/stick/units/loaders.rb', line 49

def register_loader(loader)
  loaders[loader] ||= loader
  loader.handles.each do |h|
    loader_hash[h] ||= begin
      eval %{
        module_eval do
          def #{h}(*a, &b)
            self.class.send(:loader_hash)[#{h.inspect}].#{h}(self, *a, &b)
          end
        end
      }
      loader
    end
  end
  @loader_hash
end

.registered_convertersObject

Returns the list of names of registered converters.



905
906
907
# File 'lib/stick/units/base.rb', line 905

def registered_converters
  converters.keys
end

.require(file) ⇒ Object



38
39
40
41
42
43
# File 'lib/stick/units/loaders.rb', line 38

def require(file)
  @required_configs[file] ||= begin
    load_config(file + ".rb", self)
    true
  end
end

.simplify_unit(unit) ⇒ Object

:nodoc:



913
914
915
916
# File 'lib/stick/units/base.rb', line 913

def simplify_unit(unit) # :nodoc:
  conv = convert_conversion(unit.units)
  [conv[:multiplier], conv[:units]]
end

.with_converter(conv) ⇒ Object

:nodoc:

Raises:

  • (::ArgumentError)


862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
# File 'lib/stick/units/base.rb', line 862

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

Instance Method Details

#base_unit(name) ⇒ Object

Returns the base unit with this name



716
717
718
719
720
721
# File 'lib/stick/units/base.rb', line 716

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

#include(conv) ⇒ Object

Included the given converter in the receiver, unless it was already included.



676
677
678
679
680
681
# File 'lib/stick/units/base.rb', line 676

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

#included_converters(result = []) ⇒ Object

Returns the list of all included converters. This list may contain duplicates in some cases.



696
697
698
699
700
# File 'lib/stick/units/base.rb', line 696

def included_converters(result = [])
  result << self
  @included.reverse_each { |c| c.included_converters(result) }
  result
end

#includes?(conv) ⇒ Boolean

Returns whether the given converter was included in the receiver.

Returns:

  • (Boolean)


685
686
687
688
689
690
691
692
# File 'lib/stick/units/base.rb', line 685

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

#load(file) ⇒ Object



83
84
85
# File 'lib/stick/units/loaders.rb', line 83

def load(file)
  self.class.send(:load_config, file, self)
end

#registered?(unit) ⇒ Boolean

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

Returns:

  • (Boolean)


704
705
706
707
708
709
710
711
712
713
# File 'lib/stick/units/base.rb', line 704

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

#registered_unitsObject

Returns the list of registered unit names as symbols.



724
725
726
# File 'lib/stick/units/base.rb', line 724

def registered_units
  @conversions.keys
end

#to_sObject Also known as: inspect

Returns a human readable string representation of this Converter.



740
741
742
# File 'lib/stick/units/base.rb', line 740

def to_s
  (@name.to_s if @name) || "#<Converter:#{object_id.to_s(16)}>"
end