Class: Quantity::Unit

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/quantity/unit.rb,
lib/quantity/systems/si.rb,
lib/quantity/systems/us.rb,
lib/quantity/systems/imperial.rb,
lib/quantity/systems/enumerable.rb,
lib/quantity/systems/information.rb

Overview

Imperial versions of british/american customary units

Constant Summary collapse

@@units =

All known units

{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Unit

A new compound unit. There are two modes of operation. One provides a way to add units with a DSL. The other provides an options hash a little better for programming. The last provides a way to create a unit for a given dimension--useful for reference units.

Parameters:

  • opts (String Symbol) (defaults to: {})

    :name

  • opts ([Unit]) (defaults to: {})

    :units

  • opts (Dimension) (defaults to: {})

    :dimension

Raises:

  • (ArgumentError)


276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/quantity/unit.rb', line 276

def initialize(opts)
  @units = opts[:units]
  @dimension = opts[:dimension]
  @value = opts[:value] || calculate_value
  if @dimension.nil?
    raise ArgumentError, "Adding invalid unit with nil dimension (#{name} - #{dimension})"
  end
  unless opts[:name] || !@dimension.is_base?
    raise ArgumentError, "Single-order units must be uniquely named (#{name} - #{dimension})"
  end
  @name = opts[:name] || string_form
  self.class.add_alias(self,@name.to_sym)
  raise ArgumentError, "Creating new unit with no value" unless @value
end

Instance Attribute Details

#aliasesObject (readonly)

Instance-level methods/vars



86
87
88
# File 'lib/quantity/unit.rb', line 86

def aliases
  @aliases
end

#dimensionObject (readonly)

Instance-level methods/vars



86
87
88
# File 'lib/quantity/unit.rb', line 86

def dimension
  @dimension
end

#nameObject (readonly)

Instance-level methods/vars



86
87
88
# File 'lib/quantity/unit.rb', line 86

def name
  @name
end

#unitsObject (readonly)

Higher-order units have a set of units to reference each aspect of the dimension they measure. This is unused in basic units.



265
266
267
# File 'lib/quantity/unit.rb', line 265

def units
  @units
end

#valueObject (readonly)

Instance-level methods/vars



86
87
88
# File 'lib/quantity/unit.rb', line 86

def value
  @value
end

Class Method Details

.add_alias(unit, *names) ⇒ Object

Register a unit with the given symbols

Parameters:



48
49
50
51
52
53
# File 'lib/quantity/unit.rb', line 48

def self.add_alias(unit,*names)
  unit = Unit.for(unit) unless unit.is_a? Unit
  names.each do |name|
    @@units[name] = unit
  end
end

.add_unit(name, dimension, value, *names) ⇒ Object Also known as: add

Add a unit to the system

Parameters:

  • dimension (Dimension)
  • name (Symbol)
  • value (Numeric)
  • *aliases ([String Symbol])


60
61
62
63
64
65
# File 'lib/quantity/unit.rb', line 60

def self.add_unit(name,dimension,value,*names)
  new_unit = Unit.new({ :name => name,:dimension => Quantity::Dimension.for(dimension),:value => value})
  names.each do | name |
    add_alias new_unit, name
  end
end

.add_units(&block) ⇒ Object

Add a number of units to the system

Examples:

length = Quantity::Dimension.for(:length)
Quantity::Unit.add_units do
  add :meter length 1000
  add :mm :length 1
end


75
76
77
# File 'lib/quantity/unit.rb', line 75

def self.add_units(&block)
  self.class_eval(&block)
end

.for(to) ⇒ Unit

The unit for a given symbol or string description of a compound unit

Parameters:

  • to (Symbol String Unit)

Returns:



34
35
36
# File 'lib/quantity/unit.rb', line 34

def self.for(to)
  to.is_a?(Unit) ? to : @@units[to]
end

.from_string_form(to) ⇒ Object

Parse a string representation of a unit, such as foot^2/time^2, and return a compound object representing it.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/quantity/unit.rb', line 242

def self.from_string_form(to)
  if Unit.for(to)
    Unit.for(to)
  else
    dimension_string = to.to_s.dup
    units = {}
    to.to_s.split(/(\^|\/|\*)/).each do | name |
      next if name =~ /(\^|\/|\*)/ || name =~ /^\d$/
      unit = Unit.for(name.to_sym) || Unit.for(name)
      dimension_string.gsub!(name,unit.dimension.name.to_s)
      units[unit.dimension] = unit
    end
    dimension = Dimension.for(dimension_string.to_sym)
    raise ArgumentError, "Couldn't create Unit for #{to}" unless dimension && units
    unit = Unit.new({ :dimension => dimension, :units => units })
    add_alias(unit,unit.name.to_sym)
    unit
  end
end

.is_unit?(to) ⇒ Boolean

Whether or not the given symbol or string refers to an existing unit

Parameters:

  • to (Symbol String Unit)

Returns:

  • (Boolean)


41
42
43
# File 'lib/quantity/unit.rb', line 41

def self.is_unit?(to)
  to.is_a?(Unit) || @@units.has_key?(to)
end

.string_form(dimension, units) ⇒ String

a vaguely human-readable format for a compound unit

Parameters:

Returns:

  • (String)


318
319
320
321
322
323
324
# File 'lib/quantity/unit.rb', line 318

def self.string_form(dimension,units)
  string = dimension.string_form
  units.each do | dimension, unit |
    string = string.gsub(dimension.name.to_s, unit.name.to_s)
  end
  string
end

Instance Method Details

#*(other) ⇒ Unit

Unit multiplication.

Parameters:

Returns:



192
193
194
195
196
197
198
199
200
201
202
# File 'lib/quantity/unit.rb', line 192

def *(other)
  if other.is_a?(Unit)
    units = other.units || { other.dimension => other }
    units.merge!(@units || { @dimension => self })
    dim = @dimension * other.dimension
    existing = Unit.for(Unit.string_form(dim,units).to_sym)
    existing ||= Unit.new({ :dimension => dim, :units => units }) 
  else
    raise ArgumentError, "Cannot multiply #{self} with #{other}"
  end
end

#**(other) ⇒ Unit

Exponentiation

Parameters:

  • other (Numeric)

Returns:



181
182
183
184
185
186
187
# File 'lib/quantity/unit.rb', line 181

def **(other)
  if other.is_a?(Fixnum) && other > 0
    other == 1 ? self : self * self**(other-1)
  else
    raise ArgumentError, "#{self} cannot be raised to #{other} power."
  end
end

#/(other) ⇒ Unit

Unit division.

Parameters:

Returns:



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/quantity/unit.rb', line 207

def /(other)
  if other.is_a?(Unit)
    units = other.units || { other.dimension => other }
    units.merge!(@units || { @dimension => self })
    dim = @dimension / other.dimension
    existing = Unit.for(Unit.string_form(dim,units).to_sym)
    existing ||= Unit.new({ :dimension => dim, :units => units }) 
    existing
  else
    raise ArgumentError, "Cannot multiply #{self} with #{other}"
  end
end

#<=>(other) ⇒ Object



168
169
170
171
172
173
174
175
176
# File 'lib/quantity/unit.rb', line 168

def <=>(other)
  if other.is_a?(Unit) && other.dimension == @dimension
    @value <=> other.value
  elsif other.is_a?(Unit)
    @name <=> other.name
  else
    nil
  end
end

#bitObject



7
# File 'lib/quantity/systems/information.rb', line 7

add_unit :bit, :data, 1, :bits

#calculate_valueObject

calculate this unit's value compared to the reference unit



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/quantity/unit.rb', line 292

def calculate_value
  value = defined?(Rational) ? Rational(1) : 1.0
  @dimension.numerators.each do | component |
    component.power.times do 
      # we might have a unit for a compound dimension, such as liters for length^3.
      value *= @units[Quantity::Dimension.for(component.dimension)].value
    end
  end
  @dimension.denominators.each do | component |
    component.power.times do 
      value /= @units[Quantity::Dimension.for(component.dimension)].value
    end
  end
  @value = value
end

#can_convert_to?(to) ⇒ Boolean

Can this unit be converted into the target unit?

Parameters:

  • (Symbol String Unit)

Returns:

  • (Boolean)


102
103
104
# File 'lib/quantity/unit.rb', line 102

def can_convert_to?(to)
  Unit.for(to).dimension == @dimension
end

#convert(target) ⇒ Unit

Convert a portion of this compound to another unit. This one is tricky, because a lot of things can be happening. It's valid to convert m^2 to ft^2 and to feet (ft^2), but not really valid to convert to ft^3.

Parameters:

Returns:



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

def convert(to)
  Unit.for(to)
end

#convert_proc(to) ⇒ Unit

Return a proc that will perform conversion from this unit to the given one

Parameters:

  • (Symbol String Unit)

Returns:

Raises:

  • (ArgumentError)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/quantity/unit.rb', line 118

def convert_proc(to)
  to = convert(to)
  #to = Unit.for(to)
  raise ArgumentError, "Unable to find unit #{to}" unless to
  unless (to.dimension == self.dimension)
    raise ArgumentError, "Cannot convert #{self.dimension} to #{to.dimension}"
  end
  if defined?(Rational) && (@value.is_a?(Fixnum)) && (to.value.is_a?(Fixnum))
    lambda do | from |
      from * Rational(@value, to.value) 
    end
  elsif defined?(Rational) && (@value.is_a?(Rational)) && (to.value.is_a?(Rational))
    lambda do | from |
      from * @value / to.value
    end
  else
    lambda do | from |
      from * (@value / to.value.to_f)
    end
  end
end

#inspectObject



163
164
165
166
# File 'lib/quantity/unit.rb', line 163

def inspect
  sprintf('#<%s:0x%s @name=%s @value=%s @dimension=%s>', self.class.name,
            self.__id__.to_s(16), @name.inspect, @value.inspect, @dimension.inspect)
end

#names[Symbol String]

All the known aliases for this Unit, i.e. name + aliases

Returns:

  • ([Symbol String])


90
91
92
# File 'lib/quantity/unit.rb', line 90

def names
  [@name] + @aliases
end

#reduced_nameObject

A reduced form of this unit



95
96
97
# File 'lib/quantity/unit.rb', line 95

def reduced_name
  to_string_form.to_sym
end

#s_for(value) ⇒ String

A string representation of this unit at the given value

Parameters:

  • value (Any)

Returns:

  • (String)


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

def s_for(value)
  "#{value} #{@name.to_s}"
end

#string_formString

A vaguely human-readable form for this unit

Returns:

  • (String)


310
311
312
# File 'lib/quantity/unit.rb', line 310

def string_form
  self.class.string_form(@dimension,@units)
end

#value_for(reference_value) ⇒ Numeric

The value for a given reference value.

Examples:

Unit.add_unit :meter, :length, :1000
Unit.for(:meter).value_for(5000) = 5

Parameters:

  • value (Numeric)

Returns:

  • (Numeric)


146
147
148
149
150
151
152
153
154
# File 'lib/quantity/unit.rb', line 146

def value_for(reference_value)
  if defined?(Rational) && (reference_value.is_a?(Fixnum)) && (@value.is_a?(Fixnum))
    Rational(reference_value, @value)
  elsif defined?(Rational) && (reference_value.is_a?(Rational) || reference_value.is_a?(Fixnum)) && (@value.is_a?(Rational))
    reference_value / @value #Rational(reference_value, @value)
  else
    reference_value / @value.to_f
  end
end