ruby-units 1.1.5 → 1.2.0.a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +2 -0
- data/lib/ruby-units.rb +1 -1
- data/lib/ruby_units.rb +1 -1
- data/lib/ruby_units/complex.rb +1 -1
- data/lib/ruby_units/date.rb +14 -8
- data/lib/ruby_units/math.rb +46 -32
- data/lib/ruby_units/ruby-units.rb +170 -126
- data/lib/ruby_units/string.rb +33 -9
- data/lib/ruby_units/time.rb +9 -12
- data/test/test_ruby-units.rb +111 -55
- metadata +24 -30
data/CHANGELOG.txt
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
Change Log for Ruby-units
|
2
2
|
=========================
|
3
|
+
2010-11-07 1.2.0.a * a bunch of fixes to make ruby-units ruby 1.9 compatible
|
4
|
+
(ruby 1.9.3dev (2010-11-07 trunk 29711) [i386-darwin9.8.0])
|
3
5
|
2010-03-16 1.1.5 * another bugfix, and update url to point to github
|
4
6
|
2010-03-15 1.1.4 * fixed a couple of outstanding bugs
|
5
7
|
2007-12-13 1.1.3 * fixed a minor bug with string %
|
data/lib/ruby-units.rb
CHANGED
@@ -2,10 +2,10 @@ require 'ruby_units/array'
|
|
2
2
|
require 'ruby_units/date'
|
3
3
|
require 'ruby_units/time'
|
4
4
|
require 'ruby_units/math'
|
5
|
+
require 'ruby_units/complex'
|
5
6
|
require 'ruby_units/numeric'
|
6
7
|
require 'ruby_units/object'
|
7
8
|
require 'ruby_units/string'
|
8
|
-
require 'ruby_units/complex'
|
9
9
|
require 'ruby_units/units'
|
10
10
|
require 'ruby_units/ruby-units'
|
11
11
|
|
data/lib/ruby_units.rb
CHANGED
@@ -2,10 +2,10 @@ require 'ruby_units/array'
|
|
2
2
|
require 'ruby_units/date'
|
3
3
|
require 'ruby_units/time'
|
4
4
|
require 'ruby_units/math'
|
5
|
+
require 'ruby_units/complex'
|
5
6
|
require 'ruby_units/numeric'
|
6
7
|
require 'ruby_units/object'
|
7
8
|
require 'ruby_units/string'
|
8
|
-
require 'ruby_units/complex'
|
9
9
|
require 'ruby_units/units'
|
10
10
|
require 'ruby_units/ruby-units'
|
11
11
|
|
data/lib/ruby_units/complex.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Complex < Numeric
|
2
2
|
def to_unit(other = nil)
|
3
3
|
real_unit = self.real.to_unit
|
4
|
-
image_unit = self.
|
4
|
+
image_unit = self.imaginary.to_unit
|
5
5
|
raise ArgumentError, 'Units on real and imaginary parts are incompatible' unless real_unit =~ image_unit
|
6
6
|
final_unit = (real_unit.units.empty? ? image_unit.units : real_unit.units).to_unit
|
7
7
|
final_unit * Complex(real_unit.to(final_unit).scalar, image_unit.to(final_unit).scalar)
|
data/lib/ruby_units/date.rb
CHANGED
@@ -7,10 +7,11 @@ class Date
|
|
7
7
|
alias :unit_date_add :+
|
8
8
|
def +(unit)
|
9
9
|
case unit
|
10
|
-
when Unit
|
10
|
+
when Unit
|
11
11
|
unit = unit.to('d').round if ['y', 'decade', 'century'].include? unit.units
|
12
12
|
unit_date_add(unit.to('day').scalar)
|
13
|
-
when Time
|
13
|
+
when Time
|
14
|
+
unit_date_add(unit.to_datetime)
|
14
15
|
else
|
15
16
|
unit_date_add(unit)
|
16
17
|
end
|
@@ -20,10 +21,11 @@ class Date
|
|
20
21
|
alias :unit_date_sub :-
|
21
22
|
def -(unit)
|
22
23
|
case unit
|
23
|
-
when Unit
|
24
|
+
when Unit
|
24
25
|
unit = unit.to('d').round if ['y', 'decade', 'century'].include? unit.units
|
25
26
|
unit_date_sub(unit.to('day').scalar)
|
26
|
-
when Time
|
27
|
+
when Time
|
28
|
+
unit_date_sub(unit.to_datetime)
|
27
29
|
else
|
28
30
|
unit_date_sub(unit)
|
29
31
|
end
|
@@ -34,8 +36,10 @@ class Date
|
|
34
36
|
end
|
35
37
|
alias :unit :to_unit
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
+
unless Date.instance_methods.include?(:to_time)
|
40
|
+
def to_time
|
41
|
+
Time.local(*ParseDate.parsedate(self.to_s))
|
42
|
+
end
|
39
43
|
end
|
40
44
|
|
41
45
|
alias :units_datetime_inspect :inspect
|
@@ -44,8 +48,10 @@ class Date
|
|
44
48
|
self.to_s
|
45
49
|
end
|
46
50
|
|
47
|
-
|
48
|
-
|
51
|
+
unless Date.instance_methods.include?(:to_date)
|
52
|
+
def to_date
|
53
|
+
Date.civil(self.year, self.month, self.day)
|
54
|
+
end
|
49
55
|
end
|
50
56
|
|
51
57
|
end
|
data/lib/ruby_units/math.rb
CHANGED
@@ -1,47 +1,76 @@
|
|
1
1
|
# Math will convert unit objects to radians and then attempt to use the value for
|
2
2
|
# trigonometric functions.
|
3
|
+
require 'mathn'
|
3
4
|
|
4
5
|
module Math
|
5
|
-
|
6
|
+
|
7
|
+
alias :unit_sqrt :sqrt
|
6
8
|
def sqrt(n)
|
7
9
|
if Unit === n
|
8
|
-
(n**(1
|
10
|
+
(n**(Rational(1,2))).to_unit
|
9
11
|
else
|
10
12
|
unit_sqrt(n)
|
11
13
|
end
|
12
14
|
end
|
15
|
+
module_function :unit_sqrt
|
16
|
+
module_function :sqrt
|
17
|
+
|
18
|
+
if self.respond_to?(:cbrt)
|
19
|
+
alias :unit_cbrt :cbrt
|
20
|
+
def cbrt(n)
|
21
|
+
if Unit === n
|
22
|
+
(n**(Rational(1,3))).to_unit
|
23
|
+
else
|
24
|
+
unit_cbrt(n)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
module_function :unit_cbrt
|
28
|
+
module_function :cbrt
|
29
|
+
end
|
13
30
|
|
14
|
-
alias unit_sin sin
|
31
|
+
alias :unit_sin :sin
|
15
32
|
def sin(n)
|
16
33
|
Unit === n ? unit_sin(n.to('radian').scalar) : unit_sin(n)
|
17
34
|
end
|
35
|
+
module_function :unit_sin
|
36
|
+
module_function :sin
|
18
37
|
|
19
|
-
alias unit_cos cos
|
38
|
+
alias :unit_cos :cos
|
20
39
|
def cos(n)
|
21
40
|
Unit === n ? unit_cos(n.to('radian').scalar) : unit_cos(n)
|
22
41
|
end
|
42
|
+
module_function :unit_cos
|
43
|
+
module_function :cos
|
23
44
|
|
24
|
-
alias unit_sinh sinh
|
45
|
+
alias :unit_sinh :sinh
|
25
46
|
def sinh(n)
|
26
47
|
Unit === n ? unit_sinh(n.to('radian').scalar) : unit_sinh(n)
|
27
48
|
end
|
49
|
+
module_function :unit_sinh
|
50
|
+
module_function :sinh
|
28
51
|
|
29
|
-
alias unit_cosh cosh
|
52
|
+
alias :unit_cosh :cosh
|
30
53
|
def cosh(n)
|
31
54
|
Unit === n ? unit_cosh(n.to('radian').scalar) : unit_cosh(n)
|
32
55
|
end
|
56
|
+
module_function :unit_cosh
|
57
|
+
module_function :cosh
|
33
58
|
|
34
|
-
alias unit_tan tan
|
59
|
+
alias :unit_tan :tan
|
35
60
|
def tan(n)
|
36
61
|
Unit === n ? unit_tan(n.to('radian').scalar) : unit_tan(n)
|
37
62
|
end
|
63
|
+
module_function :tan
|
64
|
+
module_function :unit_tan
|
38
65
|
|
39
|
-
alias unit_tanh tanh
|
66
|
+
alias :unit_tanh :tanh
|
40
67
|
def tanh(n)
|
41
68
|
Unit === n ? unit_tanh(n.to('radian').scalar) : unit_tanh(n)
|
42
69
|
end
|
70
|
+
module_function :unit_tanh
|
71
|
+
module_function :tanh
|
43
72
|
|
44
|
-
alias unit_hypot hypot
|
73
|
+
alias :unit_hypot :hypot
|
45
74
|
# Convert parameters to consistent units and perform the function
|
46
75
|
def hypot(x,y)
|
47
76
|
if Unit === x && Unit === y
|
@@ -50,38 +79,23 @@ module Math
|
|
50
79
|
unit_hypot(x,y)
|
51
80
|
end
|
52
81
|
end
|
82
|
+
module_function :unit_hypot
|
83
|
+
module_function :hypot
|
53
84
|
|
54
|
-
alias unit_atan2 atan2
|
85
|
+
alias :unit_atan2 :atan2
|
55
86
|
def atan2(x,y)
|
56
87
|
case
|
57
88
|
when (Unit === x && Unit === y) && (x !~ y)
|
58
89
|
raise ArgumentError, "Incompatible Units"
|
59
90
|
when (Unit === x && Unit === y) && (x =~ y)
|
60
|
-
unit_atan2(x.base_scalar, y.base_scalar)
|
91
|
+
Math::unit_atan2(x.base_scalar, y.base_scalar)
|
61
92
|
when (Unit === x || Unit === y)
|
62
93
|
raise ArgumentError, "Incompatible Units"
|
63
94
|
else
|
64
|
-
unit_atan2(x,y)
|
95
|
+
Math::unit_atan2(x,y)
|
65
96
|
end
|
66
97
|
end
|
67
|
-
|
68
|
-
|
69
|
-
module_function :hypot
|
70
|
-
module_function :unit_sqrt
|
71
|
-
module_function :sqrt
|
72
|
-
module_function :unit_sin
|
73
|
-
module_function :sin
|
74
|
-
module_function :unit_cos
|
75
|
-
module_function :cos
|
76
|
-
module_function :unit_sinh
|
77
|
-
module_function :sinh
|
78
|
-
module_function :unit_cosh
|
79
|
-
module_function :cosh
|
80
|
-
module_function :unit_tan
|
81
|
-
module_function :tan
|
82
|
-
module_function :unit_tanh
|
83
|
-
module_function :tanh
|
84
|
-
module_function :unit_atan2
|
85
|
-
module_function :atan2
|
98
|
+
module_function :unit_atan2
|
99
|
+
module_function :atan2
|
86
100
|
|
87
|
-
end
|
101
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require 'mathn'
|
2
|
-
require 'rational'
|
3
1
|
require 'date'
|
4
|
-
|
5
|
-
|
2
|
+
if RUBY_VERSION < "1.9"
|
3
|
+
require 'parsedate'
|
4
|
+
require 'rational'
|
5
|
+
end
|
6
6
|
# = Ruby Units
|
7
7
|
#
|
8
|
-
# Copyright 2006 by Kevin C. Olbrich, Ph.D.
|
8
|
+
# Copyright 2006-2010 by Kevin C. Olbrich, Ph.D.
|
9
9
|
#
|
10
10
|
# See http://rubyforge.org/ruby-units/
|
11
11
|
#
|
@@ -40,7 +40,7 @@ require 'parsedate'
|
|
40
40
|
# Unit.setup
|
41
41
|
class Unit < Numeric
|
42
42
|
# pre-generate hashes from unit definitions for performance.
|
43
|
-
VERSION = '1.
|
43
|
+
VERSION = '1.2.0.a'
|
44
44
|
@@USER_DEFINITIONS = {}
|
45
45
|
@@PREFIX_VALUES = {}
|
46
46
|
@@PREFIX_MAP = {}
|
@@ -133,12 +133,11 @@ class Unit < Numeric
|
|
133
133
|
@@OUTPUT_MAP[key]=value[0][0]
|
134
134
|
end
|
135
135
|
@@PREFIX_REGEX = @@PREFIX_MAP.keys.sort_by {|prefix| [prefix.length, prefix]}.reverse.join('|')
|
136
|
-
@@UNIT_REGEX = @@UNIT_MAP.keys.sort_by {|
|
136
|
+
@@UNIT_REGEX = @@UNIT_MAP.keys.sort_by {|unit_name| [unit_name.length, unit]}.reverse.join('|')
|
137
137
|
@@UNIT_MATCH_REGEX = /(#{@@PREFIX_REGEX})*?(#{@@UNIT_REGEX})\b/
|
138
138
|
Unit.new(1)
|
139
139
|
end
|
140
140
|
|
141
|
-
|
142
141
|
include Comparable
|
143
142
|
attr_accessor :scalar, :numerator, :denominator, :signature, :base_scalar, :base_numerator, :base_denominator, :output, :unit_name
|
144
143
|
|
@@ -164,11 +163,13 @@ class Unit < Numeric
|
|
164
163
|
|
165
164
|
# basically a copy of the basic to_yaml. Needed because otherwise it ends up coercing the object to a string
|
166
165
|
# before YAML'izing it.
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
166
|
+
if RUBY_VERSION < "1.9"
|
167
|
+
def to_yaml( opts = {} )
|
168
|
+
YAML::quick_emit( object_id, opts ) do |out|
|
169
|
+
out.map( taguri, to_yaml_style ) do |map|
|
170
|
+
for m in to_yaml_properties do
|
171
|
+
map.add( m[1..-1], instance_variable_get( m ) )
|
172
|
+
end
|
172
173
|
end
|
173
174
|
end
|
174
175
|
end
|
@@ -202,6 +203,8 @@ class Unit < Numeric
|
|
202
203
|
return
|
203
204
|
end
|
204
205
|
if options.size == 3
|
206
|
+
options[1] = options[1].join if options[1].kind_of?(Array)
|
207
|
+
options[2] = options[2].join if options[2].kind_of?(Array)
|
205
208
|
begin
|
206
209
|
cached = @@cached_units["#{options[1]}/#{options[2]}"] * options[0]
|
207
210
|
copy(cached)
|
@@ -212,28 +215,28 @@ class Unit < Numeric
|
|
212
215
|
end
|
213
216
|
|
214
217
|
case options[0]
|
215
|
-
when Hash
|
218
|
+
when Hash
|
216
219
|
@scalar = options[0][:scalar] || 1
|
217
220
|
@numerator = options[0][:numerator] || UNITY_ARRAY
|
218
221
|
@denominator = options[0][:denominator] || UNITY_ARRAY
|
219
222
|
@signature = options[0][:signature]
|
220
|
-
when Array
|
223
|
+
when Array
|
221
224
|
initialize(*options[0])
|
222
225
|
return
|
223
|
-
when Numeric
|
226
|
+
when Numeric
|
224
227
|
@scalar = options[0]
|
225
228
|
@numerator = @denominator = UNITY_ARRAY
|
226
|
-
when Time
|
229
|
+
when Time
|
227
230
|
@scalar = options[0].to_f
|
228
231
|
@numerator = ['<second>']
|
229
232
|
@denominator = UNITY_ARRAY
|
230
|
-
when DateTime
|
233
|
+
when DateTime
|
231
234
|
@scalar = options[0].ajd
|
232
235
|
@numerator = ['<day>']
|
233
236
|
@denominator = UNITY_ARRAY
|
234
|
-
when ""
|
237
|
+
when ""
|
235
238
|
raise ArgumentError, "No Unit Specified"
|
236
|
-
when String
|
239
|
+
when String
|
237
240
|
parse(options[0])
|
238
241
|
else
|
239
242
|
raise ArgumentError, "Invalid Unit Format"
|
@@ -284,7 +287,7 @@ class Unit < Numeric
|
|
284
287
|
# Returns 'true' if the Unit is represented in base units
|
285
288
|
def is_base?
|
286
289
|
return @is_base if defined? @is_base
|
287
|
-
return @is_base=true if
|
290
|
+
return @is_base=true if self.degree? && self.numerator.size == 1 && self.denominator == UNITY_ARRAY && self.units =~ /(deg|temp)K/
|
288
291
|
n = @numerator + @denominator
|
289
292
|
for x in n.compact do
|
290
293
|
return @is_base=false unless x == UNITY || (@@BASE_UNITS.include?((x)))
|
@@ -296,40 +299,42 @@ class Unit < Numeric
|
|
296
299
|
# results of the conversion are cached so subsequent calls to this will be fast
|
297
300
|
def to_base
|
298
301
|
return self if self.is_base?
|
299
|
-
|
302
|
+
if self.units =~ /\A(deg|temp)(C|F|K|C)\Z/
|
300
303
|
@signature = 400
|
301
304
|
base = case self.units
|
302
|
-
when /temp/
|
303
|
-
|
305
|
+
when /temp/
|
306
|
+
self.to('tempK')
|
307
|
+
when /deg/
|
308
|
+
self.to('degK')
|
304
309
|
end
|
305
310
|
return base
|
306
311
|
end
|
307
312
|
|
308
313
|
cached = ((@@base_unit_cache[self.units] * self.scalar) rescue nil)
|
309
314
|
return cached if cached
|
310
|
-
|
315
|
+
|
311
316
|
num = []
|
312
317
|
den = []
|
313
318
|
q = 1
|
314
319
|
for unit in @numerator.compact do
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
320
|
+
if @@PREFIX_VALUES[unit]
|
321
|
+
q *= @@PREFIX_VALUES[unit]
|
322
|
+
else
|
323
|
+
q *= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
|
324
|
+
num << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
|
325
|
+
den << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
|
326
|
+
end
|
322
327
|
end
|
323
328
|
for unit in @denominator.compact do
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
329
|
+
if @@PREFIX_VALUES[unit]
|
330
|
+
q /= @@PREFIX_VALUES[unit]
|
331
|
+
else
|
332
|
+
q /= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
|
333
|
+
den << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
|
334
|
+
num << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
|
335
|
+
end
|
331
336
|
end
|
332
|
-
|
337
|
+
|
333
338
|
num = num.flatten.compact
|
334
339
|
den = den.flatten.compact
|
335
340
|
num = UNITY_ARRAY if num.empty?
|
@@ -356,15 +361,15 @@ class Unit < Numeric
|
|
356
361
|
return out
|
357
362
|
else
|
358
363
|
case target_units
|
359
|
-
when :ft
|
364
|
+
when :ft
|
360
365
|
inches = self.to("in").scalar.to_int
|
361
366
|
out = "#{(inches / 12).truncate}\'#{(inches % 12).round}\""
|
362
|
-
when :lbs
|
367
|
+
when :lbs
|
363
368
|
ounces = self.to("oz").scalar.to_int
|
364
369
|
out = "#{(ounces / 16).truncate} lbs, #{(ounces % 16).round} oz"
|
365
370
|
when String
|
366
371
|
out = case target_units
|
367
|
-
when /(%[
|
372
|
+
when /(%[\-+\.\w#]+)\s*(.+)*/ #format string like '%0.2f in'
|
368
373
|
begin
|
369
374
|
if $2 #unit specified, need to convert
|
370
375
|
self.to($2).to_s($1)
|
@@ -381,7 +386,7 @@ class Unit < Numeric
|
|
381
386
|
end
|
382
387
|
else
|
383
388
|
out = case @scalar
|
384
|
-
when Rational
|
389
|
+
when Rational
|
385
390
|
"#{@scalar} #{self.units}"
|
386
391
|
else
|
387
392
|
"#{'%g' % @scalar} #{self.units}"
|
@@ -400,8 +405,15 @@ class Unit < Numeric
|
|
400
405
|
|
401
406
|
# true if unit is a 'temperature', false if a 'degree' or anything else
|
402
407
|
def is_temperature?
|
403
|
-
|
408
|
+
self.is_degree? && self.units =~ /temp/
|
409
|
+
end
|
410
|
+
alias :temperature? :is_temperature?
|
411
|
+
|
412
|
+
# true if a degree unit or equivalent.
|
413
|
+
def is_degree?
|
414
|
+
self.kind == :temperature
|
404
415
|
end
|
416
|
+
alias :degree? :is_degree?
|
405
417
|
|
406
418
|
# returns the 'degree' unit associated with a temperature unit
|
407
419
|
# '100 tempC'.unit.temperature_scale #=> 'degC'
|
@@ -420,14 +432,15 @@ class Unit < Numeric
|
|
420
432
|
# Compare two Unit objects. Throws an exception if they are not of compatible types.
|
421
433
|
# Comparisons are done based on the value of the unit in base SI units.
|
422
434
|
def <=>(other)
|
423
|
-
case
|
424
|
-
when
|
425
|
-
|
435
|
+
case
|
436
|
+
when other.zero? && !self.is_temperature?
|
437
|
+
return self.base_scalar <=> 0
|
438
|
+
when Unit === other
|
426
439
|
raise ArgumentError, "Incompatible Units" unless self =~ other
|
427
|
-
self.base_scalar <=> other.base_scalar
|
440
|
+
return self.base_scalar <=> other.base_scalar
|
428
441
|
else
|
429
442
|
x,y = coerce(other)
|
430
|
-
x <=> y
|
443
|
+
return x <=> y
|
431
444
|
end
|
432
445
|
end
|
433
446
|
|
@@ -441,7 +454,8 @@ class Unit < Numeric
|
|
441
454
|
def =~(other)
|
442
455
|
return true if self == 0 || other == 0
|
443
456
|
case other
|
444
|
-
when Unit
|
457
|
+
when Unit
|
458
|
+
self.signature == other.signature
|
445
459
|
else
|
446
460
|
x,y = coerce(other)
|
447
461
|
x =~ y
|
@@ -457,7 +471,8 @@ class Unit < Numeric
|
|
457
471
|
# Unit("100 cm") === Unit("1 m") # => false
|
458
472
|
def ===(other)
|
459
473
|
case other
|
460
|
-
when Unit
|
474
|
+
when Unit
|
475
|
+
(self.scalar == other.scalar) && (self.units == other.units)
|
461
476
|
else
|
462
477
|
x,y = coerce(other)
|
463
478
|
x === y
|
@@ -473,12 +488,12 @@ class Unit < Numeric
|
|
473
488
|
def +(other)
|
474
489
|
if Unit === other
|
475
490
|
case
|
476
|
-
when self.zero?
|
477
|
-
|
491
|
+
when self.zero?
|
492
|
+
other.dup
|
493
|
+
when self =~ other
|
478
494
|
raise ArgumentError, "Cannot add two temperatures" if ([self, other].all? {|x| x.is_temperature?})
|
479
495
|
if [self, other].any? {|x| x.is_temperature?}
|
480
|
-
|
481
|
-
when true:
|
496
|
+
if self.is_temperature?
|
482
497
|
Unit.new(:scalar => (self.scalar + other.to(self.temperature_scale).scalar), :numerator => @numerator, :denominator=>@denominator, :signature => @signature)
|
483
498
|
else
|
484
499
|
Unit.new(:scalar => (other.scalar + self.to(other.temperature_scale).scalar), :numerator => other.numerator, :denominator=>other.denominator, :signature => other.signature)
|
@@ -488,13 +503,13 @@ class Unit < Numeric
|
|
488
503
|
Unit.new(:scalar=>(self.base_scalar + other.base_scalar)*@q, :numerator=>@numerator, :denominator=>@denominator, :signature => @signature)
|
489
504
|
end
|
490
505
|
else
|
491
|
-
|
506
|
+
raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')"
|
492
507
|
end
|
493
508
|
elsif Time === other
|
494
509
|
other + self
|
495
510
|
else
|
496
|
-
|
497
|
-
|
511
|
+
x,y = coerce(other)
|
512
|
+
y + x
|
498
513
|
end
|
499
514
|
end
|
500
515
|
|
@@ -502,15 +517,16 @@ class Unit < Numeric
|
|
502
517
|
# throws an exception if the units are not compatible.
|
503
518
|
def -(other)
|
504
519
|
if Unit === other
|
505
|
-
|
506
|
-
when self.zero?
|
507
|
-
|
520
|
+
case
|
521
|
+
when self.zero?
|
522
|
+
-other.dup
|
523
|
+
when self =~ other
|
508
524
|
case
|
509
|
-
when [self, other].all? {|x| x.is_temperature?}
|
525
|
+
when [self, other].all? {|x| x.is_temperature?}
|
510
526
|
Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator => KELVIN, :denominator => UNITY_ARRAY, :signature => @signature).to(self.temperature_scale)
|
511
|
-
when self.is_temperature?
|
527
|
+
when self.is_temperature?
|
512
528
|
Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator => ['<temp-K>'], :denominator => UNITY_ARRAY, :signature => @signature).to(self)
|
513
|
-
when other.is_temperature?
|
529
|
+
when other.is_temperature?
|
514
530
|
raise ArgumentError, "Cannot subtract a temperature from a differential degree unit"
|
515
531
|
else
|
516
532
|
@q ||= ((@@cached_units[self.units].scalar / @@cached_units[self.units].base_scalar) rescue (self.units.unit.scalar/self.units.unit.to_base.scalar))
|
@@ -561,6 +577,18 @@ class Unit < Numeric
|
|
561
577
|
y / x
|
562
578
|
end
|
563
579
|
end
|
580
|
+
|
581
|
+
# divide two units and return quotient and remainder
|
582
|
+
# when both units are in the same units we just use divmod on the raw scalars
|
583
|
+
# otherwise we use the scalar of the base unit which will be a float
|
584
|
+
def divmod(other)
|
585
|
+
raise ArgumentError, "Incompatible Units" unless self =~ other
|
586
|
+
if self.units == other.units
|
587
|
+
return self.scalar.divmod(other.scalar)
|
588
|
+
else
|
589
|
+
return self.to_base.scalar.divmod(other.to_base.scalar)
|
590
|
+
end
|
591
|
+
end
|
564
592
|
|
565
593
|
# Exponentiate. Only takes integer powers.
|
566
594
|
# Note that anything raised to the power of 0 results in a Unit object with a scalar of 1, and no units.
|
@@ -578,11 +606,11 @@ class Unit < Numeric
|
|
578
606
|
return self.inverse if other == -1
|
579
607
|
end
|
580
608
|
case other
|
581
|
-
when Rational
|
609
|
+
when Rational
|
582
610
|
self.power(other.numerator).root(other.denominator)
|
583
|
-
when Integer
|
611
|
+
when Integer
|
584
612
|
self.power(other)
|
585
|
-
when Float
|
613
|
+
when Float
|
586
614
|
return self**(other.to_i) if other == other.to_i
|
587
615
|
valid = (1..9).map {|x| 1/x}
|
588
616
|
raise ArgumentError, "Not a n-th root (1..9), use 1/n" unless valid.include? other.abs
|
@@ -624,13 +652,13 @@ class Unit < Numeric
|
|
624
652
|
for item in @numerator.uniq do
|
625
653
|
x = num.find_all {|i| i==item}.size
|
626
654
|
r = ((x/n)*(n-1)).to_int
|
627
|
-
r.times {|
|
655
|
+
r.times {|y| num.delete_at(num.index(item))}
|
628
656
|
end
|
629
657
|
|
630
658
|
for item in @denominator.uniq do
|
631
659
|
x = den.find_all {|i| i==item}.size
|
632
660
|
r = ((x/n)*(n-1)).to_int
|
633
|
-
r.times {|
|
661
|
+
r.times {|y| den.delete_at(den.index(item))}
|
634
662
|
end
|
635
663
|
q = @scalar < 0 ? (-1)**Rational(1,n) * (@scalar.abs)**Rational(1,n) : @scalar**Rational(1,n)
|
636
664
|
Unit.new(:scalar=>q,:numerator=>num,:denominator=>den)
|
@@ -652,7 +680,7 @@ class Unit < Numeric
|
|
652
680
|
#
|
653
681
|
# Special handling for temperature conversions is supported. If the Unit object is converted
|
654
682
|
# from one temperature unit to another, the proper temperature offsets will be used.
|
655
|
-
# Supports Kelvin,
|
683
|
+
# Supports Kelvin, Celsius, fahrenheit, and Rankine scales.
|
656
684
|
#
|
657
685
|
# Note that if temperature is part of a compound unit, the temperature will be treated as a differential
|
658
686
|
# and the units will be scaled appropriately.
|
@@ -661,34 +689,43 @@ class Unit < Numeric
|
|
661
689
|
return self if TrueClass === other
|
662
690
|
return self if FalseClass === other
|
663
691
|
if (Unit === other && other.is_temperature?) || (String === other && other =~ /temp(K|C|R|F)/)
|
664
|
-
raise ArgumentError, "Receiver is not a temperature unit" unless self.
|
692
|
+
raise ArgumentError, "Receiver is not a temperature unit" unless self.degree?
|
665
693
|
start_unit = self.units
|
666
694
|
target_unit = other.units rescue other
|
667
695
|
unless @base_scalar
|
668
696
|
@base_scalar = case start_unit
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
697
|
+
when 'tempC'
|
698
|
+
@scalar + 273.15
|
699
|
+
when 'tempK'
|
700
|
+
@scalar
|
701
|
+
when 'tempF'
|
702
|
+
(@scalar+459.67)*(5.0/9.0)
|
703
|
+
when 'tempR'
|
704
|
+
@scalar*(5.0/9.0)
|
673
705
|
end
|
674
706
|
end
|
675
707
|
q= case target_unit
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
708
|
+
when 'tempC'
|
709
|
+
@base_scalar - 273.15
|
710
|
+
when 'tempK'
|
711
|
+
@base_scalar
|
712
|
+
when 'tempF'
|
713
|
+
@base_scalar * (9.0/5.0) - 459.67
|
714
|
+
when 'tempR'
|
715
|
+
@base_scalar * (9.0/5.0)
|
716
|
+
end
|
717
|
+
|
682
718
|
Unit.new("#{q} #{target_unit}")
|
683
719
|
else
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
720
|
+
case other
|
721
|
+
when Unit
|
722
|
+
return self if other.units == self.units
|
723
|
+
target = other
|
724
|
+
when String
|
725
|
+
target = Unit.new(other)
|
726
|
+
else
|
727
|
+
raise ArgumentError, "Unknown target units"
|
728
|
+
end
|
692
729
|
raise ArgumentError, "Incompatible Units" unless self =~ target
|
693
730
|
one = @numerator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
|
694
731
|
two = @denominator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
|
@@ -706,7 +743,7 @@ class Unit < Numeric
|
|
706
743
|
# converts the unit back to a float if it is unitless. Otherwise raises an exception
|
707
744
|
def to_f
|
708
745
|
return @scalar.to_f if self.unitless?
|
709
|
-
raise RuntimeError, "Can't convert to Float unless unitless. Use Unit#scalar"
|
746
|
+
raise RuntimeError, "Can't convert to Float unless unitless (#{self.to_s}). Use Unit#scalar"
|
710
747
|
end
|
711
748
|
|
712
749
|
# converts the unit back to a complex if it is unitless. Otherwise raises an exception
|
@@ -740,13 +777,13 @@ class Unit < Numeric
|
|
740
777
|
output_d = ['1']
|
741
778
|
else
|
742
779
|
den.each_with_index do |token,index|
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
end
|
780
|
+
if token && @@PREFIX_VALUES[token] then
|
781
|
+
output_d << "#{@@OUTPUT_MAP[token]}#{@@OUTPUT_MAP[den[index+1]]}"
|
782
|
+
den[index+1]=nil
|
783
|
+
else
|
784
|
+
output_d << "#{@@OUTPUT_MAP[token]}" if token
|
749
785
|
end
|
786
|
+
end
|
750
787
|
end
|
751
788
|
on = output_n.reject {|x| x.empty?}.map {|x| [x, output_n.find_all {|z| z==x}.size]}.uniq.map {|x| ("#{x[0]}".strip+ (x[1] > 1 ? "^#{x[1]}" : ''))}
|
752
789
|
od = output_d.reject {|x| x.empty?}.map {|x| [x, output_d.find_all {|z| z==x}.size]}.uniq.map {|x| ("#{x[0]}".strip+ (x[1] > 1 ? "^#{x[1]}" : ''))}
|
@@ -811,7 +848,7 @@ class Unit < Numeric
|
|
811
848
|
|
812
849
|
# true if scalar is zero
|
813
850
|
def zero?
|
814
|
-
return
|
851
|
+
return self.to_base.scalar.zero?
|
815
852
|
end
|
816
853
|
|
817
854
|
# '5 min'.unit.ago
|
@@ -833,10 +870,12 @@ class Unit < Numeric
|
|
833
870
|
# 'min'.since(time)
|
834
871
|
def since(time_point = ::Time.now)
|
835
872
|
case time_point
|
836
|
-
when Time
|
837
|
-
|
838
|
-
when
|
839
|
-
(DateTime.now - time_point
|
873
|
+
when Time
|
874
|
+
(Time.now - time_point).unit('s').to(self)
|
875
|
+
when DateTime, Date
|
876
|
+
(DateTime.now - time_point).unit('d').to(self)
|
877
|
+
when String
|
878
|
+
(DateTime.now - time_point.to_datetime(:context=>:past)).unit('d').to(self)
|
840
879
|
else
|
841
880
|
raise ArgumentError, "Must specify a Time, DateTime, or String"
|
842
881
|
end
|
@@ -845,11 +884,12 @@ class Unit < Numeric
|
|
845
884
|
# 'min'.until(time)
|
846
885
|
def until(time_point = ::Time.now)
|
847
886
|
case time_point
|
848
|
-
when Time
|
849
|
-
|
850
|
-
when
|
851
|
-
|
852
|
-
|
887
|
+
when Time
|
888
|
+
(time_point - Time.now).unit('s').to(self)
|
889
|
+
when DateTime, Date
|
890
|
+
(time_point - DateTime.now).unit('d').to(self)
|
891
|
+
when String
|
892
|
+
(time_point.to_datetime(:context=>:future) - DateTime.now).unit('d').to(self)
|
853
893
|
else
|
854
894
|
raise ArgumentError, "Must specify a Time, DateTime, or String"
|
855
895
|
end
|
@@ -882,7 +922,8 @@ class Unit < Numeric
|
|
882
922
|
return [other.to_unit, self]
|
883
923
|
end
|
884
924
|
case other
|
885
|
-
when Unit
|
925
|
+
when Unit
|
926
|
+
[other, self]
|
886
927
|
else
|
887
928
|
[Unit.new(other), self]
|
888
929
|
end
|
@@ -906,22 +947,21 @@ class Unit < Numeric
|
|
906
947
|
|
907
948
|
# calculates the unit signature vector used by unit_signature
|
908
949
|
def unit_signature_vector
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
vector[n] = vector[n] + 1 if n
|
916
|
-
end
|
950
|
+
return self.to_base.unit_signature_vector unless self.is_base?
|
951
|
+
vector = Array.new(SIGNATURE_VECTOR.size,0)
|
952
|
+
for element in @numerator
|
953
|
+
if r=@@ALL_UNIT_DEFINITIONS[element]
|
954
|
+
n = SIGNATURE_VECTOR.index(r[2])
|
955
|
+
vector[n] = vector[n] + 1 if n
|
917
956
|
end
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
957
|
+
end
|
958
|
+
for element in @denominator
|
959
|
+
if r=@@ALL_UNIT_DEFINITIONS[element]
|
960
|
+
n = SIGNATURE_VECTOR.index(r[2])
|
961
|
+
vector[n] = vector[n] - 1 if n
|
923
962
|
end
|
924
|
-
|
963
|
+
end
|
964
|
+
vector
|
925
965
|
end
|
926
966
|
|
927
967
|
private
|
@@ -984,8 +1024,10 @@ class Unit < Numeric
|
|
984
1024
|
den = []
|
985
1025
|
for key, value in combined do
|
986
1026
|
case
|
987
|
-
when value > 0
|
988
|
-
|
1027
|
+
when value > 0
|
1028
|
+
value.times {num << key}
|
1029
|
+
when value < 0
|
1030
|
+
value.abs.times {den << key}
|
989
1031
|
end
|
990
1032
|
end
|
991
1033
|
num = UNITY_ARRAY if num.empty?
|
@@ -1083,8 +1125,10 @@ class Unit < Numeric
|
|
1083
1125
|
n = item[1].to_i
|
1084
1126
|
x = "#{item[0]} "
|
1085
1127
|
case
|
1086
|
-
when n>=0
|
1087
|
-
|
1128
|
+
when n>=0
|
1129
|
+
top.gsub!(/#{item[0]}(\^|\*\*)#{n}/) {|s| x * n}
|
1130
|
+
when n<0
|
1131
|
+
bottom = "#{bottom} #{x * -n}"; top.gsub!(/#{item[0]}(\^|\*\*)#{n}/,"")
|
1088
1132
|
end
|
1089
1133
|
end
|
1090
1134
|
bottom.gsub!(BOTTOM_REGEX) {|s| "#{$1} " * $2.to_i} if bottom
|
data/lib/ruby_units/string.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'time'
|
1
2
|
# make a string into a unit
|
2
3
|
class String
|
3
4
|
def to_unit(other = nil)
|
@@ -9,10 +10,14 @@ class String
|
|
9
10
|
|
10
11
|
# format unit output using formating codes '%0.2f' % '1 mm'.unit => '1.00 mm'
|
11
12
|
def %(*args)
|
13
|
+
return if self.empty?
|
12
14
|
case
|
13
|
-
when Unit === args[0]
|
14
|
-
|
15
|
-
when
|
15
|
+
when Unit === args[0]
|
16
|
+
args[0].to_s(self)
|
17
|
+
when (!defined?(Uncertain).nil? && (Uncertain === args[0]))
|
18
|
+
args[0].to_s(self)
|
19
|
+
when Complex === args[0]
|
20
|
+
args[0].to_s
|
16
21
|
else
|
17
22
|
unit_format(*args)
|
18
23
|
end
|
@@ -63,18 +68,30 @@ class String
|
|
63
68
|
raise(ArgumentError, 'Invalid Time String') unless r
|
64
69
|
return r
|
65
70
|
rescue
|
66
|
-
|
71
|
+
case
|
72
|
+
when self == "now"
|
73
|
+
Time.now
|
74
|
+
when Time.respond_to?(:parse)
|
75
|
+
Time.parse(self)
|
76
|
+
else
|
77
|
+
Time.local(*ParseDate.parsedate(self))
|
78
|
+
end
|
67
79
|
end
|
68
80
|
end
|
69
81
|
|
70
82
|
def to_datetime(options = {})
|
71
83
|
begin
|
72
84
|
# raises an exception if Chronic.parse = nil or if Chronic not defined
|
73
|
-
r = Chronic.parse(self,options).to_datetime
|
74
|
-
rescue
|
75
|
-
r=
|
85
|
+
r = Chronic.parse(self,options).send(:to_datetime)
|
86
|
+
rescue Exception => e
|
87
|
+
r = case
|
88
|
+
when self.to_s == "now"
|
89
|
+
DateTime.now
|
90
|
+
else
|
91
|
+
DateTime.parse(self)
|
92
|
+
end
|
76
93
|
end
|
77
|
-
raise RuntimeError, "Invalid Time String" if r == DateTime.new
|
94
|
+
raise RuntimeError, "Invalid Time String (#{self.to_s})" if r == DateTime.new
|
78
95
|
return r
|
79
96
|
end
|
80
97
|
|
@@ -82,7 +99,14 @@ class String
|
|
82
99
|
begin
|
83
100
|
r = Chronic.parse(self,options).to_date
|
84
101
|
rescue
|
85
|
-
r =
|
102
|
+
r = case
|
103
|
+
when self == "today"
|
104
|
+
Date.today
|
105
|
+
when RUBY_VERSION < "1.9"
|
106
|
+
Date.civil(*ParseDate.parsedate(self)[0..5].compact)
|
107
|
+
else
|
108
|
+
Date.parse(self)
|
109
|
+
end
|
86
110
|
end
|
87
111
|
raise RuntimeError, 'Invalid Date String' if r == Date.new
|
88
112
|
return r
|
data/lib/ruby_units/time.rb
CHANGED
@@ -24,28 +24,24 @@ class Time
|
|
24
24
|
alias :u :to_unit
|
25
25
|
alias :unit_add :+
|
26
26
|
|
27
|
-
|
28
|
-
DateTime.civil(1970,1,1)+(self.to_f+self.gmt_offset)/86400
|
29
|
-
end
|
30
|
-
|
31
|
-
# ruby 1.8.6 has a reasonable implementation of this that works well, so let's use it.
|
32
|
-
if VERSION == "1.8.4"
|
27
|
+
unless Time.instance_methods.include?(:to_date)
|
33
28
|
def to_date
|
34
29
|
x=(Date.civil(1970,1,1)+((self.to_f+self.gmt_offset)/86400.0)-0.5)
|
35
|
-
Date.civil(x.year, x.month, x.day
|
30
|
+
Date.civil(x.year, x.month, x.day)
|
36
31
|
end
|
37
32
|
end
|
38
33
|
|
39
34
|
def +(other)
|
40
35
|
case other
|
41
|
-
when Unit
|
36
|
+
when Unit
|
42
37
|
other = other.to('d').round.to('s') if ['y', 'decade', 'century'].include? other.units
|
43
38
|
begin
|
44
39
|
unit_add(other.to('s').scalar)
|
45
40
|
rescue RangeError
|
46
41
|
self.to_datetime + other
|
47
42
|
end
|
48
|
-
when DateTime
|
43
|
+
when DateTime
|
44
|
+
unit_add(other.to_time)
|
49
45
|
else
|
50
46
|
unit_add(other)
|
51
47
|
end
|
@@ -60,15 +56,16 @@ class Time
|
|
60
56
|
|
61
57
|
def -(other)
|
62
58
|
case other
|
63
|
-
when Unit
|
59
|
+
when Unit
|
64
60
|
other = other.to('d').round.to('s') if ['y', 'decade', 'century'].include? other.units
|
65
61
|
begin
|
66
62
|
unit_sub(other.to('s').scalar)
|
67
63
|
rescue RangeError
|
68
|
-
self.to_datetime - other
|
64
|
+
self.send(:to_datetime) - other
|
69
65
|
end
|
70
66
|
|
71
|
-
when DateTime
|
67
|
+
when DateTime
|
68
|
+
unit_sub(other.to_time)
|
72
69
|
else
|
73
70
|
unit_sub(other)
|
74
71
|
end
|
data/test/test_ruby-units.rb
CHANGED
@@ -1,13 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
1
2
|
require 'test/unit'
|
2
3
|
require 'ruby-units'
|
3
|
-
require 'rubygems'
|
4
|
-
require 'uncertain' if Gem::GemPathSearcher.new.find('uncertain')
|
5
4
|
require 'yaml'
|
6
|
-
|
5
|
+
begin
|
6
|
+
require 'chronic'
|
7
|
+
rescue LoadError
|
8
|
+
warn "Can't test Chronic integration unless gem 'chronic' is installed"
|
9
|
+
end
|
10
|
+
begin
|
11
|
+
require 'uncertain'
|
12
|
+
rescue LoadError
|
13
|
+
warn "Can't test Uncertain Units unless 'Uncertain' gem is installed"
|
14
|
+
end
|
15
|
+
|
7
16
|
|
8
17
|
class Unit < Numeric
|
9
18
|
@@USER_DEFINITIONS = {'<inchworm>' => [%w{inworm inchworm}, 0.0254, :length, %w{<meter>} ],
|
10
|
-
'<habenero>' => [%{degH}, 100, :temperature, %w{<celsius>}]}
|
19
|
+
'<habenero>' => [%w{degH}, 100, :temperature, %w{<celsius>}]}
|
11
20
|
Unit.setup
|
12
21
|
end
|
13
22
|
|
@@ -17,7 +26,7 @@ class Time
|
|
17
26
|
class << self
|
18
27
|
alias :unforced_now :now
|
19
28
|
def forced_now
|
20
|
-
|
29
|
+
@@forced_now.nil? ? unforced_now : @@forced_now
|
21
30
|
end
|
22
31
|
alias :now :forced_now
|
23
32
|
|
@@ -28,7 +37,7 @@ class Time
|
|
28
37
|
|
29
38
|
alias :unforced_gmt :gmt_offset
|
30
39
|
def forced_gmt
|
31
|
-
|
40
|
+
@@forced_gmt.nil? ? unforced_gmt : @@forced_gmt
|
32
41
|
end
|
33
42
|
alias :gmt_offset :forced_gmt
|
34
43
|
|
@@ -64,7 +73,7 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
64
73
|
|
65
74
|
def setup
|
66
75
|
@april_fools = Time.at 1143910800
|
67
|
-
@april_fools_datetime = DateTime.parse('2006-
|
76
|
+
@april_fools_datetime = DateTime.parse('2006-04-01T12:00:00-05:00')
|
68
77
|
Time.forced_now = @april_fools
|
69
78
|
DateTime.forced_now = @april_fools_datetime
|
70
79
|
#Unit.clear_cache
|
@@ -85,7 +94,6 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
85
94
|
assert_equal "s", a.to_unit.units
|
86
95
|
assert_equal a + 3600, a + "1 h".unit
|
87
96
|
assert_equal a - 3600, a - "1 h".unit
|
88
|
-
b = Unit(a) + "1 h".unit
|
89
97
|
assert_in_delta Time.now - "1 h".unit, "1 h".ago, 1
|
90
98
|
assert_in_delta Time.now + 3600, "1 h".from_now, 1
|
91
99
|
assert_in_delta "1 h".unit + Time.now, "1 h".from_now, 1
|
@@ -96,42 +104,60 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
96
104
|
assert_in_delta Time.now, "now".time, 1
|
97
105
|
end
|
98
106
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
107
|
+
def test_from_now
|
108
|
+
assert_equal "1 day".from_now, @april_fools + 86400
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_from
|
112
|
+
assert_equal "1 day".from("now"), @april_fools + 86400
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_ago
|
116
|
+
assert_equal "1 day".ago, @april_fools - 86400
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_before_now
|
120
|
+
assert_equal "1 day".before_now, @april_fools - 86400
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_before
|
124
|
+
assert_equal '1 days'.before('now'), @april_fools - 86400
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_since
|
106
128
|
assert_equal 'days'.since(@april_fools - 86400), "1 day".unit
|
107
|
-
assert_equal 'days'.since('3
|
108
|
-
assert_equal 'days'.since(DateTime.parse('2006-3-31 12:00')), "1 day".unit
|
109
|
-
assert_equal 'days'.until('4/2/2006'), '1 day'.unit
|
110
|
-
assert_equal 'now'.time, Time.now
|
111
|
-
assert_equal 'now'.datetime, DateTime.now
|
112
|
-
assert_raises(ArgumentError) { 'days'.until(1) }
|
129
|
+
assert_equal 'days'.since('2006-3-31 12:00:00 -5:00'), "1 day".unit
|
130
|
+
assert_equal 'days'.since(DateTime.parse('2006-3-31 12:00 -5:00')), "1 day".unit
|
113
131
|
assert_raises(ArgumentError) { 'days'.since(1) }
|
114
|
-
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_until
|
135
|
+
assert_equal 'days'.until('2006-04-2 12:00:00 -5:00'), '1 day'.unit
|
136
|
+
assert_raises(ArgumentError) { 'days'.until(1) }
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_time_helpers
|
140
|
+
assert_equal @april_fools, Time.now
|
141
|
+
assert_equal @april_fools_datetime, DateTime.now
|
142
|
+
assert_equal Time.now, 'now'.time
|
143
|
+
assert_equal DateTime.now, 'now'.datetime
|
144
|
+
assert_equal 1143910800, Unit.new(Time.now).scalar
|
115
145
|
assert_equal @april_fools.unit.to_time, @april_fools
|
116
146
|
assert_equal Time.in('1 day'), @april_fools + 86400
|
117
|
-
assert_equal "2006-04-01T12:00:00
|
147
|
+
assert_equal "2006-04-01T12:00:00-05:00", @april_fools_datetime.inspect
|
118
148
|
assert_equal "2006-04-01T00:00:00+00:00", '2453826.5 days'.unit.to_datetime.to_s
|
119
149
|
end
|
120
150
|
|
121
151
|
def test_string_helpers
|
122
152
|
assert_equal '1 mm'.to('in'), Unit('1 mm').to('in')
|
123
153
|
end
|
124
|
-
|
125
|
-
def test_math
|
126
|
-
pi = Math::PI
|
127
|
-
assert_equal Math.sin(pi), Math.sin("180 deg".unit)
|
128
|
-
assert_equal Math.cos(pi), Math.cos("180 deg".unit)
|
129
|
-
assert_equal Math.tan(pi), Math.tan("180 deg".unit)
|
130
|
-
assert_equal Math.sinh(pi), Math.sinh("180 deg".unit)
|
131
|
-
assert_equal Math.cosh(pi), Math.cosh("180 deg".unit)
|
132
|
-
assert_equal Math.tanh(pi), Math.tanh("180 deg".unit)
|
133
|
-
end
|
134
154
|
|
155
|
+
[:sin, :cos, :tan, :sinh, :cosh, :tanh].each do |trig|
|
156
|
+
define_method("test_#{trig}") do
|
157
|
+
assert_equal Math.send(trig, Math::PI), Math.send(trig, "180 deg".unit)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
135
161
|
def test_clone
|
136
162
|
unit1= "1 mm".unit
|
137
163
|
unit2 = unit1.clone
|
@@ -312,7 +338,7 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
312
338
|
assert_equal ['<foot>'], unit3.numerator
|
313
339
|
assert_equal ['<1>'],unit3.denominator
|
314
340
|
}
|
315
|
-
assert_raises(ArgumentError) {
|
341
|
+
assert_raises(ArgumentError) { unit1 >> 5.0}
|
316
342
|
assert_equal unit1, unit1.to(true)
|
317
343
|
assert_equal unit1, unit1.to(false)
|
318
344
|
assert_equal unit1, unit1.to(nil)
|
@@ -405,7 +431,7 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
405
431
|
assert_equal "2".unit, unit2
|
406
432
|
}
|
407
433
|
assert_raises(ArgumentError) {
|
408
|
-
|
434
|
+
"1".unit + nil
|
409
435
|
}
|
410
436
|
end
|
411
437
|
|
@@ -505,7 +531,7 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
505
531
|
assert_equal unit2, unit1**-2
|
506
532
|
}
|
507
533
|
assert_raises(ZeroDivisionError) {
|
508
|
-
|
534
|
+
"0 mm".unit**-1
|
509
535
|
}
|
510
536
|
end
|
511
537
|
|
@@ -581,8 +607,9 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
581
607
|
assert_raises(ArgumentError) { '-1000 tempC'.unit}
|
582
608
|
assert_raises(ArgumentError) { '-1000 tempF'.unit}
|
583
609
|
|
584
|
-
|
585
|
-
|
610
|
+
assert_in_delta '32 tempF'.unit.base_scalar, '0 tempC'.unit.base_scalar, 0.01
|
611
|
+
assert_in_delta '0 tempC'.unit.base_scalar, '32 tempF'.unit.base_scalar, 0.01
|
612
|
+
assert_in_delta '0 tempC'.unit.base_scalar, '273.15 tempK'.unit.base_scalar, 0.01
|
586
613
|
assert_in_delta '0 tempC'.unit.base_scalar, '491.67 tempR'.unit.base_scalar, 0.01
|
587
614
|
|
588
615
|
a = '10 degC'.unit
|
@@ -592,14 +619,13 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
592
619
|
assert_equal a >> 'tempF', '-441.67 tempF'.unit
|
593
620
|
|
594
621
|
unit1 = '37 tempC'.unit
|
595
|
-
unit2 = unit1 >> "tempF" >> 'tempK' >> 'tempR' >> 'tempC'
|
596
622
|
assert_equal unit1 >> 'tempF' >> 'tempK' >> 'tempR' >> 'tempC', unit1
|
597
623
|
|
598
624
|
a = '100 tempF'.unit
|
599
625
|
b = '10 degC'.unit
|
600
626
|
c = '50 tempF'.unit
|
601
627
|
d = '18 degF'.unit
|
602
|
-
assert_equal
|
628
|
+
assert_equal('118 tempF'.unit,a+b)
|
603
629
|
assert_equal b+a, '118 tempF'.unit
|
604
630
|
assert_equal a-b, '82 tempF'.unit
|
605
631
|
assert_in_delta((a-c).scalar, '50 degF'.unit.scalar, 0.01)
|
@@ -752,14 +778,12 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
752
778
|
assert_equal "cells", a.units
|
753
779
|
end
|
754
780
|
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
end
|
762
|
-
end
|
781
|
+
if defined?(Uncertain)
|
782
|
+
def test_uncertain
|
783
|
+
a = '1 +/- 1 mm'.unit
|
784
|
+
assert_equal a.to_s, '1 +/- 1 mm'
|
785
|
+
end
|
786
|
+
end
|
763
787
|
|
764
788
|
def test_format
|
765
789
|
assert_equal "%0.2f" % "1 mm".unit, "1.00 mm"
|
@@ -771,7 +795,7 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
771
795
|
end
|
772
796
|
|
773
797
|
def test_currency
|
774
|
-
assert_nothing_raised {
|
798
|
+
assert_nothing_raised {"$1".unit}
|
775
799
|
end
|
776
800
|
|
777
801
|
def test_kind
|
@@ -781,9 +805,9 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
781
805
|
|
782
806
|
def test_percent
|
783
807
|
assert_nothing_raised {
|
784
|
-
|
785
|
-
|
786
|
-
|
808
|
+
"1 percent".unit
|
809
|
+
"1%".unit
|
810
|
+
"0.01%".unit
|
787
811
|
}
|
788
812
|
a = '100 ml'.unit
|
789
813
|
b = '50%'.unit
|
@@ -852,9 +876,29 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
852
876
|
a = '-9 mm^2'.unit
|
853
877
|
b = a**(0.5)
|
854
878
|
assert_in_delta Math.sqrt(a).to_unit.scalar.real, b.scalar.real, 0.00001
|
855
|
-
|
856
|
-
|
879
|
+
# ruby 1.8.x uses .image while 1.9.x uses .imaginary
|
880
|
+
if Complex.instance_methods.include?(:imaginary)
|
881
|
+
assert_in_delta Math.sqrt(a).to_unit.scalar.imaginary, b.scalar.imaginary, 0.00001
|
882
|
+
else
|
883
|
+
assert_in_delta Math.sqrt(a).to_unit.scalar.image, b.scalar.image, 0.00001
|
884
|
+
end
|
857
885
|
end
|
886
|
+
|
887
|
+
def test_div
|
888
|
+
assert_equal 11,"23 m".unit.div('2 m'.unit)
|
889
|
+
end
|
890
|
+
|
891
|
+
def test_divmod
|
892
|
+
assert_equal [277,1], 555.unit('mm').divmod(2.unit('mm'))
|
893
|
+
assert_equal [277, 0.0010000000000000373], '0.555 m'.unit.divmod('2 mm'.unit)
|
894
|
+
assert_raises(ArgumentError) { '1 m'.unit.divmod('1 kg'.unit) }
|
895
|
+
end
|
896
|
+
|
897
|
+
if Math.respond_to?(:cbrt)
|
898
|
+
def test_cbrt
|
899
|
+
assert_in_delta '2 mm'.to_unit, Math.cbrt('8 mm^3'.to_unit), 0.0001
|
900
|
+
end
|
901
|
+
end
|
858
902
|
|
859
903
|
def test_hypot
|
860
904
|
assert_equal Math.hypot('3 mm'.unit,'4 mm'.unit), '5 mm'.unit
|
@@ -914,7 +958,7 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
914
958
|
end
|
915
959
|
|
916
960
|
def test_version
|
917
|
-
assert_equal('1.
|
961
|
+
assert_equal('1.2.0.a', Unit::VERSION)
|
918
962
|
end
|
919
963
|
|
920
964
|
def test_negation
|
@@ -922,5 +966,17 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
922
966
|
assert_equal(a.class, (1-a).class)
|
923
967
|
end
|
924
968
|
|
969
|
+
def test_degree
|
970
|
+
assert "100 tempF".unit.degree?
|
971
|
+
assert "100 degC".unit.degree?
|
972
|
+
assert !"1 mm".unit.degree?
|
973
|
+
end
|
974
|
+
|
975
|
+
def test_temperature
|
976
|
+
assert "100 tempF".unit.temperature?
|
977
|
+
assert !"100 degC".unit.temperature?
|
978
|
+
assert !"1 mm".unit.temperature?
|
979
|
+
end
|
980
|
+
|
925
981
|
end
|
926
982
|
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-units
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: true
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
-
|
8
|
-
-
|
9
|
-
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
- a
|
10
|
+
version: 1.2.0.a
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Kevin Olbrich, Ph.D
|
@@ -14,13 +15,14 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-11-07 01:00:00 -04:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: rubyforge
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
@@ -31,24 +33,11 @@ dependencies:
|
|
31
33
|
version: 2.0.4
|
32
34
|
type: :development
|
33
35
|
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: gemcutter
|
36
|
-
prerelease: false
|
37
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
segments:
|
42
|
-
- 0
|
43
|
-
- 5
|
44
|
-
- 0
|
45
|
-
version: 0.5.0
|
46
|
-
type: :development
|
47
|
-
version_requirements: *id002
|
48
36
|
- !ruby/object:Gem::Dependency
|
49
37
|
name: hoe-yard
|
50
38
|
prerelease: false
|
51
|
-
requirement: &
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
52
41
|
requirements:
|
53
42
|
- - ">="
|
54
43
|
- !ruby/object:Gem::Version
|
@@ -58,21 +47,22 @@ dependencies:
|
|
58
47
|
- 2
|
59
48
|
version: 0.1.2
|
60
49
|
type: :development
|
61
|
-
version_requirements: *
|
50
|
+
version_requirements: *id002
|
62
51
|
- !ruby/object:Gem::Dependency
|
63
52
|
name: hoe
|
64
53
|
prerelease: false
|
65
|
-
requirement: &
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
66
56
|
requirements:
|
67
57
|
- - ">="
|
68
58
|
- !ruby/object:Gem::Version
|
69
59
|
segments:
|
70
60
|
- 2
|
71
|
-
-
|
72
|
-
-
|
73
|
-
version: 2.
|
61
|
+
- 6
|
62
|
+
- 1
|
63
|
+
version: 2.6.1
|
74
64
|
type: :development
|
75
|
-
version_requirements: *
|
65
|
+
version_requirements: *id003
|
76
66
|
description: This library handles unit conversions and unit math
|
77
67
|
email: kevin.olbrich+ruby_units@gmail.com
|
78
68
|
executables: []
|
@@ -116,6 +106,7 @@ rdoc_options:
|
|
116
106
|
require_paths:
|
117
107
|
- lib
|
118
108
|
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
119
110
|
requirements:
|
120
111
|
- - ">="
|
121
112
|
- !ruby/object:Gem::Version
|
@@ -123,16 +114,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
114
|
- 0
|
124
115
|
version: "0"
|
125
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
126
118
|
requirements:
|
127
|
-
- - "
|
119
|
+
- - ">"
|
128
120
|
- !ruby/object:Gem::Version
|
129
121
|
segments:
|
130
|
-
-
|
131
|
-
|
122
|
+
- 1
|
123
|
+
- 3
|
124
|
+
- 1
|
125
|
+
version: 1.3.1
|
132
126
|
requirements: []
|
133
127
|
|
134
128
|
rubyforge_project: ruby-units
|
135
|
-
rubygems_version: 1.3.
|
129
|
+
rubygems_version: 1.3.7
|
136
130
|
signing_key:
|
137
131
|
specification_version: 3
|
138
132
|
summary: A class that performs unit conversions and unit math
|