Class: Dimensional::Unit

Inherits:
Object
  • Object
show all
Extended by:
Enumerable
Defined in:
lib/dimensional/unit.rb

Overview

A standard scale unit for measuring physical quantities. In addition to the Dimension and System attribute that are well-defined by classes above, the user-defined metric attribute is available to identify units as belonging to an arbitrary metric like length, draft or property size. Effective use of the metric attribute can simplify presentation of Measures and make parsing of user input more accurate. Reference: en.wikipedia.org/wiki/Units_of_measurement

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, system, dimension, options = {}) ⇒ Unit

Returns a new instance of Unit.



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/dimensional/unit.rb', line 48

def initialize(name, system, dimension, options = {})
  @name = name.to_s
  @system = system
  @dimension = dimension
  @reference_factor = options[:reference_factor] || Rational(1,1)
  @reference_units = options[:reference_units] || {}
  @abbreviation = options[:abbreviation]
  @detector = options[:detector] || /\A#{[name, abbreviation].compact.join('|')}\Z/
  @format = options[:format] || "%s %U"
  @preference = options[:preference] || 0
  validate
end

Instance Attribute Details

#abbreviationObject (readonly)

Returns the value of attribute abbreviation.



43
44
45
# File 'lib/dimensional/unit.rb', line 43

def abbreviation
  @abbreviation
end

#detectorObject (readonly)

Returns the value of attribute detector.



46
47
48
# File 'lib/dimensional/unit.rb', line 46

def detector
  @detector
end

#dimensionObject (readonly)

Returns the value of attribute dimension.



44
45
46
# File 'lib/dimensional/unit.rb', line 44

def dimension
  @dimension
end

#formatObject (readonly)

Returns the value of attribute format.



46
47
48
# File 'lib/dimensional/unit.rb', line 46

def format
  @format
end

#nameObject (readonly)

Returns the value of attribute name.



43
44
45
# File 'lib/dimensional/unit.rb', line 43

def name
  @name
end

#preferenceObject (readonly)

Returns the value of attribute preference.



46
47
48
# File 'lib/dimensional/unit.rb', line 46

def preference
  @preference
end

#reference_factorObject (readonly)

Returns the value of attribute reference_factor.



45
46
47
# File 'lib/dimensional/unit.rb', line 45

def reference_factor
  @reference_factor
end

#reference_unitsObject (readonly)

Returns the value of attribute reference_units.



45
46
47
# File 'lib/dimensional/unit.rb', line 45

def reference_units
  @reference_units
end

#systemObject (readonly)

Returns the value of attribute system.



44
45
46
# File 'lib/dimensional/unit.rb', line 44

def system
  @system
end

Class Method Details

.[](dim, sys, sym) ⇒ Object

Lookup the unit by name or abbreviation, scoped by dimension and system



30
31
32
33
34
35
36
37
# File 'lib/dimensional/unit.rb', line 30

def self.[](dim, sys, sym)
  dim = Dimension[dim] unless dim.kind_of?(Dimension)
  sys = System[sys] unless sys.kind_of?(System)
  sym = sym.to_sym
  us = @store.select{|u| u.dimension == dim}.select{|u| u.system == sys}
  u = us.detect{|u| sym == u.name.to_sym || (u.abbreviation && sym == u.abbreviation.to_sym)}
  u || (raise ArgumentError, "Can't find unit: #{dim}, #{sys}, #{sym}")
end

.each(&block) ⇒ Object



18
19
20
# File 'lib/dimensional/unit.rb', line 18

def self.each(&block)
  @store.each(&block)
end

.register(*args) ⇒ Object



22
23
24
25
26
27
# File 'lib/dimensional/unit.rb', line 22

def self.register(*args)
  u = new(*args)
  raise "Namespace collision: #{u.inspect}" if @store.include?(u)
  @store << u
  u
end

.reset!Object



39
40
41
# File 'lib/dimensional/unit.rb', line 39

def self.reset!
  @store.clear
end

Instance Method Details

#==(other) ⇒ Object

Equality is determined by equality of value-ish attributes. Specifically, equal factors relative to the same base.



101
102
103
# File 'lib/dimensional/unit.rb', line 101

def ==(other)
  (other.base == self.base) && other.factor == self.factor
end

#baseObject

Returns the unit or array of units on which this unit’s scale is ultimately based. The technique used is to multiply the bases’ exponents by our exponent and then consolidate resulting common bases by adding their exponents.



76
77
78
79
80
81
82
# File 'lib/dimensional/unit.rb', line 76

def base
  return {self => Rational(1,1)} if base?
  @base ||= reference_units.inject({}) do |summary0, (ru0, exp0)|
    t = ru0.base.inject({}){|summary1, (ru1, exp1)| summary1[ru1] = exp1 * exp0;summary1}
    summary0.merge(t) {|ru, expa, expb| expa + expb}
  end
end

#base?Boolean

If no reference was provided during initialization, this unit must itself be a base unit.

Returns:

  • (Boolean)


69
70
71
# File 'lib/dimensional/unit.rb', line 69

def base?
  reference_units.empty?
end

#commensurable?(other) ⇒ Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/dimensional/unit.rb', line 96

def commensurable?(other)
  dimension == other.dimension
end

#convert(other) ⇒ Object

Returns the conversion factor to convert to the other unit



90
91
92
93
94
# File 'lib/dimensional/unit.rb', line 90

def convert(other)
  raise "Units #{self} and #{other} are not commensurable" unless commensurable?(other)
  return Rational(1,1) if self == other
  self.factor / other.factor
end

#eql?(other) ⇒ Boolean

Hashing collisions are desired when we have same identity-defining attributes.

Returns:

  • (Boolean)


106
107
108
# File 'lib/dimensional/unit.rb', line 106

def eql?(other)
  other.kind_of?(self.class) && other.dimension.eql?(self.dimension) && other.system.eql?(self.system) && other.name.eql?(self.name)
end

#factorObject

The conversion factor relative to the base unit.



85
86
87
# File 'lib/dimensional/unit.rb', line 85

def factor
  @factor ||= reference_factor * reference_units.inject(Rational(1,1)){|f, (ru, exp)| f * (ru.factor**exp)}
end

#hashObject

This is pretty lame, but the expected usage means we shouldn’t get penalized



111
112
113
# File 'lib/dimensional/unit.rb', line 111

def hash
  [self.class, dimension, system, name].hash
end

#inspectObject



119
120
121
# File 'lib/dimensional/unit.rb', line 119

def inspect
  "#<#{self.class.inspect}: #{dimension.to_s}:#{system.to_s}:#{to_s}>"
end

#to_sObject



115
116
117
# File 'lib/dimensional/unit.rb', line 115

def to_s
  name rescue super
end

#validateObject



61
62
63
64
65
66
# File 'lib/dimensional/unit.rb', line 61

def validate
  return "Reference factor must be numeric: #{@reference_factor}." unless factor.kind_of?(Numeric)
  return "Reference units must all be units: #{@reference_units}." unless reference_units.all?{|u, exp| u.kind_of?(Dimensional::Unit)}
  return "Reference exponents must all be rationals: #{@reference_units}." unless reference_units.all?{|u, exp| exp.kind_of?(Rational)}
  return "Preference must be numeric: #{@preference}." unless preference.kind_of?(Numeric)
end