Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

allow temperatures to work when the display name is changed #44

Merged
merged 1 commit into from

2 participants

@twalpole

Some changes so that temperature units are still handled correctly when their display names are changed

@olbrich olbrich merged commit 52ee093 into olbrich:master
@olbrich
Owner

Good catch, thanks!

@olbrich olbrich referenced this pull request from a commit
@olbrich * fix specs from issue #44 because they weren't cleaning up properly …
…after themselves

* remove all hard coded temperature handling from unit.rb
* generate regexes required to detect temperatures and degrees
* push temperature conversion routines into procs on the Unit definition.  This makes it possible to introduce arbitrary (and non-linear) temperature scales.
9b93661
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 121 additions and 18 deletions.
  1. +27 −18 lib/ruby_units/unit.rb
  2. +94 −0 spec/ruby-units/temperature_spec.rb
View
45 lib/ruby_units/unit.rb
@@ -59,8 +59,7 @@ class Unit < Numeric
FAHRENHEIT = ['<fahrenheit>']
RANKINE = ['<rankine>']
CELSIUS = ['<celsius>']
- TEMP_REGEX = /(?:temp|deg)[CFRK]/
-
+ @@TEMP_REGEX = nil
SIGNATURE_VECTOR = [
:length,
:time,
@@ -191,7 +190,7 @@ def self.define(unit_definition, &block)
Unit.use_definition(unit_definition)
return unit_definition
end
-
+
# @param [String] name Name of unit to redefine
# @param [Block] block
# @raise [ArgumentError] if a block is not given
@@ -370,11 +369,11 @@ def initialize(*options)
unary_unit = self.units || ""
if options.first.instance_of?(String)
opt_scalar, opt_units = Unit.parse_into_numbers_and_units(options[0])
- unless @@cached_units.keys.include?(opt_units) || (opt_units =~ /(#{TEMP_REGEX})|(pounds|lbs[ ,]\d+ ounces|oz)|('\d+")|(ft|feet[ ,]\d+ in|inch|inches)|%|(#{TIME_REGEX})|i\s?(.+)?|&plusmn;|\+\/-/)
+ unless @@cached_units.keys.include?(opt_units) || (opt_units =~ /(#{Unit.temp_regex})|(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
end
- unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /#{TEMP_REGEX}/) then
+ unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /#{Unit.temp_regex}/) then
@@cached_units[unary_unit] = (self.scalar == 1 ? self : unary_unit.unit)
end
[@scalar, @numerator, @denominator, @base_scalar, @signature, @is_base].each {|x| x.freeze}
@@ -441,7 +440,7 @@ def is_base?
# @todo this is brittle as it depends on the display_name of a unit, which can be changed
def to_base
return self if self.is_base?
- if self.units =~ /\A(?:temp|deg)[CRF]\Z/
+ if @@UNIT_MAP[self.units] =~ /\A<(?:temp|deg)[CRF]>\Z/
if RUBY_VERSION < "1.9"
# :nocov_19:
@signature = @@KINDS.index(:temperature)
@@ -563,7 +562,7 @@ def inspect(option=nil)
# @return [Boolean]
# @todo use unit definition to determine if it's a temperature instead of a regex
def is_temperature?
- return self.is_degree? && (!(self.units =~ /temp[CFRK]/).nil?)
+ return self.is_degree? && (!(@@UNIT_MAP[self.units] =~ /temp[CFRK]/).nil?)
end
alias :temperature? :is_temperature?
@@ -579,7 +578,7 @@ def is_degree?
# @return [String] possible values: degC, degF, degR, or degK
def temperature_scale
return nil unless self.is_temperature?
- return "deg#{self.units[/temp([CFRK])/,1]}"
+ return "deg#{@@UNIT_MAP[self.units][/temp([CFRK])/,1]}"
end
# returns true if no associated units
@@ -946,25 +945,25 @@ def convert_to(other)
start_unit = self.units
target_unit = other.units rescue other
unless @base_scalar
- @base_scalar = case start_unit
- when 'tempC'
+ @base_scalar = case @@UNIT_MAP[start_unit]
+ when '<tempC>'
@scalar + 273.15
- when 'tempK'
+ when '<tempK>'
@scalar
- when 'tempF'
+ when '<tempF>'
(@scalar+459.67)*Rational(5,9)
- when 'tempR'
+ when '<tempR>'
@scalar*Rational(5,9)
end
end
- q= case target_unit
- when 'tempC'
+ q= case @@UNIT_MAP[target_unit]
+ when '<tempC>'
@base_scalar - 273.15
- when 'tempK'
+ when '<tempK>'
@base_scalar
- when 'tempF'
+ when '<tempF>'
@base_scalar * Rational(9,5) - 459.67
- when 'tempR'
+ when '<tempR>'
@base_scalar * Rational(9,5)
end
return Unit.new("#{q} #{target_unit}")
@@ -1537,10 +1536,20 @@ def self.prefix_regex
return @@PREFIX_REGEX ||= @@PREFIX_MAP.keys.sort_by {|prefix| [prefix.length, prefix]}.reverse.join('|')
end
+ def self.temp_regex
+ @@TEMP_REGEX ||= Regexp.new "(?:#{
+ temp_units=%w(tempK tempC tempF tempR degK degC degF degR)
+ aliases=temp_units.map{|unit| d=Unit.definition(unit); d && d.aliases}.flatten.compact
+ regex_str= aliases.empty? ? '(?!x)x' : aliases.join('|')
+ regex_str
+ })"
+ end
+
# inject a definition into the internal array and set it up for use
# @private
def self.use_definition(definition)
@@UNIT_MATCH_REGEX = nil #invalidate the unit match regex
+ @@TEMP_REGEX = nil #invalidate the temp regex
if definition.prefix?
@@PREFIX_VALUES[definition.name] = definition.scalar
definition.aliases.each {|_alias| @@PREFIX_MAP[_alias] = definition.name }
View
94 spec/ruby-units/temperature_spec.rb
@@ -0,0 +1,94 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe 'temperatures' do
+ describe 'redfine display name' do
+ before(:all) do
+ Unit.redefine!("tempC") do |c|
+ c.aliases = %w{tC tempC}
+ c.display_name = "tC"
+ end
+
+ Unit.redefine!("tempF") do |f|
+ f.aliases = %w{tF tempF}
+ f.display_name = "tF"
+ end
+
+ Unit.redefine!("tempR") do |f|
+ f.aliases = %w{tR tempR}
+ f.display_name = "tR"
+ end
+
+ Unit.redefine!("tempK") do |f|
+ f.aliases = %w{tK tempK}
+ f.display_name = "tK"
+ end
+ end
+
+ after(:all) do
+ #define the temp units back to normal
+ Unit.define("tempK") do |unit|
+ unit.scalar = 1
+ unit.numerator = %w{<tempK>}
+ unit.aliases = %w{tempK}
+ unit.kind = :temperature
+ end
+
+ Unit.define('tempC') do |tempC|
+ tempC.definition = Unit('1 tempK')
+ end
+
+ temp_convert_factor = Rational(2501999792983609,4503599627370496) # approximates 1/1.8
+
+ Unit.define('tempF') do |tempF|
+ tempF.definition = Unit(temp_convert_factor, 'tempK')
+ end
+
+ Unit.define('tempR') do |tempR|
+ tempR.definition = Unit('1 tempF')
+ end
+ end
+
+ describe "Unit('100 tC')" do
+ subject {Unit("100 tC")}
+ its(:scalar) {should be_within(0.001).of 100}
+ its(:units) {should == "tC"}
+ its(:kind) {should == :temperature}
+ it {should be_temperature}
+ it {should be_degree}
+ it {should_not be_base}
+ it {should_not be_unitless}
+ it {should_not be_zero}
+ its(:base) {should be_within(Unit("0.01 degK")).of Unit("373.15 tempK")}
+ its(:temperature_scale) {should == "degC"}
+ end
+
+ context "between temperature scales" do
+ # note that 'temp' units are for temperature readings on a scale, while 'deg' units are used to represent
+ # differences between temperatures, offsets, or other differential temperatures.
+
+ specify { Unit("100 tC").should be_within(Unit("0.001 degK")).of(Unit("373.15 tempK")) }
+ specify { Unit("0 tC").should be_within(Unit("0.001 degK")).of(Unit("273.15 tempK")) }
+ specify { Unit("37 tC").should be_within(Unit("0.01 degK")).of(Unit("310.15 tempK"))}
+ specify { Unit("-273.15 tC").should == Unit("0 tempK") }
+
+ specify { Unit("212 tF").should be_within(Unit("0.001 degK")).of(Unit("373.15 tempK")) }
+ specify { Unit("32 tF").should be_within(Unit("0.001 degK")).of(Unit("273.15 tempK")) }
+ specify { Unit("98.6 tF").should be_within(Unit("0.01 degK")).of(Unit("310.15 tempK"))}
+ specify { Unit("-459.67 tF").should == Unit("0 tempK") }
+
+ specify { Unit("671.67 tR").should be_within(Unit("0.001 degK")).of(Unit("373.15 tempK")) }
+ specify { Unit("491.67 tR").should be_within(Unit("0.001 degK")).of(Unit("273.15 tempK")) }
+ specify { Unit("558.27 tR").should be_within(Unit("0.01 degK")).of(Unit("310.15 tempK"))}
+ specify { Unit("0 tR").should == Unit("0 tempK") }
+
+ specify { Unit("100 tK").convert_to("tempC").should be_within(U"0.01 degC").of(Unit("-173.15 tempC"))}
+ specify { Unit("100 tK").convert_to("tempF").should be_within(U"0.01 degF").of(Unit("-279.67 tempF"))}
+ specify { Unit("100 tK").convert_to("tempR").should be_within(U"0.01 degR").of(Unit("180 tempR"))}
+ end
+
+
+
+
+ end
+end
+
Something went wrong with that request. Please try again.