Module: SY::Unit

Includes:
NameMagic
Defined in:
lib/sy/unit.rb

Overview

This class represents a unit of measurement – a predefined magnitude of a metrological quantity.

Constant Summary collapse

PROTECTED_NAMES =
[ "kilogram" ]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#abbreviationObject Also known as: short

Unlike ordinary magnitudes, units can have names and abbreviations.



121
122
123
# File 'lib/sy/unit.rb', line 121

def abbreviation
  @abbreviation
end

#warnsObject Also known as: warns?

Whether the unit warns when the module in which unit method mixin is included contains blatant name collisions with this unit name/abbreviation.



127
128
129
# File 'lib/sy/unit.rb', line 127

def warns
  @warns
end

Class Method Details

.abbreviationsObject

Unit abbreviations as a hash of abbreviation => unit pairs.



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

def abbreviations
  ii = instances
  Hash[ ii.map( &:short ).zip( ii ).select { |short, _| ! short.nil? } ]
end

.included(target) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/sy/unit.rb', line 30

def included target
  target.namespace = self

  name_set_hook do |name, new_instance, old_name|
    ɴ = name.to_s
    up, down = ɴ.upcase, ɴ.downcase
    msg = "Unit must be either all-upper or all-lower case (#{ɴ} given)!"
    fail NameError, msg unless ɴ == up || ɴ = down
    # Reject the names starting with a full prefix.
    pref = SY::PREFIX_TABLE.full_prefixes.find do |pref|
      down.starts_with? pref unless pref.empty?
    end
    fail NameError, "Name #{ɴ} starts with #{pref}- prefix!" unless
      SY::Unit::PROTECTED_NAMES.include? down if pref
    # Warn about the method name conflicts in the #include target module
    if new_instance.warns? then
      w = SY::ExpressibleInUnits::COLLISION_WARNING
      SY::ExpressibleInUnits.included_in.each do |modul|
        im = modul.instance_methods
        warn w % [down, modul] if im.include? down
        abbrev = new_instance.abbreviation
        warn w % [abbrev, modul] if im.include? abbrev
      end
    end
    up.to_sym
  end

  # We'll now define all the prefix methods on the target (#mili, #mega...),
  # representing multiplication by the aprropriate factor (side effect being
  # returning a non-unit magnitude). However, Unit offers the opportunity to
  # _reframe_ into another quantity, specified by #quantity_by_prefix method.
  # (This method normally returns the unit's own quantity, but can and should
  # be overriden for prefixes indicating special domain (eg. +cm+)...
  # 
  SY::PREFIX_TABLE.full_prefixes.each do |pref|
    unless pref.empty?
      define_method pref do
        SY::Quantity
          .instance( quantity_by_prefix( pref ) )
          .magnitude( self * SY::PREFIX_TABLE.to_factor( pref ) )
      end
    end
  end
end

.instance(arg) ⇒ Object

Make Unit#instance ignore capitalization, accept abbreviations.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/sy/unit.rb', line 12

def instance arg
  begin
    super # let's first try the original method.
  rescue NameError => err # if we fail...
    begin # ... let's try the abbreviation
      super instances.find { |unit_inst|
        unit_inst.short.to_s == arg.to_s if unit_inst.short
      }.tap { |rslt| fail NameError if rslt.nil? } # fail if nothing found
    rescue NameError, TypeError
      begin # Let's to try upcase if we have all-downcase arg
        super arg.to_s.upcase
      rescue NameError # if not, tough luck
        raise NameError, "Unknown unit symbol: #{arg}"
      end
    end
  end
end

.known_symbolsObject

Full list of known unit names and unit abbreviations.



107
108
109
# File 'lib/sy/unit.rb', line 107

def known_symbols
  instances.names( false ).map( &:downcase ) + abbreviations.keys
end

.of(quantity, **nn) ⇒ Object

Constructor of units of a given quantity.



81
82
83
# File 'lib/sy/unit.rb', line 81

def of quantity, **nn
  quantity.unit **nn
end

.parse_sps_using_all_prefixes(sps) ⇒ Object

Parses an SPS, curring it with known unit names and abbreviations, and all known full and short prefixes.



114
115
116
# File 'lib/sy/unit.rb', line 114

def parse_sps_using_all_prefixes sps
  SY::PREFIX_TABLE.parse_sps( sps, known_symbols )
end

.standard(of: nil, **nn) ⇒ Object

Standard unit constructor. In absence of other named arguments, standard unit of the specified quantity is merely retrieved. If other named arguments than :quantity (alias :of) are supplied, they are forwarded to Quantity#new_standard_unit method, that resets the standard unit of the specified quantity. Note that :amount for standard units, if supplied, has special meaning of setting the relationship of that quantity.



92
93
94
95
96
# File 'lib/sy/unit.rb', line 92

def standard( of: nil, **nn )
  fail ArgumentError, ":of argument missing!" if of.nil?
  qnt = SY::Quantity.instance( of )
  nn.empty? ? qnt.standard_unit : qnt.new_standard_unit( **nn )
end

Instance Method Details

#*(other) ⇒ Object

Multiplication: Unit is converted to a magnitude before the operation.



171
172
173
# File 'lib/sy/unit.rb', line 171

def * other
  to_magnitude * other
end

#**(exponent) ⇒ Object

Exponentiation: Unit is converted to a magnitude before the operation.



183
184
185
# File 'lib/sy/unit.rb', line 183

def ** exponent
  to_magnitude ** exponent
end

#+(other) ⇒ Object

Addition: Unit is converted to a magnitude before the operation.



159
160
161
# File 'lib/sy/unit.rb', line 159

def + other
  to_magnitude + other
end

#-(other) ⇒ Object

Subtraction: Unit is converted to a magnitude before the operation.



165
166
167
# File 'lib/sy/unit.rb', line 165

def - other
  to_magnitude - other
end

#/(other) ⇒ Object

Division: Unit is converted to a magnitude before the operation.



177
178
179
# File 'lib/sy/unit.rb', line 177

def / other
  to_magnitude / other
end

#coerce(other) ⇒ Object

Coercion: Unit is converted to a magnitude before coercion is actually performed.



190
191
192
# File 'lib/sy/unit.rb', line 190

def coerce other
  to_magnitude.coerce( other )
end

#initialize(short: nil, warns: true, **nn) ⇒ Object

Constructor of units provides support for one additional named argument: :abbreviation, alias :short. (This is in addition to :name, alias :ɴ named argument provided by NameMagic.) As a general rule, only named units unit should be given abbreviations. In choosing unit names and abbreviations, ambiguity with regard to standard prefixes and abbreviations thereof should also be avoided. Another argument, :warns, Boolean, true by default, determines whether the method warns about name collisions with other methods defined where the SY::ExpressibleInUnits mixin is included.



151
152
153
154
155
# File 'lib/sy/unit.rb', line 151

def initialize( short: nil, warns: true, **nn )
  @abbreviation = short.to_sym if short
  @warns = warns # does this unit care about blatant name collisions?
  super nn
end

#inspectObject

Inspect string for the unit.



208
209
210
# File 'lib/sy/unit.rb', line 208

def inspect
  name.nil? ? inspect_when_anonymous : inspect_when_named
end

#quantity_by_prefix(prefix) ⇒ Object

Some prefixes of some units are almost exclusively used in certain areas of science or engineering, and their appearance would indicate such specific quantity. By default, this method simply returns unit’s own quantity unchanged. But it is expected that the method will be overriden by a singleton method in those units, which have area-specific prefixes. For example, centimetre, typical for civil engineering, could cause reframing into its own CentimetreLength quantity. Assuming METRE unit, this could be specified for example by: <tt> METRE.define_singleton_method :quantity_by_prefix do |full_prefix|

case full_prefix
when :centi then CentimetreLength
else self.quantity end

end </tt>



228
229
230
# File 'lib/sy/unit.rb', line 228

def quantity_by_prefix prefix
  quantity
end

#reframe(other_quantity) ⇒ Object

Reframing: Unit is converted to a magnitude before reframing.



196
197
198
# File 'lib/sy/unit.rb', line 196

def reframe other_quantity
  to_magnnitude.reframe( other_quantity )
end

#short=(unit_abbreviation) ⇒ Object

Unit abbreviation setter (alias for #abbreviation=).



138
139
140
# File 'lib/sy/unit.rb', line 138

def short= unit_abbreviation
  @abbreviation = unit_abbreviation.to_sym
end

#to_sObject

Unit as string.



202
203
204
# File 'lib/sy/unit.rb', line 202

def to_s
  name.nil? ? to_s_when_anonymous : to_s_when_named
end