ruby-units 1.1.5 → 1.2.0.a
Sign up to get free protection for your applications and to get access to all the features.
- 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
|