lib/ruby_units/ruby-units.rb in ruby-units-1.1.0 vs lib/ruby_units/ruby-units.rb in ruby-units-1.1.2
- old
+ new
@@ -38,11 +38,11 @@
# }
# end
# Unit.setup
class Unit < Numeric
# pre-generate hashes from unit definitions for performance.
- VERSION = '1.1.0'
+ VERSION = '1.1.2'
@@USER_DEFINITIONS = {}
@@PREFIX_VALUES = {}
@@PREFIX_MAP = {}
@@UNIT_MAP = {}
@@UNIT_VALUES = {}
@@ -50,11 +50,11 @@
@@BASE_UNITS = ['<meter>','<kilogram>','<second>','<mole>', '<farad>', '<ampere>','<radian>','<kelvin>','<temp-K>','<byte>','<dollar>','<candela>','<each>','<steradian>','<decibel>']
UNITY = '<1>'
UNITY_ARRAY= [UNITY]
FEET_INCH_REGEX = /(\d+)\s*(?:'|ft|feet)\s*(\d+)\s*(?:"|in|inches)/
TIME_REGEX = /(\d+)*:(\d+)*:*(\d+)*[:,]*(\d+)*/
- LBS_OZ_REGEX = /(\d+)\s*(?:#|lbs|pounds)+[\s,]*(\d+)\s*(?:oz|ounces)/
+ LBS_OZ_REGEX = /(\d+)\s*(?:#|lbs|pounds|pound-mass)+[\s,]*(\d+)\s*(?:oz|ounces)/
SCI_NUMBER = %r{([+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*)}
RATIONAL_NUMBER = /(\d+)\/(\d+)/
COMPLEX_NUMBER = /#{SCI_NUMBER}?#{SCI_NUMBER}i\b/
NUMBER_REGEX = /#{SCI_NUMBER}*\s*(.+)?/
UNIT_STRING_REGEX = /#{SCI_NUMBER}*\s*([^\/]*)\/*(.+)*/
@@ -238,11 +238,10 @@
raise ArgumentError, "Invalid Unit Format"
end
self.update_base_scalar
raise ArgumentError, "Temperature out of range" if self.is_temperature? && self.base_scalar < 0
-
unary_unit = self.units || ""
opt_units = options[0].scan(NUMBER_REGEX)[0][1] if String === options[0]
unless @@cached_units.keys.include?(opt_units) || (opt_units =~ /(temp|deg(C|K|R|F))|(pounds|lbs[ ,]\d+ ounces|oz)|('\d+")|(ft|feet[ ,]\d+ in|inch|inches)|%|(#{TIME_REGEX})|i\s?(.+)?|±|\+\/-/)
@@cached_units[opt_units] = (self.scalar == 1 ? self : opt_units.unit) if opt_units && !opt_units.empty?
end
@@ -268,10 +267,15 @@
def self.base_unit_cache
return @@base_unit_cache
end
+ def self.parse(input)
+ first, second = input.scan(/(.+)\s(?:in|to|as)\s(.+)/i).first
+ second.nil? ? first.unit : first.unit.to(second)
+ end
+
def to_unit
self
end
alias :unit :to_unit
@@ -380,14 +384,17 @@
def inspect(option=nil)
return super() if option == :dump
self.to_s
end
+ # true if unit is a 'temperature', false if a 'degree' or anything else
def is_temperature?
return true if self.signature == 400 && self.units =~ /temp/
end
+ # returns the 'degree' unit associated with a temperature unit
+ # '100 tempC'.unit.temperature_scale #=> 'degC'
def temperature_scale
return nil unless self.is_temperature?
self.units =~ /temp(C|F|R|K)/
"deg#{$1}"
end
@@ -454,11 +461,11 @@
def +(other)
if Unit === other
case
when self.zero? : other.dup
when self =~ other :
- raise ArgumentError, "Cannot add two temperatures" if (self.is_temperature? && other.is_temperature?)
+ raise ArgumentError, "Cannot add two temperatures" if ([self, other].all? {|x| x.is_temperature?})
if [self, other].any? {|x| x.is_temperature?}
case self.is_temperature?
when true:
Unit.new(:scalar => (self.scalar + other.to(self.temperature_scale).scalar), :numerator => @numerator, :denominator=>@denominator, :signature => @signature)
else
@@ -490,11 +497,11 @@
when [self, other].all? {|x| x.is_temperature?} :
Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator => KELVIN, :denominator => UNITY_ARRAY, :signature => @signature).to(self.temperature_scale)
when self.is_temperature? :
Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator => ['<temp-K>'], :denominator => UNITY_ARRAY, :signature => @signature).to(self)
when other.is_temperature? :
- raise ArgumentError, "Cannot subtract a temperature from a differential unit"
+ raise ArgumentError, "Cannot subtract a temperature from a differential degree unit"
else
@q ||= ((@@cached_units[self.units].scalar / @@cached_units[self.units].base_scalar) rescue (self.units.unit.scalar/self.units.unit.to_base.scalar))
Unit.new(:scalar=>(self.base_scalar - other.base_scalar)*@q, :numerator=>@numerator, :denominator=>@denominator, :signature=>@signature)
end
else
@@ -550,11 +557,11 @@
# It should then convert the float to a rational and raise the unit by the numerator and root it by the denominator
# but, sadly, floats can't be converted to rationals.
#
# For now, if a rational is passed in, it will be used, otherwise we are stuck with integers and certain floats < 1
def **(other)
- raise ArgumentError, "Cannot exponentiate a temperature" if self.is_temperature?
+ raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
if Numeric === other
return Unit("1") if other.zero?
return self if other == 1
return self.inverse if other == -1
end
@@ -658,47 +665,10 @@
when 'tempK' : @base_scalar
when 'tempF' : @base_scalar * (9.0/5.0) - 459.67
when 'tempR' : @base_scalar * (9.0/5.0)
end
-=begin
- q=case start_unit
- when /\A(temp|deg)C\Z/:
- case target_unit
- when 'tempC' : @scalar
- when 'tempK' : @scalar + 273.15
- when 'tempF' : @scalar * (9.0/5.0) + 32.0
- when 'tempR' : @scalar * (9.0/5.0) + 491.67
- end
- when /\A(temp|deg)K\Z/:
- case target_unit
- when 'tempC' : @scalar - 273.15
- when 'tempK' : @scalar
- when 'tempF' : @scalar * (9.0/5.0) - 459.67
- when 'tempR' : @scalar * (9.0/5.0)
- end
- when /\A(temp|deg)F\Z/:
- case target_unit
- when 'tempC' : (@scalar-32)*(5.0/9.0)
- when 'tempK' : (@scalar+459.67)*(5.0/9.0)
- when 'tempF' : @scalar
- when 'tempR' : @scalar + 459.67
- end
- when /\A(temp|deg)R\Z/:
- case target_unit
- when 'tempC' : @scalar*(5.0/9.0) -273.15
- when 'tempK' : @scalar*(5.0/9.0)
- when 'tempF' : @scalar - 459.67
- when 'tempR' : @scalar
- end
-
- else
- return self.to_base.to(other) unless self.is_base?
- #raise ArgumentError, "Unknown temperature conversion requested #{self.numerator}"
- end
-=end
- #target_unit =~ /temp(C|K|F|R)/
Unit.new("#{q} #{target_unit}")
else
case other
when Unit:
return self if other.units == self.units
@@ -773,11 +743,10 @@
return out
end
# negates the scalar of the Unit
def -@
- #raise ArgumentError, "Cannot negate an absolute temperature" if self.is_temperature? && ['degK','degR'].include?(self.temperature_scale)
return -@scalar if self.unitless?
self.dup * -1
end
# returns abs of scalar, without the units
@@ -921,13 +890,10 @@
@base_scalar = base.scalar
@signature = base.signature
end
end
-
-
-
# calculates the unit signature vector used by unit_signature
def unit_signature_vector
return self.to_base.unit_signature_vector unless self.is_base?
result = self
vector = Array.new(SIGNATURE_VECTOR.size,0)
@@ -948,12 +914,11 @@
private
def initialize_copy(other)
@numerator = other.numerator.dup
- @denominator = other.denominator.dup
-
+ @denominator = other.denominator.dup
end
# calculates the unit signature id for use in comparing compatible units and simplification
# the signature is based on a simple classification of units and is based on the following publication
#
@@ -1031,12 +996,10 @@
def parse(passed_unit_string="0")
unit_string = passed_unit_string.dup
if unit_string =~ /\$\s*(#{NUMBER_REGEX})/
unit_string = "#{$1} USD"
end
-
-
unit_string.gsub!(/%/,'percent')
unit_string.gsub!(/'/,'feet')
unit_string.gsub!(/"/,'inch')
unit_string.gsub!(/#/,'pound')
if defined?(Uncertain) && unit_string =~ /(\+\/-|±)/
@@ -1121,10 +1084,9 @@
@numerator ||= UNITY_ARRAY
@denominator ||= UNITY_ARRAY
@numerator = top.scan(@@UNIT_MATCH_REGEX).delete_if {|x| x.empty?}.compact if top
@denominator = bottom.scan(@@UNIT_MATCH_REGEX).delete_if {|x| x.empty?}.compact if bottom
us = "#{(top || '' + bottom || '')}".to_s.gsub(@@UNIT_MATCH_REGEX,'').gsub(/[\d\*, "'_^\/\$]/,'')
-
raise( ArgumentError, "'#{passed_unit_string}' Unit not recognized") unless us.empty?
@numerator = @numerator.map do |item|
@@PREFIX_MAP[item[0]] ? [@@PREFIX_MAP[item[0]], @@UNIT_MAP[item[1]]] : [@@UNIT_MAP[item[1]]]
end.flatten.compact.delete_if {|x| x.empty?}