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?(.+)?|&plusmn;|\+\/-/) @@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 =~ /(\+\/-|&plusmn;)/ @@ -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?}