ruby-units 0.2.3 → 0.3.1
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 +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
|