ruby-units 0.2.3 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +10 -2
- data/lib/ruby-units.rb +360 -183
- data/lib/units.rb +19 -11
- data/test/test_ruby-units.rb +25 -6
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -108,5 +108,13 @@ Change Log for Ruby-units
|
|
108
108
|
* 'string'.to_datetime returns a DateTime object
|
109
109
|
* 'string'.time returns a Time object or a DateTime if the Time object fails
|
110
110
|
* 'string'.datetime returns a DateTime or a Time if the DateTime fails
|
111
|
-
|
112
|
-
|
111
|
+
|
112
|
+
2006-10-02 0.3.0 * Performance enhanced by caching results of many functions
|
113
|
+
(Thanks to Kurt Stephens for pushing this.)
|
114
|
+
* Throws an exception if the unit is not recognized
|
115
|
+
* units can now identify what 'kind' they are (:length, :mass, etc..)
|
116
|
+
* New constructors:
|
117
|
+
Unit(1,"mm")
|
118
|
+
Unit(1,"mm/s")
|
119
|
+
Unit(1,"mm","s")
|
120
|
+
2006-10-02 0.3.1 * minor bug fixes
|
data/lib/ruby-units.rb
CHANGED
@@ -2,7 +2,7 @@ require 'mathn'
|
|
2
2
|
require 'rational'
|
3
3
|
require 'date'
|
4
4
|
require 'parsedate'
|
5
|
-
# = Ruby Units 0.
|
5
|
+
# = Ruby Units 0.3.1
|
6
6
|
#
|
7
7
|
# Copyright 2006 by Kevin C. Olbrich, Ph.D.
|
8
8
|
#
|
@@ -40,49 +40,124 @@ require 'parsedate'
|
|
40
40
|
class Unit < Numeric
|
41
41
|
require 'units'
|
42
42
|
# pre-generate hashes from unit definitions for performance.
|
43
|
+
VERSION = '0.3.0'
|
43
44
|
@@USER_DEFINITIONS = {}
|
44
45
|
@@PREFIX_VALUES = {}
|
45
46
|
@@PREFIX_MAP = {}
|
46
47
|
@@UNIT_MAP = {}
|
47
48
|
@@UNIT_VALUES = {}
|
48
49
|
@@OUTPUT_MAP = {}
|
49
|
-
@@
|
50
|
+
@@BASE_UNITS = ['<meter>','<kilogram>','<second>','<mole>', '<farad>', '<ampere>','<radian>','<kelvin>','<byte>','<dollar>','<candela>','<each>','<steradian>','<decibel>']
|
51
|
+
UNITY = '<1>'
|
52
|
+
UNITY_ARRAY= [UNITY]
|
53
|
+
FEET_INCH_REGEX = /(\d+)\s*(?:'|ft|feet)\s*(\d+)\s*(?:"|in|inches)/
|
54
|
+
LBS_OZ_REGEX = /(\d+)\s*(?:#|lbs|pounds)+[\s,]*(\d+)\s*(?:oz|ounces)/
|
55
|
+
SCI_NUMBER = %r{([+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*)}
|
56
|
+
NUMBER_REGEX = /#{SCI_NUMBER}*\s*(.+)?/
|
57
|
+
UNIT_STRING_REGEX = /#{SCI_NUMBER}*\s*([^\/]*)\/*(.+)*/
|
58
|
+
TOP_REGEX = /([^ \*]+)(?:\^|\*\*)([\d-]+)/
|
59
|
+
BOTTOM_REGEX = /([^* ]+)(?:\^|\*\*)(\d+)/
|
60
|
+
|
61
|
+
KELVIN = ['<kelvin>']
|
62
|
+
FARENHEIT = ['<farenheit>']
|
63
|
+
RANKINE = ['<rankine>']
|
64
|
+
CELCIUS = ['<celcius>']
|
50
65
|
|
66
|
+
SIGNATURE_VECTOR = [:length, :time, :temperature, :mass, :current, :substance, :luminosity, :currency, :memory, :angle, :capacitance]
|
67
|
+
@@KINDS = {
|
68
|
+
-312058=>:resistance,
|
69
|
+
-312038=>:inductance,
|
70
|
+
-152040=>:magnetism,
|
71
|
+
-152038=>:magnetism,
|
72
|
+
-152058=>:potential,
|
73
|
+
-39=>:acceleration,
|
74
|
+
-38=>:radiation,
|
75
|
+
-20=>:frequency,
|
76
|
+
-19=>:speed,
|
77
|
+
-18=>:viscosity,
|
78
|
+
0=>:unitless,
|
79
|
+
1=>:length,
|
80
|
+
2=>:area,
|
81
|
+
3=>:volume,
|
82
|
+
20=>:time,
|
83
|
+
400=>:temperature,
|
84
|
+
7942=>:power,
|
85
|
+
7959=>:pressure,
|
86
|
+
7962=>:energy,
|
87
|
+
7979=>:viscosity,
|
88
|
+
7981=>:force,
|
89
|
+
7997=>:mass_concentration,
|
90
|
+
8000=>:mass,
|
91
|
+
159999=>:magnetism,
|
92
|
+
160000=>:current,
|
93
|
+
160020=>:charge,
|
94
|
+
312058=>:resistance,
|
95
|
+
3199980=>:activity,
|
96
|
+
3199997=>:molar_concentration,
|
97
|
+
3200000=>:substance,
|
98
|
+
63999998=>:illuminance,
|
99
|
+
64000000=>:luminous_power,
|
100
|
+
1280000000=>:currency,
|
101
|
+
25600000000=>:memory,
|
102
|
+
511999999980=>:angular_velocity,
|
103
|
+
512000000000=>:angle,
|
104
|
+
10240000000000=>:capacitance,
|
105
|
+
}
|
106
|
+
|
107
|
+
@@cached_units = {}
|
108
|
+
@@base_unit_cache = {}
|
109
|
+
|
51
110
|
def self.setup
|
52
|
-
|
111
|
+
@@ALL_UNIT_DEFINITIONS = UNIT_DEFINITIONS.merge!(@@USER_DEFINITIONS)
|
112
|
+
for unit in (@@ALL_UNIT_DEFINITIONS) do
|
113
|
+
key, value = unit
|
53
114
|
if value[2] == :prefix then
|
54
|
-
@@PREFIX_VALUES[
|
55
|
-
value[0]
|
115
|
+
@@PREFIX_VALUES[key]=value[1]
|
116
|
+
for name in value[0] do
|
117
|
+
@@PREFIX_MAP[name]=key
|
118
|
+
end
|
56
119
|
else
|
57
|
-
@@UNIT_VALUES[
|
58
|
-
@@UNIT_VALUES[
|
59
|
-
@@UNIT_VALUES[
|
60
|
-
@@UNIT_VALUES[
|
61
|
-
value[0]
|
62
|
-
|
63
|
-
|
120
|
+
@@UNIT_VALUES[key]={}
|
121
|
+
@@UNIT_VALUES[key][:scalar]=value[1]
|
122
|
+
@@UNIT_VALUES[key][:numerator]=value[3] if value[3]
|
123
|
+
@@UNIT_VALUES[key][:denominator]=value[4] if value[4]
|
124
|
+
for name in value[0] do
|
125
|
+
@@UNIT_MAP[name]=key
|
126
|
+
end
|
64
127
|
end
|
65
|
-
@@OUTPUT_MAP[
|
128
|
+
@@OUTPUT_MAP[key]=value[0][0]
|
66
129
|
end
|
67
130
|
@@PREFIX_REGEX = @@PREFIX_MAP.keys.sort_by {|prefix| prefix.length}.reverse.join('|')
|
68
131
|
@@UNIT_REGEX = @@UNIT_MAP.keys.sort_by {|unit| unit.length}.reverse.join('|')
|
132
|
+
@@UNIT_MATCH_REGEX = /(#{@@PREFIX_REGEX})*?(#{@@UNIT_REGEX})\b/
|
133
|
+
|
69
134
|
end
|
70
135
|
|
71
|
-
self.setup
|
72
136
|
|
73
137
|
include Comparable
|
74
|
-
attr_accessor :scalar, :numerator, :denominator, :signature, :base_scalar
|
138
|
+
attr_accessor :scalar, :numerator, :denominator, :signature, :base_scalar, :base_numerator, :base_denominator, :output, :unit_name
|
75
139
|
|
76
140
|
def to_yaml_properties
|
77
141
|
%w{@scalar @numerator @denominator @signature @base_scalar}
|
78
142
|
end
|
143
|
+
|
144
|
+
def copy(from)
|
145
|
+
@scalar = from.scalar
|
146
|
+
@numerator = from.numerator
|
147
|
+
@denominator = from.denominator
|
148
|
+
@is_base = from.is_base?
|
149
|
+
@signature = from.signature
|
150
|
+
@base_scalar = from.base_scalar
|
151
|
+
@output = from.output rescue nil
|
152
|
+
@unit_name = from.unit_name rescue nil
|
153
|
+
end
|
79
154
|
|
80
155
|
# basically a copy of the basic to_yaml. Needed because otherwise it ends up coercing the object to a string
|
81
156
|
# before YAML'izing it.
|
82
157
|
def to_yaml( opts = {} )
|
83
158
|
YAML::quick_emit( object_id, opts ) do |out|
|
84
159
|
out.map( taguri, to_yaml_style ) do |map|
|
85
|
-
to_yaml_properties
|
160
|
+
for m in to_yaml_properties do
|
86
161
|
map.add( m[1..-1], instance_variable_get( m ) )
|
87
162
|
end
|
88
163
|
end
|
@@ -101,28 +176,77 @@ class Unit < Numeric
|
|
101
176
|
# 6'4" -- recognized as 6 feet + 4 inches
|
102
177
|
# 8 lbs 8 oz -- recognized as 8 lbs + 8 ounces
|
103
178
|
#
|
104
|
-
def initialize(options)
|
105
|
-
|
106
|
-
|
179
|
+
def initialize(*options)
|
180
|
+
if options.size == 2
|
181
|
+
begin
|
182
|
+
cached = @@cached_units[options[1]] * options[0]
|
183
|
+
copy(cached)
|
184
|
+
rescue
|
185
|
+
initialize("#{options[0]} #{options[1]}")
|
186
|
+
end
|
187
|
+
return
|
188
|
+
end
|
189
|
+
if options.size == 3
|
190
|
+
begin
|
191
|
+
cached = @@cached_units["#{options[1]}/#{options[2]}"] * options[0]
|
192
|
+
copy(cached)
|
193
|
+
rescue
|
194
|
+
initialize("#{options[0]} #{options[1]}/#{options[2]}")
|
195
|
+
end
|
196
|
+
return
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
case options[0]
|
201
|
+
when String: parse(options[0])
|
107
202
|
when Hash:
|
108
|
-
@scalar = options[:scalar] || 1
|
109
|
-
@numerator = options[:numerator] ||
|
110
|
-
@denominator = options[:denominator] ||
|
111
|
-
|
112
|
-
|
203
|
+
@scalar = options[0][:scalar] || 1
|
204
|
+
@numerator = options[0][:numerator] || UNITY_ARRAY
|
205
|
+
@denominator = options[0][:denominator] || UNITY_ARRAY
|
206
|
+
@signature = options[0][:signature]
|
207
|
+
when Array:
|
208
|
+
initialize(*options[0])
|
209
|
+
return
|
113
210
|
when Numeric:
|
114
|
-
@scalar = options
|
115
|
-
@numerator = @denominator =
|
211
|
+
@scalar = options[0]
|
212
|
+
@numerator = @denominator = UNITY_ARRAY
|
116
213
|
when Time:
|
117
|
-
@scalar = options.to_f
|
214
|
+
@scalar = options[0].to_f
|
118
215
|
@numerator = ['<second>']
|
119
|
-
@denominator =
|
216
|
+
@denominator = UNITY_ARRAY
|
120
217
|
else
|
121
218
|
raise ArgumentError, "Invalid Unit Format"
|
122
219
|
end
|
123
220
|
self.update_base_scalar
|
124
221
|
self.replace_temperature
|
125
|
-
|
222
|
+
|
223
|
+
unary_unit = self.units
|
224
|
+
opt_units = options[0].scan(NUMBER_REGEX)[0][1] if String === options[0]
|
225
|
+
unless @@cached_units.keys.include?(opt_units) || (opt_units =~ /(temp|deg)(C|K|R|F)/)
|
226
|
+
@@cached_units[opt_units] = (self.scalar == 1 ? self : opt_units.unit) if opt_units && !opt_units.empty?
|
227
|
+
end
|
228
|
+
unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /(temp|deg)(C|K|R|F)/) then
|
229
|
+
@@cached_units[unary_unit] = (self.scalar == 1 ? self : unary_unit.unit)
|
230
|
+
end
|
231
|
+
@scalar.freeze
|
232
|
+
@numerator.freeze
|
233
|
+
@denominator.freeze
|
234
|
+
@base_scalar.freeze
|
235
|
+
@signature.freeze
|
236
|
+
@is_base.freeze
|
237
|
+
self
|
238
|
+
end
|
239
|
+
|
240
|
+
def kind
|
241
|
+
return @@KINDS[self.signature]
|
242
|
+
end
|
243
|
+
|
244
|
+
def self.cached
|
245
|
+
return @@cached_units
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.base_unit_cache
|
249
|
+
return @@base_unit_cache
|
126
250
|
end
|
127
251
|
|
128
252
|
def to_unit
|
@@ -132,48 +256,49 @@ class Unit < Numeric
|
|
132
256
|
|
133
257
|
# Returns 'true' if the Unit is represented in base units
|
134
258
|
def is_base?
|
135
|
-
return
|
259
|
+
return @is_base if defined? @is_base
|
260
|
+
return @is_base=true if @signature == 400 && @numerator.size == 1 && @numerator[0] =~ /(celcius|kelvin|farenheit|rankine)/
|
136
261
|
n = @numerator + @denominator
|
137
|
-
n.compact
|
138
|
-
return false unless x ==
|
139
|
-
(@@UNIT_VALUES[Regexp.escape(x)] &&
|
140
|
-
@@UNIT_VALUES[Regexp.escape(x)][:denominator].nil? &&
|
141
|
-
@@UNIT_VALUES[Regexp.escape(x)][:numerator].include?(Regexp.escape(x)))
|
262
|
+
for x in n.compact do
|
263
|
+
return @is_base=false unless x == UNITY || (@@BASE_UNITS.include?((x)))
|
142
264
|
end
|
143
|
-
return true
|
265
|
+
return @is_base = true
|
144
266
|
end
|
145
267
|
|
146
268
|
#convert to base SI units
|
147
269
|
def to_base
|
148
270
|
return self if self.is_base?
|
149
|
-
|
271
|
+
cached = @@base_unit_cache[self.units] * self.scalar rescue nil
|
272
|
+
return cached if cached
|
273
|
+
|
150
274
|
num = []
|
151
275
|
den = []
|
152
|
-
q =
|
153
|
-
@numerator.compact
|
154
|
-
if @@PREFIX_VALUES[
|
155
|
-
q *= @@PREFIX_VALUES[
|
276
|
+
q = 1
|
277
|
+
for unit in @numerator.compact do
|
278
|
+
if @@PREFIX_VALUES[unit]
|
279
|
+
q *= @@PREFIX_VALUES[unit]
|
156
280
|
else
|
157
|
-
q *= @@UNIT_VALUES[
|
158
|
-
num << @@UNIT_VALUES[
|
159
|
-
den << @@UNIT_VALUES[
|
281
|
+
q *= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
|
282
|
+
num << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
|
283
|
+
den << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
|
160
284
|
end
|
161
285
|
end
|
162
|
-
@denominator.compact
|
163
|
-
if @@PREFIX_VALUES[
|
164
|
-
q /= @@PREFIX_VALUES[
|
286
|
+
for unit in @denominator.compact do
|
287
|
+
if @@PREFIX_VALUES[unit]
|
288
|
+
q /= @@PREFIX_VALUES[unit]
|
165
289
|
else
|
166
|
-
q /= @@UNIT_VALUES[
|
167
|
-
den << @@UNIT_VALUES[
|
168
|
-
num << @@UNIT_VALUES[
|
290
|
+
q /= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
|
291
|
+
den << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
|
292
|
+
num << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
|
169
293
|
end
|
170
294
|
end
|
171
295
|
|
172
296
|
num = num.flatten.compact
|
173
297
|
den = den.flatten.compact
|
174
|
-
num =
|
175
|
-
|
176
|
-
|
298
|
+
num = UNITY_ARRAY if num.empty?
|
299
|
+
base= Unit.new(Unit.eliminate_terms(q,num,den))
|
300
|
+
@@base_unit_cache[self.units]=base
|
301
|
+
return base * @scalar
|
177
302
|
end
|
178
303
|
|
179
304
|
# Generate human readable output.
|
@@ -183,23 +308,29 @@ class Unit < Numeric
|
|
183
308
|
# :ft - outputs in feet and inches (e.g., 6'4")
|
184
309
|
# :lbs - outputs in pounds and ounces (e.g, 8 lbs, 8 oz)
|
185
310
|
def to_s(target_units=nil)
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
"#{(inches / 12).truncate}\'#{(inches % 12).round}\""
|
190
|
-
when :lbs:
|
191
|
-
ounces = self.to("oz").scalar
|
192
|
-
"#{(ounces / 16).truncate} lbs, #{(ounces % 16).round} oz"
|
193
|
-
when String
|
194
|
-
begin #first try a standard format string
|
195
|
-
target_units =~ /(%[\w\d#+-.]*)*\s*(.+)*/
|
196
|
-
return self.to($2).to_s($1) if $2
|
197
|
-
"#{($1 || '%g') % @scalar || 0} #{self.units}".strip
|
198
|
-
rescue #if that is malformed, try a time string
|
199
|
-
return (Time.gm(0) + self).strftime(target_units)
|
200
|
-
end
|
311
|
+
out = @output[target_units] rescue nil
|
312
|
+
if out
|
313
|
+
return out
|
201
314
|
else
|
202
|
-
|
315
|
+
case target_units
|
316
|
+
when :ft:
|
317
|
+
inches = self.to("in").scalar
|
318
|
+
out = "#{(inches / 12).truncate}\'#{(inches % 12).round}\""
|
319
|
+
when :lbs:
|
320
|
+
ounces = self.to("oz").scalar
|
321
|
+
out = "#{(ounces / 16).truncate} lbs, #{(ounces % 16).round} oz"
|
322
|
+
when String
|
323
|
+
begin #first try a standard format string
|
324
|
+
target_units =~ /(%[\w\d#+-.]*)*\s*(.+)*/
|
325
|
+
out = $2 ? self.to($2).to_s($1) : "#{($1 || '%g') % @scalar || 0} #{self.units}".strip
|
326
|
+
rescue #if that is malformed, try a time string
|
327
|
+
out = (Time.gm(0) + self).strftime(target_units)
|
328
|
+
end
|
329
|
+
else
|
330
|
+
out = "#{'%g' % @scalar} #{self.units}".strip
|
331
|
+
end
|
332
|
+
@output = {target_units => out}
|
333
|
+
return out
|
203
334
|
end
|
204
335
|
end
|
205
336
|
|
@@ -209,8 +340,9 @@ class Unit < Numeric
|
|
209
340
|
end
|
210
341
|
|
211
342
|
# returns true if no associated units
|
343
|
+
# false, even if the units are "unitless" like 'radians, each, etc'
|
212
344
|
def unitless?
|
213
|
-
(@numerator ==
|
345
|
+
(@numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY)
|
214
346
|
end
|
215
347
|
|
216
348
|
# Compare two Unit objects. Throws an exception if they are not of compatible types.
|
@@ -266,9 +398,8 @@ class Unit < Numeric
|
|
266
398
|
def +(other)
|
267
399
|
if Unit === other
|
268
400
|
if self =~ other then
|
269
|
-
|
270
|
-
|
271
|
-
Unit.new(:scalar=>(a.scalar + b.scalar), :numerator=>a.numerator, :denominator=>b.denominator).to(self)
|
401
|
+
q = self.zero? ? 1 : (self.scalar / self.base_scalar)
|
402
|
+
Unit.new(:scalar=>(self.base_scalar + other.base_scalar)*q, :numerator=>@numerator, :denominator=>@denominator, :signature => @signature)
|
272
403
|
else
|
273
404
|
raise ArgumentError, "Incompatible Units"
|
274
405
|
end
|
@@ -285,9 +416,8 @@ class Unit < Numeric
|
|
285
416
|
def -(other)
|
286
417
|
if Unit === other
|
287
418
|
if self =~ other then
|
288
|
-
|
289
|
-
|
290
|
-
Unit.new(:scalar=>(a.scalar - b.scalar), :numerator=>a.numerator, :denominator=>b.denominator).to(self)
|
419
|
+
q = self.zero? ? 1 : (self.scalar / self.base_scalar)
|
420
|
+
Unit.new(:scalar=>(self.base_scalar - other.base_scalar)*q, :numerator=>@numerator, :denominator=>@denominator, :signature=>@signature)
|
291
421
|
else
|
292
422
|
raise ArgumentError, "Incompatible Units"
|
293
423
|
end
|
@@ -295,14 +425,19 @@ class Unit < Numeric
|
|
295
425
|
other - self
|
296
426
|
else
|
297
427
|
x,y = coerce(other)
|
298
|
-
x
|
428
|
+
y-x
|
299
429
|
end
|
300
430
|
end
|
301
431
|
|
302
432
|
# Multiply two units.
|
303
433
|
def *(other)
|
304
|
-
|
305
|
-
|
434
|
+
case other
|
435
|
+
when Unit
|
436
|
+
opts = Unit.eliminate_terms(@scalar*other.scalar, @numerator + other.numerator ,@denominator + other.denominator)
|
437
|
+
opts.merge!(:signature => @signature + other.signature)
|
438
|
+
Unit.new(opts)
|
439
|
+
when Numeric
|
440
|
+
Unit.new(:scalar=>@scalar*other, :numerator=>@numerator, :denominator=>@denominator, :signature => @signature)
|
306
441
|
else
|
307
442
|
x,y = coerce(other)
|
308
443
|
x * y
|
@@ -312,9 +447,15 @@ class Unit < Numeric
|
|
312
447
|
# Divide two units.
|
313
448
|
# Throws an exception if divisor is 0
|
314
449
|
def /(other)
|
315
|
-
|
450
|
+
case other
|
451
|
+
when Unit
|
452
|
+
raise ZeroDivisionError if other.zero?
|
453
|
+
opts = Unit.eliminate_terms(@scalar/other.scalar, @numerator + other.denominator ,@denominator + other.numerator)
|
454
|
+
opts.merge!(:signature=> @signature - other.signature)
|
455
|
+
Unit.new(opts)
|
456
|
+
when Numeric
|
316
457
|
raise ZeroDivisionError if other.zero?
|
317
|
-
Unit.new(
|
458
|
+
Unit.new(:scalar=>@scalar/other, :numerator=>@numerator, :denominator=>@denominator, :signature => @signature)
|
318
459
|
else
|
319
460
|
x,y = coerce(other)
|
320
461
|
y / x
|
@@ -375,16 +516,16 @@ class Unit < Numeric
|
|
375
516
|
vec = self.unit_signature_vector
|
376
517
|
vec=vec.map {|x| x % n}
|
377
518
|
raise ArgumentError, "Illegal root" unless vec.max == 0
|
378
|
-
num = @numerator.
|
379
|
-
den = @denominator.
|
519
|
+
num = @numerator.dup
|
520
|
+
den = @denominator.dup
|
380
521
|
|
381
|
-
@numerator.uniq
|
522
|
+
for item in @numerator.uniq do
|
382
523
|
x = num.find_all {|i| i==item}.size
|
383
524
|
r = ((x/n)*(n-1)).to_int
|
384
525
|
r.times {|x| num.delete_at(num.index(item))}
|
385
526
|
end
|
386
527
|
|
387
|
-
@denominator.uniq
|
528
|
+
for item in @denominator.uniq do
|
388
529
|
x = den.find_all {|i| i==item}.size
|
389
530
|
r = ((x/n)*(n-1)).to_int
|
390
531
|
r.times {|x| den.delete_at(den.index(item))}
|
@@ -419,7 +560,6 @@ class Unit < Numeric
|
|
419
560
|
return self if FalseClass === other
|
420
561
|
if (Unit === other && other.units =~ /temp(K|C|R|F)/) || (String === other && other =~ /temp(K|C|R|F)/)
|
421
562
|
raise ArgumentError, "Receiver is not a temperature unit" unless self.signature==400
|
422
|
-
return self.to_base.to(other) unless self.is_base?
|
423
563
|
start_unit = self.units
|
424
564
|
target_unit = other.units rescue other
|
425
565
|
q=case start_unit
|
@@ -452,7 +592,8 @@ class Unit < Numeric
|
|
452
592
|
when 'tempR' : @scalar
|
453
593
|
end
|
454
594
|
else
|
455
|
-
|
595
|
+
return self.to_base.to(other) unless self.is_base?
|
596
|
+
#raise ArgumentError, "Unknown temperature conversion requested #{self.numerator}"
|
456
597
|
end
|
457
598
|
target_unit =~ /temp(C|K|F|R)/
|
458
599
|
Unit.new("#{q} deg#{$1}")
|
@@ -466,14 +607,14 @@ class Unit < Numeric
|
|
466
607
|
raise ArgumentError, "Unknown target units"
|
467
608
|
end
|
468
609
|
raise ArgumentError, "Incompatible Units" unless self =~ target
|
469
|
-
one = @numerator.map {|x| @@PREFIX_VALUES[
|
470
|
-
two = @denominator.map {|x| @@PREFIX_VALUES[
|
610
|
+
one = @numerator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
|
611
|
+
two = @denominator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
|
471
612
|
v = one.inject(1) {|product,n| product*n} / two.inject(1) {|product,n| product*n}
|
472
|
-
one = target.numerator.map {|x| @@PREFIX_VALUES[
|
473
|
-
two = target.denominator.map {|x| @@PREFIX_VALUES[
|
613
|
+
one = target.numerator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
|
614
|
+
two = target.denominator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
|
474
615
|
y = one.inject(1) {|product,n| product*n} / two.inject(1) {|product,n| product*n}
|
475
616
|
q = @scalar * v/y
|
476
|
-
Unit.new(:scalar=>q, :numerator=>target.numerator, :denominator=>target.denominator)
|
617
|
+
Unit.new(:scalar=>q, :numerator=>target.numerator, :denominator=>target.denominator, :signature => target.signature)
|
477
618
|
end
|
478
619
|
end
|
479
620
|
alias :>> :to
|
@@ -489,43 +630,46 @@ class Unit < Numeric
|
|
489
630
|
|
490
631
|
# returns the 'unit' part of the Unit object without the scalar
|
491
632
|
def units
|
492
|
-
return "" if @numerator ==
|
633
|
+
return "" if @numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY
|
634
|
+
return @unit_name unless @unit_name.nil?
|
493
635
|
output_n = []
|
494
636
|
output_d =[]
|
495
637
|
num = @numerator.clone.compact
|
496
638
|
den = @denominator.clone.compact
|
497
|
-
if @numerator ==
|
639
|
+
if @numerator == UNITY_ARRAY
|
498
640
|
output_n << "1"
|
499
641
|
else
|
500
642
|
num.each_with_index do |token,index|
|
501
|
-
if token && @@PREFIX_VALUES[
|
502
|
-
output_n << "#{@@OUTPUT_MAP[
|
643
|
+
if token && @@PREFIX_VALUES[token] then
|
644
|
+
output_n << "#{@@OUTPUT_MAP[token]}#{@@OUTPUT_MAP[num[index+1]]}"
|
503
645
|
num[index+1]=nil
|
504
646
|
else
|
505
|
-
output_n << "#{@@OUTPUT_MAP[
|
647
|
+
output_n << "#{@@OUTPUT_MAP[token]}" if token
|
506
648
|
end
|
507
649
|
end
|
508
650
|
end
|
509
|
-
if @denominator ==
|
651
|
+
if @denominator == UNITY_ARRAY
|
510
652
|
output_d = ['1']
|
511
653
|
else
|
512
654
|
den.each_with_index do |token,index|
|
513
|
-
if token && @@PREFIX_VALUES[
|
514
|
-
output_d << "#{@@OUTPUT_MAP[
|
655
|
+
if token && @@PREFIX_VALUES[token] then
|
656
|
+
output_d << "#{@@OUTPUT_MAP[token]}#{@@OUTPUT_MAP[den[index+1]]}"
|
515
657
|
den[index+1]=nil
|
516
658
|
else
|
517
|
-
output_d << "#{@@OUTPUT_MAP[
|
659
|
+
output_d << "#{@@OUTPUT_MAP[token]}" if token
|
518
660
|
end
|
519
661
|
end
|
520
662
|
end
|
521
663
|
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]}" : ''))}
|
522
664
|
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]}" : ''))}
|
523
|
-
"#{on.join('*')}#{od == ['1'] ? '': '/'+od.join('*')}".strip
|
665
|
+
out = "#{on.join('*')}#{od == ['1'] ? '': '/'+od.join('*')}".strip
|
666
|
+
@unit_name = out unless self.kind == :temperature
|
667
|
+
return out
|
524
668
|
end
|
525
669
|
|
526
670
|
# negates the scalar of the Unit
|
527
671
|
def -@
|
528
|
-
Unit.new(
|
672
|
+
Unit.new(-@scalar,@numerator,@denominator)
|
529
673
|
end
|
530
674
|
|
531
675
|
# returns abs of scalar, without the units
|
@@ -534,18 +678,18 @@ class Unit < Numeric
|
|
534
678
|
end
|
535
679
|
|
536
680
|
def ceil
|
537
|
-
Unit.new(
|
681
|
+
Unit.new(@scalar.ceil, @numerator, @denominator)
|
538
682
|
end
|
539
683
|
|
540
684
|
def floor
|
541
|
-
Unit.new(
|
685
|
+
Unit.new(@scalar.floor, @numerator, @denominator)
|
542
686
|
end
|
543
687
|
|
544
688
|
# changes internal scalar to an integer, but retains the units
|
545
689
|
# if unitless, returns an int
|
546
690
|
def to_int
|
547
691
|
return @scalar.to_int if unitless?
|
548
|
-
Unit.new(
|
692
|
+
Unit.new(@scalar.to_int, @numerator, @denominator)
|
549
693
|
end
|
550
694
|
|
551
695
|
# Tries to make a Time object from current unit
|
@@ -557,7 +701,7 @@ class Unit < Numeric
|
|
557
701
|
alias :truncate :to_int
|
558
702
|
|
559
703
|
def round
|
560
|
-
Unit.new(
|
704
|
+
Unit.new(@scalar.round, @numerator, @denominator)
|
561
705
|
end
|
562
706
|
|
563
707
|
# true if scalar is zero
|
@@ -567,16 +711,16 @@ class Unit < Numeric
|
|
567
711
|
|
568
712
|
# '5 min'.unit.ago
|
569
713
|
def ago
|
570
|
-
|
714
|
+
self.before
|
571
715
|
end
|
572
716
|
|
573
717
|
# '5 min'.before(time)
|
574
718
|
def before(time_point = ::Time.now)
|
575
719
|
raise ArgumentError, "Must specify a Time" unless time_point
|
576
720
|
if String === time_point
|
577
|
-
time_point.time - self
|
721
|
+
time_point.time - self rescue time_point.datetime - self
|
578
722
|
else
|
579
|
-
time_point - self
|
723
|
+
time_point - self rescue time_point.to_datetime - self
|
580
724
|
end
|
581
725
|
end
|
582
726
|
alias :before_now :before
|
@@ -585,7 +729,7 @@ class Unit < Numeric
|
|
585
729
|
def since(time_point = ::Time.now)
|
586
730
|
case time_point
|
587
731
|
when Time: (Time.now - time_point).unit('s').to(self)
|
588
|
-
when DateTime: (DateTime.now - time_point).unit('d').to(self)
|
732
|
+
when DateTime, Date: (DateTime.now - time_point).unit('d').to(self)
|
589
733
|
when String:
|
590
734
|
(DateTime.now - time_point.time(:context=>:past)).unit('d').to(self)
|
591
735
|
else
|
@@ -597,7 +741,7 @@ class Unit < Numeric
|
|
597
741
|
def until(time_point = ::Time.now)
|
598
742
|
case time_point
|
599
743
|
when Time: (time_point - Time.now).unit('s').to(self)
|
600
|
-
when DateTime: (time_point - DateTime.now).unit('d').to(self)
|
744
|
+
when DateTime, Date: (time_point - DateTime.now).unit('d').to(self)
|
601
745
|
when String:
|
602
746
|
r = (time_point.time(:context=>:future) - DateTime.now)
|
603
747
|
Time === time_point.time ? r.unit('s').to(self) : r.unit('d').to(self)
|
@@ -610,9 +754,9 @@ class Unit < Numeric
|
|
610
754
|
def from(time_point = ::Time.now)
|
611
755
|
raise ArgumentError, "Must specify a Time" unless time_point
|
612
756
|
if String === time_point
|
613
|
-
time_point.time + self
|
757
|
+
time_point.time + self rescue time_point.datetime + self
|
614
758
|
else
|
615
|
-
time_point + self
|
759
|
+
time_point + self rescue time_point.to_datetime + self
|
616
760
|
end
|
617
761
|
end
|
618
762
|
alias :after :from
|
@@ -621,7 +765,7 @@ class Unit < Numeric
|
|
621
765
|
def succ
|
622
766
|
raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
|
623
767
|
q = @scalar.to_i.succ
|
624
|
-
Unit.new(
|
768
|
+
Unit.new(q, @numerator, @denominator)
|
625
769
|
end
|
626
770
|
|
627
771
|
# Protected and Private Functions that should only be called from this class
|
@@ -629,6 +773,7 @@ class Unit < Numeric
|
|
629
773
|
|
630
774
|
|
631
775
|
def update_base_scalar
|
776
|
+
return @base_scalar unless @base_scalar.nil?
|
632
777
|
if self.is_base?
|
633
778
|
@base_scalar = @scalar
|
634
779
|
@signature = unit_signature
|
@@ -653,37 +798,42 @@ class Unit < Numeric
|
|
653
798
|
def unit_signature_vector
|
654
799
|
return self.to_base.unit_signature_vector unless self.is_base?
|
655
800
|
result = self
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
801
|
+
vector = Array.new(SIGNATURE_VECTOR.size,0)
|
802
|
+
for element in @numerator
|
803
|
+
if r=@@ALL_UNIT_DEFINITIONS[element]
|
804
|
+
n = SIGNATURE_VECTOR.index(r[2])
|
805
|
+
vector[n] = vector[n] + 1 if n
|
806
|
+
end
|
807
|
+
end
|
808
|
+
for element in @denominator
|
809
|
+
if r=@@ALL_UNIT_DEFINITIONS[element]
|
810
|
+
n = SIGNATURE_VECTOR.index(r[2])
|
811
|
+
vector[n] = vector[n] - 1 if n
|
812
|
+
end
|
661
813
|
end
|
662
814
|
vector
|
663
815
|
end
|
664
816
|
|
665
817
|
def replace_temperature
|
666
|
-
return self unless self.
|
818
|
+
return self unless self.kind == :temperature && self.units =~ /temp(R|K|F|C)/
|
667
819
|
un = $1
|
668
|
-
target = self.units
|
669
820
|
@numerator = case un
|
670
|
-
when 'R' :
|
671
|
-
when 'C' :
|
672
|
-
when 'F' :
|
673
|
-
when 'K' :
|
821
|
+
when 'R' : RANKINE
|
822
|
+
when 'C' : CELCIUS
|
823
|
+
when 'F' : FARENHEIT
|
824
|
+
when 'K' : KELVIN
|
674
825
|
end
|
826
|
+
@unit_name = nil
|
675
827
|
r= self.to("tempK")
|
676
|
-
|
677
|
-
@denominator = r.denominator
|
678
|
-
@scalar = r.scalar
|
828
|
+
copy(r)
|
679
829
|
end
|
680
|
-
|
681
|
-
|
830
|
+
|
682
831
|
private
|
683
832
|
|
684
833
|
def initialize_copy(other)
|
685
|
-
@numerator = other.numerator.
|
686
|
-
@denominator = other.denominator.
|
834
|
+
@numerator = other.numerator.dup
|
835
|
+
@denominator = other.denominator.dup
|
836
|
+
|
687
837
|
end
|
688
838
|
|
689
839
|
# calculates the unit signature id for use in comparing compatible units and simplification
|
@@ -695,17 +845,18 @@ class Unit < Numeric
|
|
695
845
|
# http://ieeexplore.ieee.org/Xplore/login.jsp?url=/iel1/32/9079/00403789.pdf?isnumber=9079&prod=JNL&arnumber=403789&arSt=651&ared=661&arAuthor=Novak%2C+G.S.%2C+Jr.
|
696
846
|
#
|
697
847
|
def unit_signature
|
848
|
+
return @signature unless @signature.nil?
|
698
849
|
vector = unit_signature_vector
|
699
850
|
vector.each_with_index {|item,index| vector[index] = item * 20**index}
|
700
851
|
@signature=vector.inject(0) {|sum,n| sum+n}
|
701
852
|
end
|
702
853
|
|
703
854
|
def self.eliminate_terms(q, n, d)
|
704
|
-
num = n.
|
705
|
-
den = d.
|
855
|
+
num = n.dup
|
856
|
+
den = d.dup
|
706
857
|
|
707
|
-
num.delete_if {|v| v ==
|
708
|
-
den.delete_if {|v| v ==
|
858
|
+
num.delete_if {|v| v == UNITY}
|
859
|
+
den.delete_if {|v| v == UNITY}
|
709
860
|
combined = Hash.new(0)
|
710
861
|
|
711
862
|
i = 0
|
@@ -718,7 +869,7 @@ class Unit < Numeric
|
|
718
869
|
k = num[i]
|
719
870
|
i += 1
|
720
871
|
end
|
721
|
-
combined[k] += 1 unless k.nil? || k ==
|
872
|
+
combined[k] += 1 unless k.nil? || k == UNITY
|
722
873
|
end
|
723
874
|
|
724
875
|
j = 0
|
@@ -731,19 +882,19 @@ class Unit < Numeric
|
|
731
882
|
k = den[j]
|
732
883
|
j += 1
|
733
884
|
end
|
734
|
-
combined[k] -= 1 unless k.nil? || k ==
|
885
|
+
combined[k] -= 1 unless k.nil? || k == UNITY
|
735
886
|
end
|
736
887
|
|
737
888
|
num = []
|
738
889
|
den = []
|
739
|
-
|
890
|
+
for key, value in combined do
|
740
891
|
case
|
741
892
|
when value > 0 : value.times {num << key}
|
742
893
|
when value < 0 : value.abs.times {den << key}
|
743
894
|
end
|
744
895
|
end
|
745
|
-
num =
|
746
|
-
den =
|
896
|
+
num = UNITY_ARRAY if num.empty?
|
897
|
+
den = UNITY_ARRAY if den.empty?
|
747
898
|
{:scalar=>q, :numerator=>num.flatten.compact, :denominator=>den.flatten.compact}
|
748
899
|
end
|
749
900
|
|
@@ -759,36 +910,47 @@ class Unit < Numeric
|
|
759
910
|
# "GPa" -- creates a unit with scalar 1 with units 'GPa'
|
760
911
|
# 6'4" -- recognized as 6 feet + 4 inches
|
761
912
|
# 8 lbs 8 oz -- recognized as 8 lbs + 8 ounces
|
762
|
-
def parse(
|
763
|
-
|
764
|
-
|
913
|
+
def parse(passed_unit_string="0")
|
914
|
+
|
915
|
+
unit_string = passed_unit_string.dup
|
916
|
+
if unit_string =~ /\$\s*(#{NUMBER_REGEX})/
|
917
|
+
unit_string = "#{$1} USD"
|
918
|
+
end
|
919
|
+
if unit_string =~ /(#{SCI_NUMBER})\s*%/
|
920
|
+
unit_string = "#{$1} percent"
|
921
|
+
end
|
922
|
+
|
923
|
+
unit_string =~ NUMBER_REGEX
|
924
|
+
unit = @@cached_units[$2]
|
925
|
+
mult = ($1.empty? ? 1.0 : $1.to_f) rescue 1.0
|
926
|
+
if unit
|
927
|
+
copy(unit)
|
928
|
+
@scalar *= mult
|
929
|
+
@base_scalar *= mult
|
930
|
+
return self
|
931
|
+
end
|
932
|
+
|
765
933
|
unit_string.gsub!(/[<>]/,"")
|
766
934
|
|
767
935
|
# Special processing for unusual unit strings
|
768
936
|
# feet -- 6'5"
|
769
|
-
feet, inches = unit_string.scan(
|
937
|
+
feet, inches = unit_string.scan(FEET_INCH_REGEX)[0]
|
770
938
|
if (feet && inches)
|
771
939
|
result = Unit.new("#{feet} ft") + Unit.new("#{inches} inches")
|
772
|
-
|
773
|
-
@numerator = result.numerator
|
774
|
-
@denominator = result.denominator
|
775
|
-
@base_scalar = result.base_scalar
|
940
|
+
copy(result)
|
776
941
|
return self
|
777
942
|
end
|
778
943
|
|
779
944
|
# weight -- 8 lbs 12 oz
|
780
|
-
pounds, oz = unit_string.scan(
|
945
|
+
pounds, oz = unit_string.scan(LBS_OZ_REGEX)[0]
|
781
946
|
if (pounds && oz)
|
782
947
|
result = Unit.new("#{pounds} lbs") + Unit.new("#{oz} oz")
|
783
|
-
|
784
|
-
@numerator = result.numerator
|
785
|
-
@denominator = result.denominator
|
786
|
-
@base_scalar = result.base_scalar
|
948
|
+
copy(result)
|
787
949
|
return self
|
788
950
|
end
|
789
|
-
@scalar, top, bottom = unit_string.scan(
|
951
|
+
@scalar, top, bottom = unit_string.scan(UNIT_STRING_REGEX)[0] #parse the string into parts
|
790
952
|
|
791
|
-
top.scan(
|
953
|
+
top.scan(TOP_REGEX).each do |item|
|
792
954
|
n = item[1].to_i
|
793
955
|
x = "#{item[0]} "
|
794
956
|
case
|
@@ -796,33 +958,27 @@ class Unit < Numeric
|
|
796
958
|
when n<0 : bottom = "#{bottom} #{x * -n}"; top.gsub!(/#{item[0]}(\^|\*\*)#{n}/,"")
|
797
959
|
end
|
798
960
|
end
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
end
|
810
|
-
|
811
|
-
@numerator = top.scan(/(#{@@PREFIX_REGEX})*?(#{@@UNIT_REGEX})\b/).delete_if {|x| x.empty?}.compact if top
|
812
|
-
@denominator = bottom.scan(/(#{@@PREFIX_REGEX})*?(#{@@UNIT_REGEX})\b/).delete_if {|x| x.empty?}.compact if bottom
|
961
|
+
bottom.gsub!(BOTTOM_REGEX) {|s| "#{$1} " * $2.to_i} if bottom
|
962
|
+
@scalar = @scalar.to_f unless @scalar.nil? || @scalar.empty?
|
963
|
+
@scalar = 1 unless @scalar.kind_of? Numeric
|
964
|
+
|
965
|
+
@numerator ||= UNITY_ARRAY
|
966
|
+
@denominator ||= UNITY_ARRAY
|
967
|
+
@numerator = top.scan(@@UNIT_MATCH_REGEX).delete_if {|x| x.empty?}.compact if top
|
968
|
+
@denominator = bottom.scan(@@UNIT_MATCH_REGEX).delete_if {|x| x.empty?}.compact if bottom
|
969
|
+
us = "#{(top || '' + bottom || '')}".to_s.gsub(@@UNIT_MATCH_REGEX,'').gsub(/[\d\*, "'_^\/\$]/,'')
|
970
|
+
raise( ArgumentError, "'#{passed_unit_string}' Unit not recognized (#{us})") unless us.empty?
|
813
971
|
|
814
972
|
@numerator = @numerator.map do |item|
|
815
|
-
item.map {|x| Regexp.escape(x) if x}
|
816
973
|
@@PREFIX_MAP[item[0]] ? [@@PREFIX_MAP[item[0]], @@UNIT_MAP[item[1]]] : [@@UNIT_MAP[item[1]]]
|
817
974
|
end.flatten.compact.delete_if {|x| x.empty?}
|
818
975
|
|
819
976
|
@denominator = @denominator.map do |item|
|
820
|
-
item.map {|x| Regexp.escape(x) if x}
|
821
977
|
@@PREFIX_MAP[item[0]] ? [@@PREFIX_MAP[item[0]], @@UNIT_MAP[item[1]]] : [@@UNIT_MAP[item[1]]]
|
822
978
|
end.flatten.compact.delete_if {|x| x.empty?}
|
823
979
|
|
824
|
-
@numerator =
|
825
|
-
@denominator =
|
980
|
+
@numerator = UNITY_ARRAY if @numerator.empty?
|
981
|
+
@denominator = UNITY_ARRAY if @denominator.empty?
|
826
982
|
self
|
827
983
|
end
|
828
984
|
end
|
@@ -832,9 +988,11 @@ end
|
|
832
988
|
# Date.today + U"1 week" => gives today+1 week
|
833
989
|
class Date
|
834
990
|
alias :unit_date_add :+
|
835
|
-
def +
|
991
|
+
def +unit
|
836
992
|
case unit
|
837
|
-
when Unit:
|
993
|
+
when Unit:
|
994
|
+
unit = unit.to('d').round if ['y', 'decade', 'century'].include? unit.units
|
995
|
+
unit_date_add(unit.to('day').scalar)
|
838
996
|
when Time: unit_date_add(unit.to_datetime)
|
839
997
|
else
|
840
998
|
unit_date_add(unit)
|
@@ -842,9 +1000,11 @@ class Date
|
|
842
1000
|
end
|
843
1001
|
|
844
1002
|
alias :unit_date_sub :-
|
845
|
-
def -
|
1003
|
+
def -unit
|
846
1004
|
case unit
|
847
|
-
when Unit:
|
1005
|
+
when Unit:
|
1006
|
+
unit = unit.to('d').round if ['y', 'decade', 'century'].include? unit.units
|
1007
|
+
unit_date_sub(unit.to('day').scalar)
|
848
1008
|
when Time: unit_date_sub(unit.to_datetime)
|
849
1009
|
else
|
850
1010
|
unit_date_sub(unit)
|
@@ -864,9 +1024,10 @@ class Date
|
|
864
1024
|
end
|
865
1025
|
|
866
1026
|
class Object
|
867
|
-
def Unit(other)
|
1027
|
+
def Unit(*other)
|
868
1028
|
other.to_unit
|
869
1029
|
end
|
1030
|
+
|
870
1031
|
alias :U :Unit
|
871
1032
|
alias :u :Unit
|
872
1033
|
end
|
@@ -874,7 +1035,7 @@ end
|
|
874
1035
|
# make a unitless unit with a given scalar
|
875
1036
|
class Numeric
|
876
1037
|
def to_unit(other = nil)
|
877
|
-
other ? Unit.new(self
|
1038
|
+
other ? Unit.new(self, other) : Unit.new(self)
|
878
1039
|
end
|
879
1040
|
alias :unit :to_unit
|
880
1041
|
alias :u :to_unit
|
@@ -893,7 +1054,7 @@ end
|
|
893
1054
|
# make a string into a unit
|
894
1055
|
class String
|
895
1056
|
def to_unit(other = nil)
|
896
|
-
other ? Unit.new(self)
|
1057
|
+
other ? Unit.new(self).to(other) : Unit.new(self)
|
897
1058
|
end
|
898
1059
|
alias :unit :to_unit
|
899
1060
|
alias :u :to_unit
|
@@ -961,6 +1122,16 @@ class String
|
|
961
1122
|
raise RuntimeError, "Invalid Time String" if r == DateTime.new
|
962
1123
|
return r
|
963
1124
|
end
|
1125
|
+
|
1126
|
+
def to_date(options={})
|
1127
|
+
begin
|
1128
|
+
r = Chronic.parse(self,options).to_date
|
1129
|
+
rescue
|
1130
|
+
r = Date.civil(*ParseDate.parsedate(self)[0..5].compact)
|
1131
|
+
end
|
1132
|
+
raise RuntimeError, 'Invalid Date String' if r == Date.new
|
1133
|
+
return r
|
1134
|
+
end
|
964
1135
|
|
965
1136
|
def datetime(options = {})
|
966
1137
|
self.to_datetime(options) rescue self.to_time(options)
|
@@ -994,6 +1165,10 @@ class Time
|
|
994
1165
|
DateTime.civil(1970,1,1)+(self.to_f+self.gmt_offset)/86400
|
995
1166
|
end
|
996
1167
|
|
1168
|
+
def to_date
|
1169
|
+
Date.civil(1970,1,1)+(self.to_f+self.gmt_offset)/86400
|
1170
|
+
end
|
1171
|
+
|
997
1172
|
def +(other)
|
998
1173
|
case other
|
999
1174
|
when Unit: unit_add(other.to('s').scalar)
|
@@ -1085,4 +1260,6 @@ module Math
|
|
1085
1260
|
module_function :tan
|
1086
1261
|
module_function :unit_tanh
|
1087
1262
|
module_function :tanh
|
1088
|
-
end
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
Unit.setup
|
data/lib/units.rb
CHANGED
@@ -71,6 +71,7 @@ UNIT_DEFINITIONS = {
|
|
71
71
|
#area
|
72
72
|
'<hectare>'=>[%w{hectare}, 10000, :area, %w{<meter> <meter>}],
|
73
73
|
'<acre>'=>[%w(acre acres), 4046.85642, :area, %w{<meter> <meter>}],
|
74
|
+
'<sqft>'=>[%w(sqft), 1, :area, %w{<feet> <feet>}],
|
74
75
|
|
75
76
|
#volume
|
76
77
|
'<liter>' => [%w{l L liter liters litre litres}, 0.001, :volume, %w{<meter> <meter> <meter>}],
|
@@ -87,6 +88,9 @@ UNIT_DEFINITIONS = {
|
|
87
88
|
'<mph>' => [%w{mph}, 0.44704, :speed, %w{<meter>}, %w{<second>}],
|
88
89
|
'<knot>' => [%w{kn knot knots}, 0.514444444, :speed, %w{<meter>}, %w{<second>}],
|
89
90
|
'<fps>' => [%w{fps}, 0.3048, :speed, %w{<meter>}, %w{<second>}],
|
91
|
+
|
92
|
+
#acceleration
|
93
|
+
'<gee>' => [%w{gee}, 9.80655, :acceleration, %w{<meter>}, %w{<second> <second>}],
|
90
94
|
|
91
95
|
#temperature_difference
|
92
96
|
'<kelvin>' => [%w{degK kelvin Kelvin}, 1.0, :temperature, %w{<kelvin>}],
|
@@ -184,15 +188,15 @@ UNIT_DEFINITIONS = {
|
|
184
188
|
'<steradian>' => [%w{sr steradian steradians}, 1.0, :solid_angle, %w{<steradian>}],
|
185
189
|
|
186
190
|
#rotation
|
187
|
-
'<rotation>' => [%w{rotation}, 2.0*Math::PI, :
|
188
|
-
'<rpm>' =>[%w{rpm}, 2.0*Math::PI / 60.0, :
|
191
|
+
'<rotation>' => [%w{rotation}, 2.0*Math::PI, :angle, %w{<radian>}],
|
192
|
+
'<rpm>' =>[%w{rpm}, 2.0*Math::PI / 60.0, :angular_velocity, %w{<radian>}, %w{<second>}],
|
189
193
|
|
190
194
|
#memory
|
191
195
|
'<byte>' =>[%w{B byte}, 1.0, :memory, %w{<byte>}],
|
192
196
|
'<bit>' =>[%w{b bit}, 0.125, :memory, %w{<byte>}],
|
193
197
|
|
194
|
-
#
|
195
|
-
'<dollar>'=>[
|
198
|
+
#currency
|
199
|
+
'<dollar>'=>[%w{USD dollar}, 1.0, :currency, %w{<dollar>}],
|
196
200
|
'<cents>' =>[%w{cents}, 0.01, :currency, %w{<dollar>}],
|
197
201
|
|
198
202
|
#luminosity
|
@@ -204,25 +208,29 @@ UNIT_DEFINITIONS = {
|
|
204
208
|
'<watt>' => [%w{W watt watts}, 1.0, :power, %w{<kilogram> <meter> <meter>}, %w{<second> <second> <second>}],
|
205
209
|
'<horsepower>' => [%w{hp horsepower}, 745.699872, :power, %w{<kilogram> <meter> <meter>}, %w{<second> <second> <second>}],
|
206
210
|
|
207
|
-
#
|
211
|
+
#radiation
|
208
212
|
'<gray>' => [%w{Gy gray grays}, 1.0, :radiation, %w{<meter> <meter>}, %w{<second> <second>}],
|
209
213
|
'<roentgen>' => [%w{R roentgen}, 0.009330, :radiation, %w{<meter> <meter>}, %w{<second> <second>}],
|
210
214
|
'<sievert>' => [%w{Sv sievert sieverts}, 1.0, :radiation, %w{<meter> <meter>}, %w{<second> <second>}],
|
211
215
|
'<becquerel>' => [%w{Bq bequerel bequerels}, 1.0, :radiation, %w{<1>},%w{<second>}],
|
212
216
|
'<curie>' => [%w{Ci curie curies}, 3.7e10, :radiation, %w{<1>},%w{<second>}],
|
213
|
-
|
214
|
-
|
217
|
+
|
218
|
+
# rate
|
219
|
+
'<cpm>' => [%w{cpm}, 1.0/60.0, :rate, %w{<count>},%w{<second>}],
|
220
|
+
'<dpm>' => [%w{dpm}, 1.0/60.0, :rate, %w{<count>},%w{<second>}],
|
221
|
+
'<bpm>' => [%w{bpm}, 1.0/60.0, :rate, %w{<count>},%w{<second>}],
|
215
222
|
|
216
223
|
#other
|
224
|
+
'<cell>' => [%w{cells cell}, 1, :counting, %w{<each>}],
|
217
225
|
'<each>' => [%w{each}, 1.0, :counting, %w{<each>}],
|
218
226
|
'<count>' => [%w{count}, 1.0, :counting, %w{<each>}],
|
219
227
|
'<base-pair>' => [%w{bp}, 1.0, :counting, %w{<each>}],
|
220
228
|
'<nucleotide>' => [%w{nt}, 1.0, :counting, %w{<each>}],
|
221
|
-
'<molecule>' => [%w{molecule molecules}, 1.0, :counting, %w{<
|
229
|
+
'<molecule>' => [%w{molecule molecules}, 1.0, :counting, %w{<1>}],
|
222
230
|
'<dozen>' => [%w{doz dz dozen},12.0,:prefix_only, %w{<each>}],
|
223
|
-
'<percent>'=> [%w{% percent}, 0.01, :prefix_only, %w{<
|
224
|
-
'<ppm>' => [%w{ppm},1e-6,:prefix_only, %w{<
|
225
|
-
'<ppt>' => [%w{ppt},1e-9,:prefix_only, %w{<
|
231
|
+
'<percent>'=> [%w{% percent}, 0.01, :prefix_only, %w{<1>}],
|
232
|
+
'<ppm>' => [%w{ppm},1e-6,:prefix_only, %w{<1>}],
|
233
|
+
'<ppt>' => [%w{ppt},1e-9,:prefix_only, %w{<1>}],
|
226
234
|
'<gross>' => [%w{gr gross},144.0, :prefix_only, %w{<dozen> <dozen>}],
|
227
235
|
'<decibel>' => [%w{dB decibel decibels}, 1.0, :logarithmic, %w{<decibel>}]
|
228
236
|
|
data/test/test_ruby-units.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require 'rubygems'
|
3
2
|
require 'ruby-units'
|
4
|
-
require 'yaml'
|
5
3
|
require 'uncertain'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'yaml'
|
6
6
|
require 'chronic'
|
7
7
|
|
8
8
|
class Unit < Numeric
|
9
9
|
@@USER_DEFINITIONS = {'<inchworm>' => [%w{inworm inchworm}, 0.0254, :length, %w{<meter>} ],
|
10
|
-
'<cell>' => [%w{cells cell}, 1, :counting, %w{<each>}],
|
11
10
|
'<habenero>' => [%{degH}, 100, :temperature, %w{<celcius>}]}
|
12
11
|
Unit.setup
|
13
12
|
end
|
@@ -226,7 +225,7 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
226
225
|
end
|
227
226
|
|
228
227
|
def test_create_from_array
|
229
|
-
unit1 = Unit.new(
|
228
|
+
unit1 = Unit.new(1, "mm^2", "ul^2")
|
230
229
|
assert_equal 1, unit1.scalar
|
231
230
|
assert_equal ['<milli>','<meter>','<milli>','<meter>'], unit1.numerator
|
232
231
|
assert_equal ['<micro>','<liter>','<micro>','<liter>'], unit1.denominator
|
@@ -712,8 +711,28 @@ class TestRubyUnits < Test::Unit::TestCase
|
|
712
711
|
end
|
713
712
|
|
714
713
|
def test_format
|
715
|
-
assert_equal "%0.2f" % "1 mm".unit, "1.00 mm"
|
716
|
-
|
714
|
+
assert_equal "%0.2f" % "1 mm".unit, "1.00 mm"
|
715
|
+
end
|
716
|
+
|
717
|
+
def test_bad_units
|
718
|
+
assert_raises(ArgumentError) { '1 doohickey / thingamabob'.unit}
|
719
|
+
assert_raises(ArgumentError) { '1 minimeter'.unit}
|
717
720
|
end
|
718
721
|
|
722
|
+
def test_currency
|
723
|
+
assert_nothing_raised {a = "$1".unit}
|
724
|
+
end
|
725
|
+
|
726
|
+
def test_kind
|
727
|
+
a = "1 mm".unit
|
728
|
+
assert_equal a.kind, :length
|
729
|
+
end
|
730
|
+
|
731
|
+
def test_percent
|
732
|
+
assert_nothing_raised {
|
733
|
+
z = "1 percent".unit
|
734
|
+
a = "1%".unit
|
735
|
+
b = "0.01%".unit
|
736
|
+
}
|
737
|
+
end
|
719
738
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: ruby-units
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2006-
|
6
|
+
version: 0.3.1
|
7
|
+
date: 2006-10-02 00:00:00 -04:00
|
8
8
|
summary: A model that performs unit conversions and unit math
|
9
9
|
require_paths:
|
10
10
|
- lib
|