class Quantify::Quantity

Attributes

unit[RW]
value[RW]

Public Class Methods

auto_consolidate_units=(true_or_false) click to toggle source
# File lib/quantify/quantity.rb, line 99
def self.auto_consolidate_units=(true_or_false)
  @auto_consolidate_units = true_or_false
end
auto_consolidate_units?() click to toggle source
# File lib/quantify/quantity.rb, line 103
def self.auto_consolidate_units?
  @auto_consolidate_units.nil? ? false : @auto_consolidate_units
end
configure(&block) click to toggle source
# File lib/quantify/quantity.rb, line 95
def self.configure(&block)
  self.class_eval(&block) if block
end
new(value, unit = 'unity') click to toggle source

Initialize a new Quantity object. Two arguments are required: a value and a unit. The unit can be a an instance of Unit::Base or the name, symbol or JScience label of a known (or derivable through know units and prefixes) unit

# File lib/quantify/quantity.rb, line 125
def initialize(value, unit = 'unity')
  if value
    @value = value.to_f
  else
    @value = nil
  end
  if unit
    @unit = Unit.for(unit)
  else
    @unit = Unit.for('unity')
  end
end
parse(string,options={}) click to toggle source

Parse a string and return a Quantity object based upon the value and subseqent unit name, symbol or JScience label. Returns an array containing quantity objects for each quantity recognised.

# File lib/quantify/quantity.rb, line 47
def self.parse(string,options={})

  return [Quantity.new(nil, nil)] unless string != nil && string.strip.length > 0

  quantities   = []
  remainder    = []
  words        = string.words

  until words.empty? do
    word = words.shift
    if word.starts_with_number?
      if (Unit::QUANTITY_REGEX).match(word)
        word, other = $1, $2
        words.unshift(other)
      end
      quantities << [word]
    else
      if quantities.empty?
        remainder << word
      else
        quantities.last << word
      end
    end
  end      

  remainders = []
  remainders << remainder.join(" ")

  quantities.map! do |words|

    value = words.shift
    string = words.join(" ")

    # Parse string for unit references
    unit, remainder = Unit.parse(string, :iterative => true, :remainder => true) 
    unit, remainder = Unit.dimensionless, string if unit.nil?
    remainders << remainder

    # Instantiate quantity using value and unit
    Quantity.new(value,unit)
      
  end.compact
  return [quantities, remainders] if options[:remainder] == true
  return quantities
rescue Quantify::Exceptions::InvalidArgumentError
  raise Quantify::Exceptions::QuantityParseError, "Cannot parse string into value and unit"
end

Protected Class Methods

is_basic_conversion_with_scalings?(quantity,new_unit) click to toggle source

alias :auto_consolidate_units? :auto_consolidate_units

# File lib/quantify/quantity.rb, line 110
def self.is_basic_conversion_with_scalings?(quantity,new_unit)
  return true if (quantity.unit.has_scaling? || new_unit.has_scaling?) &&
    !quantity.unit.is_compound_unit? &&
    !new_unit.is_compound_unit?
  return false
end

Public Instance Methods

*(other)
Alias for: multiply
**(power)
Alias for: pow
+(other)
Alias for: add
-(other)
Alias for: subtract
/(other)
Alias for: divide
<=>(other) click to toggle source
# File lib/quantify/quantity.rb, line 291
def <=>(other)
  raise Exceptions::InvalidArgumentError unless other.is_a? Quantity
  raise Exceptions::InvalidArgumentError unless other.unit.is_alternative_for?(unit)
  return 0 if @value.nil? && (other.nil? || other.value.nil?)

  other = other.to @unit
  @value.to_f <=> other.value.to_f
end
===(range) click to toggle source
# File lib/quantify/quantity.rb, line 300
def ===(range)
  raise Exceptions::InvalidArgumentError unless range.is_a? Range
  range.cover? self
end
add(other) click to toggle source
# File lib/quantify/quantity.rb, line 230
def add(other)
  Quantity.new(@value,@unit).add!(other)
end
Also aliased as: +
add!(other) click to toggle source
# File lib/quantify/quantity.rb, line 214
def add!(other)
  add_or_subtract!(:+, other)
end
between?(min, max) click to toggle source
Calls superclass method
# File lib/quantify/quantity.rb, line 305
def between?(min, max)
  raise NoMethodError if @value.nil?
  super(min,max)
end
cancel_base_units!(*units) click to toggle source
# File lib/quantify/quantity.rb, line 268
def cancel_base_units!(*units)
  @unit = @unit.cancel_base_units!(*units) if @unit.is_compound_unit?
  return self
end
divide(other) click to toggle source
# File lib/quantify/quantity.rb, line 246
def divide(other)
  Quantity.new(@value,@unit).divide!(other)
end
Also aliased as: /
divide!(other) click to toggle source
# File lib/quantify/quantity.rb, line 226
def divide!(other)
  multiply_or_divide!(:/, other)
end
inspect() click to toggle source
# File lib/quantify/quantity.rb, line 165
def inspect
  to_s
end
multiply(other) click to toggle source
# File lib/quantify/quantity.rb, line 240
def multiply(other)
  Quantity.new(@value,@unit).multiply!(other)
end
Also aliased as: times, *
multiply!(other) click to toggle source
# File lib/quantify/quantity.rb, line 222
def multiply!(other)
  multiply_or_divide!(:*, other)
end
pow(power) click to toggle source
# File lib/quantify/quantity.rb, line 251
def pow(power)
  Quantity.new(@value,@unit).pow!(power)
end
Also aliased as: **
pow!(power) click to toggle source
# File lib/quantify/quantity.rb, line 207
def pow!(power)
  raise Exceptions::InvalidArgumentError, "Argument must be an integer" unless power.is_a? Integer
  @value = @value ** power
  @unit = @unit ** power
  return self
end
rationalize_units() click to toggle source
# File lib/quantify/quantity.rb, line 256
def rationalize_units
  return self unless @unit.is_a? Unit::Compound
  self.to @unit.clone.rationalize_base_units!
end
rationalize_units!() click to toggle source
# File lib/quantify/quantity.rb, line 261
def rationalize_units!
  rationalized_quantity = self.rationalize_units
  @value = rationalized_quantity.value
  @unit = rationalized_quantity.unit
  return self
end
represents() click to toggle source

Returns a description of what the quantity describes, based upon the physica quantity which is represented by the Dimensions object in the Quantity unit. e.g.

Quantity.parse("25 yr").represents            #=> :time

1.foot.represents                             #=> :length

Quantity.new(123.456, :degree_celsius).represents
                                              #=> :temperature
# File lib/quantify/quantity.rb, line 148
def represents
  @unit.measures
end
round(decimal_places=0) click to toggle source

Similar to round! but returns new Quantity instance rather than rounding in place

# File lib/quantify/quantity.rb, line 286
def round(decimal_places=0)
  rounded_quantity = Quantity.new @value, @unit
  rounded_quantity.round! decimal_places
end
round!(decimal_places=0) click to toggle source

Round the value attribute to the specified number of decimal places. If no argument is given, the value is rounded to NO decimal places, i.e. to an integer

# File lib/quantify/quantity.rb, line 277
def round!(decimal_places=0)
  factor = ( decimal_places == 0 ? 1 : 10.0 ** decimal_places )
  @value = (@value * factor).round / factor
  self
end
subtract(other) click to toggle source
# File lib/quantify/quantity.rb, line 235
def subtract(other)
  Quantity.new(@value,@unit).subtract!(other)
end
Also aliased as: -
subtract!(other) click to toggle source
# File lib/quantify/quantity.rb, line 218
def subtract!(other)
  add_or_subtract!(:-, other)
end
times(other)
Alias for: multiply
to(new_unit) click to toggle source

Converts self into a quantity using the unit provided as an argument. The new unit must represent the same physical quantity, i.e. have the same dimensions, e.g.

Quantity.parse("12 yd").to(:foot).to_s          #=> "36 ft"

1000.kilogram.to(:tonne).to_s                   #=> "1 t"

The method method_missing provides some syntactic sugar for the new unit to be provided as part of the method name, based around /to_(<unit>)/, e.g.

200.cm.to_metre.to_s                   #=> "1 t"

The unit value is converted to the corresponding value for the same quantity in terms of the new unit.

# File lib/quantify/quantity.rb, line 185
def to(new_unit)
  new_unit = Unit.for new_unit
  if Quantity.is_basic_conversion_with_scalings?(self,new_unit)
    Quantity.new(@value,@unit).conversion_with_scalings! new_unit
  elsif self.unit.is_alternative_for? new_unit
    Quantity.new(@value,@unit).convert_to_equivalent_unit! new_unit
  elsif self.unit.is_compound_unit?
    Quantity.new(@value,@unit).convert_compound_unit_to_non_equivalent_unit! new_unit
  else
    nil # raise? or ...
  end      
end
to_s(format=:symbol) click to toggle source

Returns a string representation of the quantity, using the unit symbol

# File lib/quantify/quantity.rb, line 153
def to_s format=:symbol
  if format == :name
    if @value == 1 || @value == -1
      "#{@value} #{@unit.name}"
    else
      "#{@value} #{@unit.pluralized_name}"
    end
  else
    "#{@value} #{@unit.send format}"
  end
end
to_si() click to toggle source

Converts a quantity to the equivalent quantity using only SI units

# File lib/quantify/quantity.rb, line 199
def to_si
  if @unit.is_compound_unit?
    Quantity.new(@value,@unit).convert_compound_unit_to_si!
  else
    self.to(@unit.si_unit)
  end
end

Protected Instance Methods

add_or_subtract!(operator,other) click to toggle source

Quantities must be of the same dimension in order to operate. If they are represented by different units (but represent the same physical quantity) the second quantity is converted into the unit belonging to the first unit and the addition is completed

# File lib/quantify/quantity.rb, line 317
def add_or_subtract!(operator,other)
  if other.is_a? Quantity
    other = other.to(@unit) if other.unit.is_alternative_for?(@unit)
    if @unit.is_equivalent_to? other.unit
      @value = @value.send operator, other.value
      return self
    else
      raise Quantify::Exceptions::InvalidObjectError, "Cannot add or subtract Quantities with different dimensions"
    end
  else
    raise Quantify::Exceptions::InvalidObjectError, "Cannot add or subtract non-Quantity objects"
  end
end
coerce(object) click to toggle source

Enables shorthand for reciprocal of quantity, e.g.

quantity = 2.m

(1/quantity).to_s :name                 #=> "0.5 per metre"
# File lib/quantify/quantity.rb, line 397
def coerce(object)
  if object.kind_of? Numeric
    return Quantity.new(object, Unit.unity), self
  else
    raise Exceptions::InvalidArgumentError, "Cannot coerce #{self.class} into #{object.class}"
  end
end
conversion_with_scalings!(new_unit) click to toggle source
# File lib/quantify/quantity.rb, line 360
def conversion_with_scalings!(new_unit)
  @value = (((@value + @unit.scaling) * @unit.factor) / new_unit.factor) - new_unit.scaling
  @unit = new_unit
  return self
end
convert_compound_unit_to_non_equivalent_unit!(new_unit) click to toggle source

Conversion where self is a compound unit, and new unit is not an alternative to the whole compound but IS an alternative to one or more of the base units, e.g.,

Unit.kilowatt_hour.to :second           #=> 'kilowatt second'
# File lib/quantify/quantity.rb, line 372
def convert_compound_unit_to_non_equivalent_unit!(new_unit)
  @unit.base_units.select do |base|
    base.unit.is_alternative_for? new_unit
  end.inject(self) do |quantity,base|
    factor = Unit.ratio(new_unit**base.index, base.unit**base.index)
    quantity.multiply!(factor).cancel_base_units!(base.unit)
  end
end
convert_compound_unit_to_si!() click to toggle source
# File lib/quantify/quantity.rb, line 381
def convert_compound_unit_to_si!
  until @unit.is_base_quantity_si_unit? do
    unit = @unit.base_units.find do |base|
      !base.is_base_quantity_si_unit?
    end.unit
    self.convert_compound_unit_to_non_equivalent_unit!(unit.si_unit)
  end
  return self
end
convert_to_equivalent_unit!(new_unit) click to toggle source

Conversion where both units (including compound units) are of precisely equivalent dimensions, i.e. direct alternatives for one another. Where previous unit is a compound unit, new unit must be cancelled by all original base units

# File lib/quantify/quantity.rb, line 353
def convert_to_equivalent_unit!(new_unit)
  old_unit = @unit
  self.multiply!(Unit.ratio new_unit, old_unit)
  old_base_units = old_unit.base_units.map { |base| base.unit } if old_unit.is_compound_unit?
  self.cancel_base_units!(*old_base_units || [old_unit])
end
method_missing(method, *args, &block) click to toggle source

Dynamic method for converting to another unit, e.g

2.ft.to_metre.to_s                           #=> "0.6096 m"

30.degree_celsius.to_K.to_s :name            #=> "303.15 kelvins"
Calls superclass method
# File lib/quantify/quantity.rb, line 411
def method_missing(method, *args, &block)
  if method.to_s =~ /(to_)(.*)/
    to($2)
  else
    super
  end
end
multiply_or_divide!(operator,other) click to toggle source
# File lib/quantify/quantity.rb, line 331
def multiply_or_divide!(operator,other)
  if other.kind_of? Numeric
    raise ZeroDivisionError if (other.to_f == 0.0 && operator == :/)
    @value = @value.send(operator,other)

    return self
  elsif other.kind_of? Quantity
    @unit = @unit.send(operator,other.unit).or_equivalent
    @unit.consolidate_base_units! if @unit.is_compound_unit? && Quantity.auto_consolidate_units?
    @value = @value.send(operator,other.value)
    
    return self
  else
    raise Quantify::Exceptions::InvalidArgumentError, "Cannot multiply or divide a Quantity by a non-Quantity or non-Numeric object"
  end
end