class Quantify::Quantity
Attributes
Public Class Methods
# File lib/quantify/quantity.rb, line 99 def self.auto_consolidate_units=(true_or_false) @auto_consolidate_units = true_or_false end
# File lib/quantify/quantity.rb, line 103 def self.auto_consolidate_units? @auto_consolidate_units.nil? ? false : @auto_consolidate_units end
# File lib/quantify/quantity.rb, line 95 def self.configure(&block) self.class_eval(&block) if block end
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 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
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
# 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
# File lib/quantify/quantity.rb, line 300 def ===(range) raise Exceptions::InvalidArgumentError unless range.is_a? Range range.cover? self end
# File lib/quantify/quantity.rb, line 230 def add(other) Quantity.new(@value,@unit).add!(other) end
# File lib/quantify/quantity.rb, line 214 def add!(other) add_or_subtract!(:+, other) end
# File lib/quantify/quantity.rb, line 305 def between?(min, max) raise NoMethodError if @value.nil? super(min,max) end
# 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
# File lib/quantify/quantity.rb, line 246 def divide(other) Quantity.new(@value,@unit).divide!(other) end
# File lib/quantify/quantity.rb, line 226 def divide!(other) multiply_or_divide!(:/, other) end
# File lib/quantify/quantity.rb, line 165 def inspect to_s end
# File lib/quantify/quantity.rb, line 240 def multiply(other) Quantity.new(@value,@unit).multiply!(other) end
# File lib/quantify/quantity.rb, line 222 def multiply!(other) multiply_or_divide!(:*, other) end
# File lib/quantify/quantity.rb, line 251 def pow(power) Quantity.new(@value,@unit).pow!(power) end
# 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
# 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
# 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
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 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
# File lib/quantify/quantity.rb, line 235 def subtract(other) Quantity.new(@value,@unit).subtract!(other) end
# File lib/quantify/quantity.rb, line 218 def subtract!(other) add_or_subtract!(:-, other) end
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
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
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
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
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
# 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
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
# 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
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
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"
# File lib/quantify/quantity.rb, line 411 def method_missing(method, *args, &block) if method.to_s =~ /(to_)(.*)/ to($2) else super end end
# 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