ruby-units 2.0.1 → 2.1.0

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.
@@ -25,14 +25,14 @@ module RubyUnits
25
25
  class Unit < Numeric
26
26
  VERSION = Unit::Version::STRING
27
27
  @@definitions = {}
28
- @@PREFIX_VALUES = {}
29
- @@PREFIX_MAP = {}
30
- @@UNIT_MAP = {}
31
- @@UNIT_VALUES = {}
32
- @@UNIT_REGEX = nil
33
- @@UNIT_MATCH_REGEX = nil
34
- UNITY = '<1>'
35
- UNITY_ARRAY = [UNITY]
28
+ @@prefix_values = {}
29
+ @@prefix_map = {}
30
+ @@unit_map = {}
31
+ @@unit_values = {}
32
+ @@unit_regex = nil
33
+ @@unit_match_regex = nil
34
+ UNITY = '<1>'.freeze
35
+ UNITY_ARRAY = [UNITY].freeze
36
36
  # ideally we would like to generate this regex from the alias for a 'feet' and 'inches', but they aren't
37
37
  # defined at the point in the code where we need this regex.
38
38
  FEET_INCH_UNITS_REGEX = /(?:'|ft|feet)\s*(\d+)\s*(?:"|in|inch(?:es)?)/
@@ -41,127 +41,133 @@ module RubyUnits
41
41
  # defined at the point in the code where we need this regex.
42
42
  LBS_OZ_UNIT_REGEX = /(?:#|lbs?|pounds?|pound-mass)+[\s,]*(\d+)\s*(?:ozs?|ounces?)/
43
43
  LBS_OZ_REGEX = /(\d+)\s*#{LBS_OZ_UNIT_REGEX}/
44
+ # ideally we would like to generate this regex from the alias for a 'stone' and 'pound', but they aren't
45
+ # defined at the point in the code where we need this regex.
46
+ # also note that the plural of 'stone' is still 'stone', but we accept 'stones' anyway.
47
+ STONE_LB_UNIT_REGEX = /(?:sts?|stones?)+[\s,]*(\d+)\s*(?:#|lbs?|pounds?|pound-mass)*/
48
+ STONE_LB_REGEX = /(\d+)\s*#{STONE_LB_UNIT_REGEX}/
44
49
  TIME_REGEX = /(\d+)*:(\d+)*:*(\d+)*[:,]*(\d+)*/
45
- SCI_NUMBER = %r{([+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*)}
46
- RATIONAL_NUMBER = /\(?([+-])?(\d+[ -])?(\d+)\/(\d+)\)?/
50
+ SCI_NUMBER = /([+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*)/
51
+ RATIONAL_NUMBER = %r{\(?([+-])?(\d+[ -])?(\d+)\/(\d+)\)?}
47
52
  COMPLEX_NUMBER = /#{SCI_NUMBER}?#{SCI_NUMBER}i\b/
48
53
  NUMBER_REGEX = /#{SCI_NUMBER}*\s*(.+)?/
49
- UNIT_STRING_REGEX = /#{SCI_NUMBER}*\s*([^\/]*)\/*(.+)*/
54
+ UNIT_STRING_REGEX = %r{#{SCI_NUMBER}*\s*([^\/]*)\/*(.+)*}
50
55
  TOP_REGEX = /([^ \*]+)(?:\^|\*\*)([\d-]+)/
51
56
  BOTTOM_REGEX = /([^* ]+)(?:\^|\*\*)(\d+)/
52
57
  NUMBER_UNIT_REGEX = /#{SCI_NUMBER}?(.*)/
53
58
  COMPLEX_REGEX = /#{COMPLEX_NUMBER}\s?(.+)?/
54
59
  RATIONAL_REGEX = /#{RATIONAL_NUMBER}\s?(.+)?/
55
- KELVIN = ['<kelvin>']
56
- FAHRENHEIT = ['<fahrenheit>']
57
- RANKINE = ['<rankine>']
58
- CELSIUS = ['<celsius>']
59
- @@TEMP_REGEX = nil
60
- SIGNATURE_VECTOR = [
61
- :length,
62
- :time,
63
- :temperature,
64
- :mass,
65
- :current,
66
- :substance,
67
- :luminosity,
68
- :currency,
69
- :information,
70
- :angle
71
- ]
72
- @@KINDS = {
73
- -312078 => :elastance,
74
- -312058 => :resistance,
75
- -312038 => :inductance,
76
- -152040 => :magnetism,
77
- -152038 => :magnetism,
78
- -152058 => :potential,
79
- -7997 => :specific_volume,
80
- -79 => :snap,
81
- -59 => :jolt,
82
- -39 => :acceleration,
83
- -38 => :radiation,
84
- -20 => :frequency,
85
- -19 => :speed,
86
- -18 => :viscosity,
87
- -17 => :volumetric_flow,
88
- -1 => :wavenumber,
89
- 0 => :unitless,
90
- 1 => :length,
91
- 2 => :area,
92
- 3 => :volume,
93
- 20 => :time,
94
- 400 => :temperature,
95
- 7941 => :yank,
96
- 7942 => :power,
97
- 7959 => :pressure,
98
- 7962 => :energy,
99
- 7979 => :viscosity,
100
- 7961 => :force,
101
- 7981 => :momentum,
102
- 7982 => :angular_momentum,
103
- 7997 => :density,
104
- 7998 => :area_density,
105
- 8000 => :mass,
106
- 152020 => :radiation_exposure,
107
- 159999 => :magnetism,
108
- 160000 => :current,
109
- 160020 => :charge,
110
- 312058 => :conductance,
111
- 312078 => :capacitance,
112
- 3199980 => :activity,
113
- 3199997 => :molar_concentration,
114
- 3200000 => :substance,
115
- 63999998 => :illuminance,
116
- 64000000 => :luminous_power,
117
- 1280000000 => :currency,
118
- 25600000000 => :information,
119
- 511999999980 => :angular_velocity,
120
- 512000000000 => :angle
121
- }
60
+ KELVIN = ['<kelvin>'].freeze
61
+ FAHRENHEIT = ['<fahrenheit>'].freeze
62
+ RANKINE = ['<rankine>'].freeze
63
+ CELSIUS = ['<celsius>'].freeze
64
+ @@temp_regex = nil
65
+ SIGNATURE_VECTOR = %i(
66
+ length
67
+ time
68
+ temperature
69
+ mass
70
+ current
71
+ substance
72
+ luminosity
73
+ currency
74
+ information
75
+ angle
76
+ ).freeze
77
+ @@kinds = {
78
+ -312_078 => :elastance,
79
+ -312_058 => :resistance,
80
+ -312_038 => :inductance,
81
+ -152_040 => :magnetism,
82
+ -152_038 => :magnetism,
83
+ -152_058 => :potential,
84
+ -7997 => :specific_volume,
85
+ -79 => :snap,
86
+ -59 => :jolt,
87
+ -39 => :acceleration,
88
+ -38 => :radiation,
89
+ -20 => :frequency,
90
+ -19 => :speed,
91
+ -18 => :viscosity,
92
+ -17 => :volumetric_flow,
93
+ -1 => :wavenumber,
94
+ 0 => :unitless,
95
+ 1 => :length,
96
+ 2 => :area,
97
+ 3 => :volume,
98
+ 20 => :time,
99
+ 400 => :temperature,
100
+ 7941 => :yank,
101
+ 7942 => :power,
102
+ 7959 => :pressure,
103
+ 7962 => :energy,
104
+ 7979 => :viscosity,
105
+ 7961 => :force,
106
+ 7981 => :momentum,
107
+ 7982 => :angular_momentum,
108
+ 7997 => :density,
109
+ 7998 => :area_density,
110
+ 8000 => :mass,
111
+ 152_020 => :radiation_exposure,
112
+ 159_999 => :magnetism,
113
+ 160_000 => :current,
114
+ 160_020 => :charge,
115
+ 312_058 => :conductance,
116
+ 312_078 => :capacitance,
117
+ 3_199_980 => :activity,
118
+ 3_199_997 => :molar_concentration,
119
+ 3_200_000 => :substance,
120
+ 63_999_998 => :illuminance,
121
+ 64_000_000 => :luminous_power,
122
+ 1_280_000_000 => :currency,
123
+ 25_600_000_000 => :information,
124
+ 511_999_999_980 => :angular_velocity,
125
+ 512_000_000_000 => :angle
126
+ }.freeze
122
127
  @@cached_units = {}
123
128
  @@base_unit_cache = {}
124
129
 
130
+ # Class Methods
131
+
125
132
  # setup internal arrays and hashes
126
133
  # @return [true]
127
134
  def self.setup
128
- self.clear_cache
129
- @@PREFIX_VALUES = {}
130
- @@PREFIX_MAP = {}
131
- @@UNIT_VALUES = {}
132
- @@UNIT_MAP = {}
133
- @@UNIT_REGEX = nil
134
- @@UNIT_MATCH_REGEX = nil
135
- @@PREFIX_REGEX = nil
136
-
137
- @@definitions.each do |name, definition|
138
- self.use_definition(definition)
135
+ clear_cache
136
+ @@prefix_values = {}
137
+ @@prefix_map = {}
138
+ @@unit_values = {}
139
+ @@unit_map = {}
140
+ @@unit_regex = nil
141
+ @@unit_match_regex = nil
142
+ @@prefix_regex = nil
143
+
144
+ @@definitions.each do |_name, definition|
145
+ use_definition(definition)
139
146
  end
140
147
 
141
148
  RubyUnits::Unit.new(1)
142
- return true
149
+ true
143
150
  end
144
151
 
145
-
146
152
  # determine if a unit is already defined
147
153
  # @param [String] unit
148
154
  # @return [Boolean]
149
155
  def self.defined?(unit)
150
- self.definitions.values.any? { |d| d.aliases.include?(unit) }
156
+ definitions.values.any? { |d| d.aliases.include?(unit) }
151
157
  end
152
158
 
153
159
  # return the unit definition for a unit
154
160
  # @param [String] unit
155
161
  # @return [RubyUnits::Unit::Definition, nil]
156
- def self.definition(_unit)
157
- unit = (_unit =~ /^<.+>$/) ? _unit : "<#{_unit}>"
158
- return @@definitions[unit]
162
+ def self.definition(unit_name)
163
+ unit = unit_name =~ /^<.+>$/ ? unit_name : "<#{unit_name}>"
164
+ @@definitions[unit]
159
165
  end
160
166
 
161
167
  # return a list of all defined units
162
168
  # @return [Array]
163
169
  def self.definitions
164
- return @@definitions
170
+ @@definitions
165
171
  end
166
172
 
167
173
  # @param [RubyUnits::Unit::Definition|String] unit_definition
@@ -180,12 +186,12 @@ module RubyUnits
180
186
  # RubyUnits::Unit.define(unit_definition)
181
187
  def self.define(unit_definition, &block)
182
188
  if block_given?
183
- raise ArgumentError, "When using the block form of RubyUnits::Unit.define, pass the name of the unit" unless unit_definition.instance_of?(String)
189
+ raise ArgumentError, 'When using the block form of RubyUnits::Unit.define, pass the name of the unit' unless unit_definition.instance_of?(String)
184
190
  unit_definition = RubyUnits::Unit::Definition.new(unit_definition, &block)
185
191
  end
186
192
  RubyUnits::Unit.definitions[unit_definition.name] = unit_definition
187
193
  RubyUnits::Unit.use_definition(unit_definition)
188
- return unit_definition
194
+ unit_definition
189
195
  end
190
196
 
191
197
  # @param [String] name Name of unit to redefine
@@ -195,8 +201,9 @@ module RubyUnits
195
201
  # @return (see RubyUnits::Unit.define)
196
202
  # Get the definition for a unit and allow it to be redefined
197
203
  def self.redefine!(name)
198
- fail ArgumentError, 'A block is required to redefine a unit' unless block_given?
204
+ raise ArgumentError, 'A block is required to redefine a unit' unless block_given?
199
205
  unit_definition = definition(name)
206
+ raise(ArgumentError, "'#{name}' Unit not recognized") unless unit_definition
200
207
  yield unit_definition
201
208
  @@definitions.delete("<#{name}>")
202
209
  define(unit_definition)
@@ -211,6 +218,185 @@ module RubyUnits
211
218
  RubyUnits::Unit.setup
212
219
  end
213
220
 
221
+ # @return [Hash]
222
+ def self.cached
223
+ @@cached_units
224
+ end
225
+
226
+ # @return [true]
227
+ def self.clear_cache
228
+ @@cached_units = {}
229
+ @@base_unit_cache = {}
230
+ RubyUnits::Unit.new(1)
231
+ true
232
+ end
233
+
234
+ # @return [Hash]
235
+ def self.base_unit_cache
236
+ @@base_unit_cache
237
+ end
238
+
239
+ # @example parse strings
240
+ # "1 minute in seconds"
241
+ # @param [String] input
242
+ # @return [Unit]
243
+ def self.parse(input)
244
+ first, second = input.scan(/(.+)\s(?:in|to|as)\s(.+)/i).first
245
+ second.nil? ? RubyUnits::Unit.new(first) : RubyUnits::Unit.new(first).convert_to(second)
246
+ end
247
+
248
+ # @param [Numeric] q quantity
249
+ # @param [Array] n numerator
250
+ # @param [Array] d denominator
251
+ # @return [Hash]
252
+ def self.eliminate_terms(q, n, d)
253
+ num = n.dup
254
+ den = d.dup
255
+
256
+ num.delete_if { |v| v == UNITY }
257
+ den.delete_if { |v| v == UNITY }
258
+ combined = Hash.new(0)
259
+
260
+ i = 0
261
+ loop do
262
+ break if i > num.size
263
+ if @@prefix_values.key? num[i]
264
+ k = [num[i], num[i + 1]]
265
+ i += 2
266
+ else
267
+ k = num[i]
268
+ i += 1
269
+ end
270
+ combined[k] += 1 unless k.nil? || k == UNITY
271
+ end
272
+
273
+ j = 0
274
+ loop do
275
+ break if j > den.size
276
+ if @@prefix_values.key? den[j]
277
+ k = [den[j], den[j + 1]]
278
+ j += 2
279
+ else
280
+ k = den[j]
281
+ j += 1
282
+ end
283
+ combined[k] -= 1 unless k.nil? || k == UNITY
284
+ end
285
+
286
+ num = []
287
+ den = []
288
+ combined.each do |key, value|
289
+ if value >= 0
290
+ value.times { num << key }
291
+ elsif value < 0
292
+ value.abs.times { den << key }
293
+ end
294
+ end
295
+ num = UNITY_ARRAY if num.empty?
296
+ den = UNITY_ARRAY if den.empty?
297
+ { scalar: q, numerator: num.flatten.compact, denominator: den.flatten.compact }
298
+ end
299
+
300
+ # return an array of base units
301
+ # @return [Array]
302
+ def self.base_units
303
+ @@base_units ||= @@definitions.dup.delete_if { |_, defn| !defn.base? }.keys.map { |u| RubyUnits::Unit.new(u) }
304
+ end
305
+
306
+ # parse a string consisting of a number and a unit string
307
+ # NOTE: This does not properly handle units formatted like '12mg/6ml'
308
+ # @param [String] string
309
+ # @return [Array] consisting of [Numeric, "unit"]
310
+ def self.parse_into_numbers_and_units(string)
311
+ # scientific notation.... 123.234E22, -123.456e-10
312
+ sci = /[+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*/
313
+ # rational numbers.... -1/3, 1/5, 20/100, -6 1/2, -6-1/2
314
+ rational = %r{\(?[+-]?(?:\d+[ -])?\d+\/\d+\)?}
315
+ # complex numbers... -1.2+3i, +1.2-3.3i
316
+ complex = /#{sci}{2,2}i/
317
+ anynumber = /(?:(#{complex}|#{rational}|#{sci}))?\s?([^-\d\.].*)?/
318
+
319
+ num, unit = string.scan(anynumber).first
320
+
321
+ [
322
+ case num
323
+ when NilClass
324
+ 1
325
+ when complex
326
+ if num.respond_to?(:to_c)
327
+ num.to_c
328
+ else
329
+ #:nocov_19:
330
+ Complex(*num.scan(/(#{sci})(#{sci})i/).flatten.map(&:to_i))
331
+ #:nocov_19:
332
+ end
333
+ when rational
334
+ # if it has whitespace, it will be of the form '6 1/2'
335
+ if num =~ RATIONAL_NUMBER
336
+ sign = $1 == '-' ? -1 : 1
337
+ n = $2.to_i
338
+ f = Rational($3.to_i, $4.to_i)
339
+ sign * (n + f)
340
+ else
341
+ Rational(*num.split('/').map(&:to_i))
342
+ end
343
+ else
344
+ num.to_f
345
+ end,
346
+ unit.to_s.strip
347
+ ]
348
+ end
349
+
350
+ # return a fragment of a regex to be used for matching units or reconstruct it if hasn't been used yet.
351
+ # Unit names are reverse sorted by length so the regexp matcher will prefer longer and more specific names
352
+ # @return [String]
353
+ def self.unit_regex
354
+ @@unit_regex ||= @@unit_map.keys.sort_by { |unit_name| [unit_name.length, unit_name] }.reverse.join('|')
355
+ end
356
+
357
+ # return a regex used to match units
358
+ # @return [RegExp]
359
+ def self.unit_match_regex
360
+ @@unit_match_regex ||= /(#{RubyUnits::Unit.prefix_regex})??(#{RubyUnits::Unit.unit_regex})\b/
361
+ end
362
+
363
+ # return a regexp fragment used to match prefixes
364
+ # @return [String]
365
+ # @private
366
+ def self.prefix_regex
367
+ @@prefix_regex ||= @@prefix_map.keys.sort_by { |prefix| [prefix.length, prefix] }.reverse.join('|')
368
+ end
369
+
370
+ def self.temp_regex
371
+ @@temp_regex ||= begin
372
+ temp_units = %w(tempK tempC tempF tempR degK degC degF degR)
373
+ aliases = temp_units.map do |unit|
374
+ d = RubyUnits::Unit.definition(unit)
375
+ d && d.aliases
376
+ end.flatten.compact
377
+ regex_str = aliases.empty? ? '(?!x)x' : aliases.join('|')
378
+ Regexp.new "(?:#{regex_str})"
379
+ end
380
+ end
381
+
382
+ # inject a definition into the internal array and set it up for use
383
+ def self.use_definition(definition)
384
+ @@unit_match_regex = nil # invalidate the unit match regex
385
+ @@temp_regex = nil # invalidate the temp regex
386
+ if definition.prefix?
387
+ @@prefix_values[definition.name] = definition.scalar
388
+ definition.aliases.each { |alias_name| @@prefix_map[alias_name] = definition.name }
389
+ @@prefix_regex = nil # invalidate the prefix regex
390
+ else
391
+ @@unit_values[definition.name] = {}
392
+ @@unit_values[definition.name][:scalar] = definition.scalar
393
+ @@unit_values[definition.name][:numerator] = definition.numerator if definition.numerator
394
+ @@unit_values[definition.name][:denominator] = definition.denominator if definition.denominator
395
+ definition.aliases.each { |alias_name| @@unit_map[alias_name] = definition.name }
396
+ @@unit_regex = nil # invalidate the unit regex
397
+ end
398
+ end
399
+
214
400
  include Comparable
215
401
 
216
402
  # @return [Numeric]
@@ -240,13 +426,6 @@ module RubyUnits
240
426
  # @return [String]
241
427
  attr_accessor :unit_name
242
428
 
243
- # needed to make complex units play nice -- otherwise not detected as a complex_generic
244
- # @param [Class]
245
- # @return [Boolean]
246
- def kind_of?(klass)
247
- self.scalar.kind_of?(klass)
248
- end
249
-
250
429
  # Used to copy one unit to another
251
430
  # @param [Unit] from Unit to copy definition from
252
431
  # @return [Unit]
@@ -254,11 +433,11 @@ module RubyUnits
254
433
  @scalar = from.scalar
255
434
  @numerator = from.numerator
256
435
  @denominator = from.denominator
257
- @is_base = from.is_base?
436
+ @base = from.base?
258
437
  @signature = from.signature
259
438
  @base_scalar = from.base_scalar
260
439
  @unit_name = from.unit_name rescue nil
261
- return self
440
+ self
262
441
  end
263
442
 
264
443
  # Create a new Unit object. Can be initialized using a String, a Hash, an Array, Time, DateTime
@@ -274,7 +453,7 @@ module RubyUnits
274
453
  # "6'4\""" -- recognized as 6 feet + 4 inches
275
454
  # "8 lbs 8 oz" -- recognized as 8 lbs + 8 ounces
276
455
  # [1, 'kg']
277
- # {:scalar => 1, :numerator=>'kg'}
456
+ # {scalar: 1, numerator: 'kg'}
278
457
  #
279
458
  # @param [Unit,String,Hash,Array,Date,Time,DateTime] options
280
459
  # @return [Unit]
@@ -287,7 +466,7 @@ module RubyUnits
287
466
  @unit_name = nil
288
467
  @signature = nil
289
468
  @output = {}
290
- raise ArgumentError, "Invalid Unit Format" if options[0].nil?
469
+ raise ArgumentError, 'Invalid Unit Format' if options[0].nil?
291
470
  if options.size == 2
292
471
  # options[0] is the scalar
293
472
  # options[1] is a unit string
@@ -300,8 +479,8 @@ module RubyUnits
300
479
  return
301
480
  end
302
481
  if options.size == 3
303
- options[1] = options[1].join if options[1].kind_of?(Array)
304
- options[2] = options[2].join if options[2].kind_of?(Array)
482
+ options[1] = options[1].join if options[1].is_a?(Array)
483
+ options[2] = options[2].join if options[2].is_a?(Array)
305
484
  begin
306
485
  cached = @@cached_units["#{options[1]}/#{options[2]}"] * options[0]
307
486
  copy(cached)
@@ -312,88 +491,58 @@ module RubyUnits
312
491
  end
313
492
 
314
493
  case options[0]
315
- when Unit
316
- copy(options[0])
317
- return
318
- when Hash
319
- @scalar = options[0][:scalar] || 1
320
- @numerator = options[0][:numerator] || UNITY_ARRAY
321
- @denominator = options[0][:denominator] || UNITY_ARRAY
322
- @signature = options[0][:signature]
323
- when Array
324
- initialize(*options[0])
325
- return
326
- when Numeric
327
- @scalar = options[0]
328
- @numerator = @denominator = UNITY_ARRAY
329
- when Time
330
- @scalar = options[0].to_f
331
- @numerator = ['<second>']
332
- @denominator = UNITY_ARRAY
333
- when DateTime, Date
334
- @scalar = options[0].ajd
335
- @numerator = ['<day>']
336
- @denominator = UNITY_ARRAY
337
- when /^\s*$/
338
- raise ArgumentError, "No Unit Specified"
339
- when String
340
- parse(options[0])
341
- else
342
- raise ArgumentError, "Invalid Unit Format"
494
+ when Unit
495
+ copy(options[0])
496
+ return
497
+ when Hash
498
+ @scalar = options[0][:scalar] || 1
499
+ @numerator = options[0][:numerator] || UNITY_ARRAY
500
+ @denominator = options[0][:denominator] || UNITY_ARRAY
501
+ @signature = options[0][:signature]
502
+ when Array
503
+ initialize(*options[0])
504
+ return
505
+ when Numeric
506
+ @scalar = options[0]
507
+ @numerator = @denominator = UNITY_ARRAY
508
+ when Time
509
+ @scalar = options[0].to_f
510
+ @numerator = ['<second>']
511
+ @denominator = UNITY_ARRAY
512
+ when DateTime, Date
513
+ @scalar = options[0].ajd
514
+ @numerator = ['<day>']
515
+ @denominator = UNITY_ARRAY
516
+ when /^\s*$/
517
+ raise ArgumentError, 'No Unit Specified'
518
+ when String
519
+ parse(options[0])
520
+ else
521
+ raise ArgumentError, 'Invalid Unit Format'
343
522
  end
344
- self.update_base_scalar
345
- raise ArgumentError, "Temperatures must not be less than absolute zero" if self.is_temperature? && self.base_scalar < 0
346
- unary_unit = self.units || ""
523
+ update_base_scalar
524
+ raise ArgumentError, 'Temperatures must not be less than absolute zero' if temperature? && base_scalar < 0
525
+ unary_unit = units || ''
347
526
  if options.first.instance_of?(String)
348
- opt_scalar, opt_units = RubyUnits::Unit.parse_into_numbers_and_units(options[0])
349
- unless @@cached_units.keys.include?(opt_units) ||
350
- (opt_units =~ %r{\D/[\d+\.]+}) ||
351
- (opt_units =~ /(#{RubyUnits::Unit.temp_regex})|(#{LBS_OZ_UNIT_REGEX})|(#{FEET_INCH_UNITS_REGEX})|%|(#{TIME_REGEX})|i\s?(.+)?|&plusmn;|\+\/-/)
352
- @@cached_units[opt_units] = (self.scalar == 1 ? self : opt_units.to_unit) if opt_units && !opt_units.empty?
527
+ _opt_scalar, opt_units = RubyUnits::Unit.parse_into_numbers_and_units(options[0])
528
+ unless @@cached_units.keys.include?(opt_units) ||
529
+ (opt_units =~ %r{\D/[\d+\.]+}) ||
530
+ (opt_units =~ %r{(#{RubyUnits::Unit.temp_regex})|(#{STONE_LB_UNIT_REGEX})|(#{LBS_OZ_UNIT_REGEX})|(#{FEET_INCH_UNITS_REGEX})|%|(#{TIME_REGEX})|i\s?(.+)?|&plusmn;|\+\/-})
531
+ @@cached_units[opt_units] = (scalar == 1 ? self : opt_units.to_unit) if opt_units && !opt_units.empty?
353
532
  end
354
533
  end
355
- unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /#{RubyUnits::Unit.temp_regex}/) then
356
- @@cached_units[unary_unit] = (self.scalar == 1 ? self : unary_unit.to_unit)
534
+ unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /#{RubyUnits::Unit.temp_regex}/)
535
+ @@cached_units[unary_unit] = (scalar == 1 ? self : unary_unit.to_unit)
357
536
  end
358
- [@scalar, @numerator, @denominator, @base_scalar, @signature, @is_base].each { |x| x.freeze }
359
- return self
537
+ [@scalar, @numerator, @denominator, @base_scalar, @signature, @base].each(&:freeze)
538
+ self
360
539
  end
361
540
 
362
541
  # @todo: figure out how to handle :counting units. This method should probably return :counting instead of :unitless for 'each'
363
542
  # return the kind of the unit (:mass, :length, etc...)
364
543
  # @return [Symbol]
365
544
  def kind
366
- return @@KINDS[self.signature]
367
- end
368
-
369
- # @private
370
- # @return [Hash]
371
- def self.cached
372
- return @@cached_units
373
- end
374
-
375
- # @private
376
- # @return [true]
377
- def self.clear_cache
378
- @@cached_units = {}
379
- @@base_unit_cache = {}
380
- RubyUnits::Unit.new(1)
381
- return true
382
- end
383
-
384
- # @private
385
- # @return [Hash]
386
- def self.base_unit_cache
387
- return @@base_unit_cache
388
- end
389
-
390
- # @example parse strings
391
- # "1 minute in seconds"
392
- # @param [String] input
393
- # @return [Unit]
394
- def self.parse(input)
395
- first, second = input.scan(/(.+)\s(?:in|to|as)\s(.+)/i).first
396
- second.nil? ? RubyUnits::Unit.new(first) : RubyUnits::Unit.new(first).convert_to(second)
545
+ @@kinds[signature]
397
546
  end
398
547
 
399
548
  # @return [Unit]
@@ -401,59 +550,60 @@ module RubyUnits
401
550
  self
402
551
  end
403
552
 
404
- alias :unit :to_unit
553
+ alias unit to_unit
405
554
 
406
555
  # Is this unit in base form?
407
556
  # @return [Boolean]
408
- def is_base?
409
- return @is_base if defined? @is_base
410
- @is_base = (@numerator + @denominator).compact.uniq.
411
- map { |unit| RubyUnits::Unit.definition(unit) }.
412
- all? { |element| element.unity? || element.base? }
413
- return @is_base
557
+ def base?
558
+ return @base if defined? @base
559
+ @base = (@numerator + @denominator)
560
+ .compact
561
+ .uniq
562
+ .map { |unit| RubyUnits::Unit.definition(unit) }
563
+ .all? { |element| element.unity? || element.base? }
564
+ @base
414
565
  end
415
566
 
416
- alias :base? :is_base?
567
+ alias is_base? base?
417
568
 
418
569
  # convert to base SI units
419
570
  # results of the conversion are cached so subsequent calls to this will be fast
420
571
  # @return [Unit]
421
572
  # @todo this is brittle as it depends on the display_name of a unit, which can be changed
422
573
  def to_base
423
- return self if self.is_base?
424
- if @@UNIT_MAP[self.units] =~ /\A<(?:temp|deg)[CRF]>\Z/
425
- @signature = @@KINDS.key(:temperature)
426
- base = case
427
- when self.is_temperature?
428
- self.convert_to('tempK')
429
- when self.is_degree?
430
- self.convert_to('degK')
574
+ return self if base?
575
+ if @@unit_map[units] =~ /\A<(?:temp|deg)[CRF]>\Z/
576
+ @signature = @@kinds.key(:temperature)
577
+ base = if temperature?
578
+ convert_to('tempK')
579
+ elsif degree?
580
+ convert_to('degK')
431
581
  end
432
582
  return base
433
583
  end
434
584
 
435
- cached = ((@@base_unit_cache[self.units] * self.scalar) rescue nil)
585
+ cached = ((@@base_unit_cache[units] * scalar) rescue nil)
436
586
  return cached if cached
437
587
 
438
588
  num = []
439
589
  den = []
440
590
  q = 1
441
- for unit in @numerator.compact do
442
- if @@PREFIX_VALUES[unit]
443
- q *= @@PREFIX_VALUES[unit]
591
+ @numerator.compact.each do |unit|
592
+ if @@prefix_values[unit]
593
+ q *= @@prefix_values[unit]
444
594
  else
445
- q *= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
446
- num << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
447
- den << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
595
+ q *= @@unit_values[unit][:scalar] if @@unit_values[unit]
596
+ num << @@unit_values[unit][:numerator] if @@unit_values[unit] && @@unit_values[unit][:numerator]
597
+ den << @@unit_values[unit][:denominator] if @@unit_values[unit] && @@unit_values[unit][:denominator]
448
598
  end
449
599
  end
450
- for unit in @denominator.compact do
451
- if @@PREFIX_VALUES[unit]
452
- q /= @@PREFIX_VALUES[unit]
600
+ @denominator.compact.each do |unit|
601
+ if @@prefix_values[unit]
602
+ q /= @@prefix_values[unit]
453
603
  else
454
- q /= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
455
- den << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
456
- num << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
604
+ q /= @@unit_values[unit][:scalar] if @@unit_values[unit]
605
+ den << @@unit_values[unit][:numerator] if @@unit_values[unit] && @@unit_values[unit][:numerator]
606
+ num << @@unit_values[unit][:denominator] if @@unit_values[unit] && @@unit_values[unit][:denominator]
457
607
  end
458
608
  end
459
609
 
@@ -461,11 +611,11 @@ module RubyUnits
461
611
  den = den.flatten.compact
462
612
  num = UNITY_ARRAY if num.empty?
463
613
  base = RubyUnits::Unit.new(RubyUnits::Unit.eliminate_terms(q, num, den))
464
- @@base_unit_cache[self.units]=base
465
- return base * @scalar
614
+ @@base_unit_cache[units] = base
615
+ base * @scalar
466
616
  end
467
617
 
468
- alias :base :to_base
618
+ alias base to_base
469
619
 
470
620
  # Generate human readable output.
471
621
  # If the name of a unit is passed, the unit will first be converted to the target unit before output.
@@ -482,48 +632,49 @@ module RubyUnits
482
632
  #
483
633
  # @param [Symbol] target_units
484
634
  # @return [String]
485
- def to_s(target_units=nil)
635
+ def to_s(target_units = nil)
486
636
  out = @output[target_units]
487
- if out
488
- return out
489
- else
490
- case target_units
491
- when :ft
492
- inches = self.convert_to("in").scalar.to_int
493
- out = "#{(inches / 12).truncate}\'#{(inches % 12).round}\""
494
- when :lbs
495
- ounces = self.convert_to("oz").scalar.to_int
496
- out = "#{(ounces / 16).truncate} lbs, #{(ounces % 16).round} oz"
497
- when String
498
- out = case target_units.strip
499
- when /\A\s*\Z/ # whitespace only
500
- ''
501
- when /(%[\-+\.\w#]+)\s*(.+)*/ #format string like '%0.2f in'
502
- begin
503
- if $2 #unit specified, need to convert
504
- self.convert_to($2).to_s($1)
505
- else
506
- "#{$1 % @scalar} #{$2 || self.units}".strip
507
- end
508
- rescue # parse it like a strftime format string
509
- (DateTime.new(0) + self).strftime(target_units)
637
+ return out if out
638
+ separator = RubyUnits.configuration.separator
639
+ case target_units
640
+ when :ft
641
+ inches = convert_to('in').scalar.to_int
642
+ out = "#{(inches / 12).truncate}\'#{(inches % 12).round}\""
643
+ when :lbs
644
+ ounces = convert_to('oz').scalar.to_int
645
+ out = "#{(ounces / 16).truncate}#{separator}lbs, #{(ounces % 16).round}#{separator}oz"
646
+ when :stone
647
+ pounds = convert_to('lbs').scalar.to_int
648
+ out = "#{(pounds / 14).truncate}#{separator}stone, #{(pounds % 14).round}#{separator}lb"
649
+ when String
650
+ out = case target_units.strip
651
+ when /\A\s*\Z/ # whitespace only
652
+ ''
653
+ when /(%[\-+\.\w#]+)\s*(.+)*/ # format string like '%0.2f in'
654
+ begin
655
+ if $2 # unit specified, need to convert
656
+ convert_to($2).to_s($1)
657
+ else
658
+ "#{$1 % @scalar}#{separator}#{$2 || units}".strip
510
659
  end
511
- when /(\S+)/ #unit only 'mm' or '1/mm'
512
- self.convert_to($1).to_s
513
- else
514
- raise "unhandled case"
660
+ rescue # parse it like a strftime format string
661
+ (DateTime.new(0) + self).strftime(target_units)
515
662
  end
516
- else
517
- out = case @scalar
518
- when Rational, Complex
519
- "#{@scalar} #{self.units}"
520
- else
521
- "#{'%g' % @scalar} #{self.units}"
522
- end.strip
523
- end
524
- @output[target_units] = out
525
- return out
663
+ when /(\S+)/ # unit only 'mm' or '1/mm'
664
+ convert_to($1).to_s
665
+ else
666
+ raise 'unhandled case'
667
+ end
668
+ else
669
+ out = case @scalar
670
+ when Rational, Complex
671
+ "#{@scalar}#{separator}#{units}"
672
+ else
673
+ "#{'%g' % @scalar}#{separator}#{units}"
674
+ end.strip
526
675
  end
676
+ @output[target_units] = out
677
+ out
527
678
  end
528
679
 
529
680
  # Normally pretty prints the unit, but if you really want to see the guts of it, pass ':dump'
@@ -537,33 +688,33 @@ module RubyUnits
537
688
  # true if unit is a 'temperature', false if a 'degree' or anything else
538
689
  # @return [Boolean]
539
690
  # @todo use unit definition to determine if it's a temperature instead of a regex
540
- def is_temperature?
541
- return self.is_degree? && (!(@@UNIT_MAP[self.units] =~ /temp[CFRK]/).nil?)
691
+ def temperature?
692
+ degree? && !(@@unit_map[units] =~ /temp[CFRK]/).nil?
542
693
  end
543
694
 
544
- alias :temperature? :is_temperature?
695
+ alias is_temperature? temperature?
545
696
 
546
697
  # true if a degree unit or equivalent.
547
698
  # @return [Boolean]
548
- def is_degree?
549
- return self.kind == :temperature
699
+ def degree?
700
+ kind == :temperature
550
701
  end
551
702
 
552
- alias :degree? :is_degree?
703
+ alias is_degree? degree?
553
704
 
554
705
  # returns the 'degree' unit associated with a temperature unit
555
706
  # @example '100 tempC'.to_unit.temperature_scale #=> 'degC'
556
707
  # @return [String] possible values: degC, degF, degR, or degK
557
708
  def temperature_scale
558
- return nil unless self.is_temperature?
559
- return "deg#{@@UNIT_MAP[self.units][/temp([CFRK])/, 1]}"
709
+ return nil unless temperature?
710
+ "deg#{@@unit_map[units][/temp([CFRK])/, 1]}"
560
711
  end
561
712
 
562
713
  # returns true if no associated units
563
714
  # false, even if the units are "unitless" like 'radians, each, etc'
564
715
  # @return [Boolean]
565
716
  def unitless?
566
- return(@numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY)
717
+ (@numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY)
567
718
  end
568
719
 
569
720
  # Compare two Unit objects. Throws an exception if they are not of compatible types.
@@ -573,19 +724,17 @@ module RubyUnits
573
724
  # @raise [NoMethodError] when other does not define <=>
574
725
  # @raise [ArgumentError] when units are not compatible
575
726
  def <=>(other)
576
- case
577
- when !self.base_scalar.respond_to?(:<=>)
578
- raise NoMethodError, "undefined method `<=>' for #{self.base_scalar.inspect}"
579
- when other.nil?
580
- return self.base_scalar <=> nil
581
- when !self.is_temperature? && other.respond_to?(:zero?) && other.zero?
582
- return self.base_scalar <=> 0
583
- when other.instance_of?(Unit)
584
- raise ArgumentError, "Incompatible Units ('#{self.units}' not compatible with '#{other.units}')" unless self =~ other
585
- return self.base_scalar <=> other.base_scalar
586
- else
587
- x, y = coerce(other)
588
- return y <=> x
727
+ raise NoMethodError, "undefined method `<=>' for #{base_scalar.inspect}" unless base_scalar.respond_to?(:<=>)
728
+ if other.nil?
729
+ base_scalar <=> nil
730
+ elsif !temperature? && other.respond_to?(:zero?) && other.zero?
731
+ base_scalar <=> 0
732
+ elsif other.instance_of?(Unit)
733
+ raise ArgumentError, "Incompatible Units ('#{units}' not compatible with '#{other.units}')" unless self =~ other
734
+ base_scalar <=> other.base_scalar
735
+ else
736
+ x, y = coerce(other)
737
+ y <=> x
589
738
  end
590
739
  end
591
740
 
@@ -598,19 +747,18 @@ module RubyUnits
598
747
  # @param [Object] other
599
748
  # @return [Boolean]
600
749
  def ==(other)
601
- case
602
- when other.respond_to?(:zero?) && other.zero?
603
- return self.zero?
604
- when other.instance_of?(Unit)
605
- return false unless self =~ other
606
- return self.base_scalar == other.base_scalar
607
- else
608
- begin
609
- x, y = coerce(other)
610
- return x == y
611
- rescue ArgumentError # return false when object cannot be coerced
612
- return false
613
- end
750
+ if other.respond_to?(:zero?) && other.zero?
751
+ zero?
752
+ elsif other.instance_of?(Unit)
753
+ return false unless self =~ other
754
+ base_scalar == other.base_scalar
755
+ else
756
+ begin
757
+ x, y = coerce(other)
758
+ x == y
759
+ rescue ArgumentError # return false when object cannot be coerced
760
+ false
761
+ end
614
762
  end
615
763
  end
616
764
 
@@ -625,20 +773,20 @@ module RubyUnits
625
773
  # @return [Boolean]
626
774
  def =~(other)
627
775
  case other
628
- when Unit
629
- self.signature == other.signature
630
- else
631
- begin
632
- x, y = coerce(other)
633
- return x =~ y
634
- rescue ArgumentError
635
- return false
636
- end
776
+ when Unit
777
+ signature == other.signature
778
+ else
779
+ begin
780
+ x, y = coerce(other)
781
+ return x =~ y
782
+ rescue ArgumentError
783
+ return false
784
+ end
637
785
  end
638
786
  end
639
787
 
640
- alias :compatible? :=~
641
- alias :compatible_with? :=~
788
+ alias compatible? =~
789
+ alias compatible_with? =~
642
790
 
643
791
  # Compare two units. Returns true if quantities and units match
644
792
  # @example
@@ -648,20 +796,20 @@ module RubyUnits
648
796
  # @return [Boolean]
649
797
  def ===(other)
650
798
  case other
651
- when Unit
652
- (self.scalar == other.scalar) && (self.units == other.units)
653
- else
654
- begin
655
- x, y = coerce(other)
656
- return x === y
657
- rescue ArgumentError
658
- return false
659
- end
799
+ when Unit
800
+ (scalar == other.scalar) && (units == other.units)
801
+ else
802
+ begin
803
+ x, y = coerce(other)
804
+ return x === y
805
+ rescue ArgumentError
806
+ return false
807
+ end
660
808
  end
661
809
  end
662
810
 
663
- alias :same? :===
664
- alias :same_as? :===
811
+ alias same? ===
812
+ alias same_as? ===
665
813
 
666
814
  # Add two units together. Result is same units as receiver and scalar and base_scalar are updated appropriately
667
815
  # throws an exception if the units are not compatible.
@@ -673,30 +821,29 @@ module RubyUnits
673
821
  # @raise [ArgumentError] when adding a fixed time or date to a time span
674
822
  def +(other)
675
823
  case other
676
- when Unit
677
- case
678
- when self.zero?
679
- other.dup
680
- when self =~ other
681
- raise ArgumentError, "Cannot add two temperatures" if ([self, other].all? { |x| x.is_temperature? })
682
- if [self, other].any? { |x| x.is_temperature? }
683
- if self.is_temperature?
684
- RubyUnits::Unit.new(:scalar => (self.scalar + other.convert_to(self.temperature_scale).scalar), :numerator => @numerator, :denominator => @denominator, :signature => @signature)
685
- else
686
- RubyUnits::Unit.new(:scalar => (other.scalar + self.convert_to(other.temperature_scale).scalar), :numerator => other.numerator, :denominator => other.denominator, :signature => other.signature)
687
- end
688
- else
689
- @q ||= ((@@cached_units[self.units].scalar / @@cached_units[self.units].base_scalar) rescue (self.units.to_unit.to_base.scalar))
690
- RubyUnits::Unit.new(:scalar => (self.base_scalar + other.base_scalar)*@q, :numerator => @numerator, :denominator => @denominator, :signature => @signature)
691
- end
824
+ when Unit
825
+ if zero?
826
+ other.dup
827
+ elsif self =~ other
828
+ raise ArgumentError, 'Cannot add two temperatures' if [self, other].all?(&:temperature?)
829
+ if [self, other].any?(&:temperature?)
830
+ if temperature?
831
+ RubyUnits::Unit.new(scalar: (scalar + other.convert_to(temperature_scale).scalar), numerator: @numerator, denominator: @denominator, signature: @signature)
692
832
  else
693
- raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')"
833
+ RubyUnits::Unit.new(scalar: (other.scalar + convert_to(other.temperature_scale).scalar), numerator: other.numerator, denominator: other.denominator, signature: other.signature)
834
+ end
835
+ else
836
+ @q ||= ((@@cached_units[units].scalar / @@cached_units[units].base_scalar) rescue units.to_unit.to_base.scalar)
837
+ RubyUnits::Unit.new(scalar: (base_scalar + other.base_scalar) * @q, numerator: @numerator, denominator: @denominator, signature: @signature)
694
838
  end
695
- when Date, Time
696
- raise ArgumentError, "Date and Time objects represent fixed points in time and cannot be added to a Unit"
697
839
  else
698
- x, y = coerce(other)
699
- y + x
840
+ raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')"
841
+ end
842
+ when Date, Time
843
+ raise ArgumentError, 'Date and Time objects represent fixed points in time and cannot be added to a Unit'
844
+ else
845
+ x, y = coerce(other)
846
+ y + x
700
847
  end
701
848
  end
702
849
 
@@ -708,34 +855,32 @@ module RubyUnits
708
855
  # @raise [ArgumentError] when subtracting a fixed time from a time span
709
856
  def -(other)
710
857
  case other
711
- when Unit
712
- case
713
- when self.zero?
714
- if other.zero?
715
- other.dup * -1 # preserve Units class
716
- else
717
- -other.dup
718
- end
719
- when self =~ other
720
- case
721
- when [self, other].all? { |x| x.is_temperature? }
722
- RubyUnits::Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator => KELVIN, :denominator => UNITY_ARRAY, :signature => @signature).convert_to(self.temperature_scale)
723
- when self.is_temperature?
724
- RubyUnits::Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator => ['<tempK>'], :denominator => UNITY_ARRAY, :signature => @signature).convert_to(self)
725
- when other.is_temperature?
726
- raise ArgumentError, "Cannot subtract a temperature from a differential degree unit"
727
- else
728
- @q ||= ((@@cached_units[self.units].scalar / @@cached_units[self.units].base_scalar) rescue (self.units.to_unit.scalar/self.units.to_unit.to_base.scalar))
729
- RubyUnits::Unit.new(:scalar => (self.base_scalar - other.base_scalar)*@q, :numerator => @numerator, :denominator => @denominator, :signature => @signature)
730
- end
731
- else
732
- raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')"
858
+ when Unit
859
+ if zero?
860
+ if other.zero?
861
+ other.dup * -1 # preserve Units class
862
+ else
863
+ -other.dup
864
+ end
865
+ elsif self =~ other
866
+ if [self, other].all?(&:temperature?)
867
+ RubyUnits::Unit.new(scalar: (base_scalar - other.base_scalar), numerator: KELVIN, denominator: UNITY_ARRAY, signature: @signature).convert_to(temperature_scale)
868
+ elsif temperature?
869
+ RubyUnits::Unit.new(scalar: (base_scalar - other.base_scalar), numerator: ['<tempK>'], denominator: UNITY_ARRAY, signature: @signature).convert_to(self)
870
+ elsif other.temperature?
871
+ raise ArgumentError, 'Cannot subtract a temperature from a differential degree unit'
872
+ else
873
+ @q ||= ((@@cached_units[units].scalar / @@cached_units[units].base_scalar) rescue (units.to_unit.scalar / units.to_unit.to_base.scalar))
874
+ RubyUnits::Unit.new(scalar: (base_scalar - other.base_scalar) * @q, numerator: @numerator, denominator: @denominator, signature: @signature)
733
875
  end
734
- when Time
735
- raise ArgumentError, "Date and Time objects represent fixed points in time and cannot be subtracted from to a Unit, which can only represent time spans"
736
876
  else
737
- x, y = coerce(other)
738
- return y-x
877
+ raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')"
878
+ end
879
+ when Time
880
+ raise ArgumentError, 'Date and Time objects represent fixed points in time and cannot be subtracted from to a Unit, which can only represent time spans'
881
+ else
882
+ x, y = coerce(other)
883
+ y - x
739
884
  end
740
885
  end
741
886
 
@@ -745,16 +890,16 @@ module RubyUnits
745
890
  # @raise [ArgumentError] when attempting to multiply two temperatures
746
891
  def *(other)
747
892
  case other
748
- when Unit
749
- raise ArgumentError, "Cannot multiply by temperatures" if [other, self].any? { |x| x.is_temperature? }
750
- opts = RubyUnits::Unit.eliminate_terms(@scalar*other.scalar, @numerator + other.numerator, @denominator + other.denominator)
751
- opts.merge!(:signature => @signature + other.signature)
752
- return RubyUnits::Unit.new(opts)
753
- when Numeric
754
- return RubyUnits::Unit.new(:scalar => @scalar*other, :numerator => @numerator, :denominator => @denominator, :signature => @signature)
755
- else
756
- x, y = coerce(other)
757
- return x * y
893
+ when Unit
894
+ raise ArgumentError, 'Cannot multiply by temperatures' if [other, self].any?(&:temperature?)
895
+ opts = RubyUnits::Unit.eliminate_terms(@scalar * other.scalar, @numerator + other.numerator, @denominator + other.denominator)
896
+ opts[:signature] = @signature + other.signature
897
+ RubyUnits::Unit.new(opts)
898
+ when Numeric
899
+ RubyUnits::Unit.new(scalar: @scalar * other, numerator: @numerator, denominator: @denominator, signature: @signature)
900
+ else
901
+ x, y = coerce(other)
902
+ x * y
758
903
  end
759
904
  end
760
905
 
@@ -766,18 +911,18 @@ module RubyUnits
766
911
  # @raise [ArgumentError] if attempting to divide a temperature by another temperature
767
912
  def /(other)
768
913
  case other
769
- when Unit
770
- raise ZeroDivisionError if other.zero?
771
- raise ArgumentError, "Cannot divide with temperatures" if [other, self].any? { |x| x.is_temperature? }
772
- opts = RubyUnits::Unit.eliminate_terms(@scalar/other.scalar, @numerator + other.denominator, @denominator + other.numerator)
773
- opts.merge!(:signature => @signature - other.signature)
774
- return RubyUnits::Unit.new(opts)
775
- when Numeric
776
- raise ZeroDivisionError if other.zero?
777
- return RubyUnits::Unit.new(:scalar => @scalar/other, :numerator => @numerator, :denominator => @denominator, :signature => @signature)
778
- else
779
- x, y = coerce(other)
780
- return y / x
914
+ when Unit
915
+ raise ZeroDivisionError if other.zero?
916
+ raise ArgumentError, 'Cannot divide with temperatures' if [other, self].any?(&:temperature?)
917
+ opts = RubyUnits::Unit.eliminate_terms(@scalar / other.scalar, @numerator + other.denominator, @denominator + other.numerator)
918
+ opts[:signature] = @signature - other.signature
919
+ RubyUnits::Unit.new(opts)
920
+ when Numeric
921
+ raise ZeroDivisionError if other.zero?
922
+ RubyUnits::Unit.new(scalar: @scalar / other, numerator: @numerator, denominator: @denominator, signature: @signature)
923
+ else
924
+ x, y = coerce(other)
925
+ y / x
781
926
  end
782
927
  end
783
928
 
@@ -788,18 +933,15 @@ module RubyUnits
788
933
  # @return [Array]
789
934
  def divmod(other)
790
935
  raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')" unless self =~ other
791
- if self.units == other.units
792
- return self.scalar.divmod(other.scalar)
793
- else
794
- return self.to_base.scalar.divmod(other.to_base.scalar)
795
- end
936
+ return scalar.divmod(other.scalar) if units == other.units
937
+ to_base.scalar.divmod(other.to_base.scalar)
796
938
  end
797
939
 
798
940
  # perform a modulo on a unit, will raise an exception if the units are not compatible
799
941
  # @param [Object] other
800
942
  # @return [Integer]
801
943
  def %(other)
802
- return self.divmod(other).last
944
+ divmod(other).last
803
945
  end
804
946
 
805
947
  # Exponentiate. Only takes integer powers.
@@ -817,26 +959,26 @@ module RubyUnits
817
959
  # @raise [ArgumentError] when attempting to raise to a complex number
818
960
  # @raise [ArgumentError] when an invalid exponent is passed
819
961
  def **(other)
820
- raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
821
- if other.kind_of?(Numeric)
822
- return self.inverse if other == -1
962
+ raise ArgumentError, 'Cannot raise a temperature to a power' if temperature?
963
+ if other.is_a?(Numeric)
964
+ return inverse if other == -1
823
965
  return self if other == 1
824
966
  return 1 if other.zero?
825
967
  end
826
968
  case other
827
- when Rational
828
- return self.power(other.numerator).root(other.denominator)
829
- when Integer
830
- return self.power(other)
831
- when Float
832
- return self**(other.to_i) if other == other.to_i
833
- valid = (1..9).map { |x| 1/x }
834
- raise ArgumentError, "Not a n-th root (1..9), use 1/n" unless valid.include? other.abs
835
- return self.root((1/other).to_int)
836
- when Complex
837
- raise ArgumentError, "exponentiation of complex numbers is not yet supported."
838
- else
839
- raise ArgumentError, "Invalid Exponent"
969
+ when Rational
970
+ return power(other.numerator).root(other.denominator)
971
+ when Integer
972
+ return power(other)
973
+ when Float
974
+ return self**other.to_i if other == other.to_i
975
+ valid = (1..9).map { |x| 1 / x }
976
+ raise ArgumentError, 'Not a n-th root (1..9), use 1/n' unless valid.include? other.abs
977
+ return root((1 / other).to_int)
978
+ when Complex
979
+ raise ArgumentError, 'exponentiation of complex numbers is not yet supported.'
980
+ else
981
+ raise ArgumentError, 'Invalid Exponent'
840
982
  end
841
983
  end
842
984
 
@@ -846,16 +988,13 @@ module RubyUnits
846
988
  # @raise [ArgumentError] when attempting to raise a temperature to a power
847
989
  # @raise [ArgumentError] when n is not an integer
848
990
  def power(n)
849
- raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
850
- raise ArgumentError, "Exponent must an Integer" unless n.kind_of?(Integer)
851
- return self.inverse if n == -1
991
+ raise ArgumentError, 'Cannot raise a temperature to a power' if temperature?
992
+ raise ArgumentError, 'Exponent must an Integer' unless n.is_a?(Integer)
993
+ return inverse if n == -1
852
994
  return 1 if n.zero?
853
995
  return self if n == 1
854
- if n > 0 then
855
- return (1..(n-1).to_i).inject(self) { |product, x| product * self }
856
- else
857
- return (1..-(n-1).to_i).inject(self) { |product, x| product / self }
858
- end
996
+ return (1..(n - 1).to_i).inject(self) { |acc, _elem| acc * self } if n >= 0
997
+ (1..-(n - 1).to_i).inject(self) { |acc, _elem| acc / self }
859
998
  end
860
999
 
861
1000
  # Calculates the n-th root of a unit
@@ -866,37 +1005,36 @@ module RubyUnits
866
1005
  # @raise [ArgumentError] when n is not an integer
867
1006
  # @raise [ArgumentError] when n is 0
868
1007
  def root(n)
869
- raise ArgumentError, "Cannot take the root of a temperature" if self.is_temperature?
870
- raise ArgumentError, "Exponent must an Integer" unless n.kind_of?(Integer)
871
- raise ArgumentError, "0th root undefined" if n.zero?
1008
+ raise ArgumentError, 'Cannot take the root of a temperature' if temperature?
1009
+ raise ArgumentError, 'Exponent must an Integer' unless n.is_a?(Integer)
1010
+ raise ArgumentError, '0th root undefined' if n.zero?
872
1011
  return self if n == 1
873
- return self.root(n.abs).inverse if n < 0
1012
+ return root(n.abs).inverse if n < 0
874
1013
 
875
- vec = self.unit_signature_vector
876
- vec =vec.map { |x| x % n }
877
- raise ArgumentError, "Illegal root" unless vec.max == 0
1014
+ vec = unit_signature_vector
1015
+ vec = vec.map { |x| x % n }
1016
+ raise ArgumentError, 'Illegal root' unless vec.max.zero?
878
1017
  num = @numerator.dup
879
1018
  den = @denominator.dup
880
1019
 
881
- for item in @numerator.uniq do
882
- x = num.find_all { |i| i==item }.size
883
- r = ((x/n)*(n-1)).to_int
884
- r.times { |y| num.delete_at(num.index(item)) }
1020
+ @numerator.uniq.each do |item|
1021
+ x = num.find_all { |i| i == item }.size
1022
+ r = ((x / n) * (n - 1)).to_int
1023
+ r.times { num.delete_at(num.index(item)) }
885
1024
  end
886
1025
 
887
- for item in @denominator.uniq do
888
- x = den.find_all { |i| i==item }.size
889
- r = ((x/n)*(n-1)).to_int
890
- r.times { |y| den.delete_at(den.index(item)) }
1026
+ @denominator.uniq.each do |item|
1027
+ x = den.find_all { |i| i == item }.size
1028
+ r = ((x / n) * (n - 1)).to_int
1029
+ r.times { den.delete_at(den.index(item)) }
891
1030
  end
892
- q = @scalar < 0 ? (-1)**Rational(1, n) * (@scalar.abs)**Rational(1, n) : @scalar**Rational(1, n)
893
- return RubyUnits::Unit.new(:scalar => q, :numerator => num, :denominator => den)
1031
+ RubyUnits::Unit.new(scalar: @scalar**Rational(1, n), numerator: num, denominator: den)
894
1032
  end
895
1033
 
896
1034
  # returns inverse of Unit (1/unit)
897
1035
  # @return [Unit]
898
1036
  def inverse
899
- return RubyUnits::Unit.new("1") / self
1037
+ RubyUnits::Unit.new('1') / self
900
1038
  end
901
1039
 
902
1040
  # convert to a specified unit string or to the same units as another Unit
@@ -922,182 +1060,195 @@ module RubyUnits
922
1060
  return self if other.nil?
923
1061
  return self if TrueClass === other
924
1062
  return self if FalseClass === other
925
- if (Unit === other && other.is_temperature?) || (String === other && other =~ /temp[CFRK]/)
926
- raise ArgumentError, "Receiver is not a temperature unit" unless self.degree?
927
- start_unit = self.units
1063
+ if (Unit === other && other.temperature?) || (String === other && other =~ /temp[CFRK]/)
1064
+ raise ArgumentError, 'Receiver is not a temperature unit' unless degree?
1065
+ start_unit = units
928
1066
  target_unit = other.units rescue other
929
1067
  unless @base_scalar
930
- @base_scalar = case @@UNIT_MAP[start_unit]
931
- when '<tempC>'
932
- @scalar + 273.15
933
- when '<tempK>'
934
- @scalar
935
- when '<tempF>'
936
- (@scalar+459.67)*Rational(5, 9)
937
- when '<tempR>'
938
- @scalar*Rational(5, 9)
1068
+ @base_scalar = case @@unit_map[start_unit]
1069
+ when '<tempC>'
1070
+ @scalar + 273.15
1071
+ when '<tempK>'
1072
+ @scalar
1073
+ when '<tempF>'
1074
+ (@scalar + 459.67) * Rational(5, 9)
1075
+ when '<tempR>'
1076
+ @scalar * Rational(5, 9)
939
1077
  end
940
1078
  end
941
- q= case @@UNIT_MAP[target_unit]
942
- when '<tempC>'
943
- @base_scalar - 273.15
944
- when '<tempK>'
945
- @base_scalar
946
- when '<tempF>'
947
- @base_scalar * Rational(9, 5) - 459.67
948
- when '<tempR>'
949
- @base_scalar * Rational(9, 5)
950
- end
1079
+ q = case @@unit_map[target_unit]
1080
+ when '<tempC>'
1081
+ @base_scalar - 273.15
1082
+ when '<tempK>'
1083
+ @base_scalar
1084
+ when '<tempF>'
1085
+ @base_scalar * Rational(9, 5) - 459.67
1086
+ when '<tempR>'
1087
+ @base_scalar * Rational(9, 5)
1088
+ end
951
1089
  return RubyUnits::Unit.new("#{q} #{target_unit}")
952
1090
  else
953
1091
  case other
954
- when Unit
955
- return self if other.units == self.units
956
- target = other
957
- when String
958
- target = RubyUnits::Unit.new(other)
959
- else
960
- raise ArgumentError, "Unknown target units"
1092
+ when Unit
1093
+ return self if other.units == units
1094
+ target = other
1095
+ when String
1096
+ target = RubyUnits::Unit.new(other)
1097
+ else
1098
+ raise ArgumentError, 'Unknown target units'
961
1099
  end
962
1100
  raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')" unless self =~ target
963
- _numerator1 = @numerator.map { |x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x }.map { |i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
964
- _denominator1 = @denominator.map { |x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x }.map { |i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
965
- _numerator2 = target.numerator.map { |x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x }.map { |x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
966
- _denominator2 = target.denominator.map { |x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x }.map { |x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
967
-
968
- q = @scalar * ((_numerator1 + _denominator2).inject(1) { |product, n| product*n }) /
969
- ((_numerator2 + _denominator1).inject(1) { |product, n| product*n })
970
- return RubyUnits::Unit.new(:scalar => q, :numerator => target.numerator, :denominator => target.denominator, :signature => target.signature)
1101
+ numerator1 = @numerator.map { |x| @@prefix_values[x] ? @@prefix_values[x] : x }.map { |i| i.is_a?(Numeric) ? i : @@unit_values[i][:scalar] }.compact
1102
+ denominator1 = @denominator.map { |x| @@prefix_values[x] ? @@prefix_values[x] : x }.map { |i| i.is_a?(Numeric) ? i : @@unit_values[i][:scalar] }.compact
1103
+ numerator2 = target.numerator.map { |x| @@prefix_values[x] ? @@prefix_values[x] : x }.map { |x| x.is_a?(Numeric) ? x : @@unit_values[x][:scalar] }.compact
1104
+ denominator2 = target.denominator.map { |x| @@prefix_values[x] ? @@prefix_values[x] : x }.map { |x| x.is_a?(Numeric) ? x : @@unit_values[x][:scalar] }.compact
1105
+
1106
+ q = @scalar * ((numerator1 + denominator2).inject(1) { |acc, elem| acc * elem }) /
1107
+ ((numerator2 + denominator1).inject(1) { |acc, elem| acc * elem })
1108
+ return RubyUnits::Unit.new(scalar: q, numerator: target.numerator, denominator: target.denominator, signature: target.signature)
971
1109
  end
972
1110
  end
973
1111
 
974
- alias :>> :convert_to
975
- alias :to :convert_to
1112
+ alias >> convert_to
1113
+ alias to convert_to
976
1114
 
977
1115
  # converts the unit back to a float if it is unitless. Otherwise raises an exception
978
1116
  # @return [Float]
979
1117
  # @raise [RuntimeError] when not unitless
980
1118
  def to_f
981
- return @scalar.to_f if self.unitless?
982
- raise RuntimeError, "Cannot convert '#{self.to_s}' to Float unless unitless. Use Unit#scalar"
1119
+ return @scalar.to_f if unitless?
1120
+ raise "Cannot convert '#{self}' to Float unless unitless. Use Unit#scalar"
983
1121
  end
984
1122
 
985
1123
  # converts the unit back to a complex if it is unitless. Otherwise raises an exception
986
1124
  # @return [Complex]
987
1125
  # @raise [RuntimeError] when not unitless
988
1126
  def to_c
989
- return Complex(@scalar) if self.unitless?
990
- raise RuntimeError, "Cannot convert '#{self.to_s}' to Complex unless unitless. Use Unit#scalar"
1127
+ return Complex(@scalar) if unitless?
1128
+ raise "Cannot convert '#{self}' to Complex unless unitless. Use Unit#scalar"
991
1129
  end
992
1130
 
993
1131
  # if unitless, returns an int, otherwise raises an error
994
1132
  # @return [Integer]
995
1133
  # @raise [RuntimeError] when not unitless
996
1134
  def to_i
997
- return @scalar.to_int if self.unitless?
998
- raise RuntimeError, "Cannot convert '#{self.to_s}' to Integer unless unitless. Use Unit#scalar"
1135
+ return @scalar.to_int if unitless?
1136
+ raise "Cannot convert '#{self}' to Integer unless unitless. Use Unit#scalar"
999
1137
  end
1000
1138
 
1001
- alias :to_int :to_i
1139
+ alias to_int to_i
1002
1140
 
1003
1141
  # if unitless, returns a Rational, otherwise raises an error
1004
1142
  # @return [Rational]
1005
1143
  # @raise [RuntimeError] when not unitless
1006
1144
  def to_r
1007
- return @scalar.to_r if self.unitless?
1008
- raise RuntimeError, "Cannot convert '#{self.to_s}' to Rational unless unitless. Use Unit#scalar"
1145
+ return @scalar.to_r if unitless?
1146
+ raise "Cannot convert '#{self}' to Rational unless unitless. Use Unit#scalar"
1009
1147
  end
1010
1148
 
1011
1149
  # Returns string formatted for json
1012
1150
  # @return [String]
1013
- def as_json(*args)
1151
+ def as_json(*)
1014
1152
  to_s
1015
1153
  end
1016
1154
 
1017
1155
  # returns the 'unit' part of the Unit object without the scalar
1018
1156
  # @return [String]
1019
- def units(with_prefix = true)
1020
- return "" if @numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY
1021
- output_numerator = []
1157
+ def units(with_prefix: true)
1158
+ return '' if @numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY
1159
+ output_numerator = ['1']
1022
1160
  output_denominator = []
1023
1161
  num = @numerator.clone.compact
1024
1162
  den = @denominator.clone.compact
1025
1163
 
1026
- if @numerator == UNITY_ARRAY
1027
- output_numerator << "1"
1028
- else
1029
- while defn = RubyUnits::Unit.definition(num.shift) do
1030
- if defn && defn.prefix?
1031
- if with_prefix
1032
- output_numerator << (defn.display_name + RubyUnits::Unit.definition(num.shift).display_name)
1033
- end
1034
- else
1035
- output_numerator << defn.display_name
1036
- end
1037
- end
1164
+ unless num == UNITY_ARRAY
1165
+ definitions = num.map { |element| RubyUnits::Unit.definition(element) }
1166
+ definitions.reject!(&:prefix?) unless with_prefix
1167
+ # there is a bug in jruby 9.1.6.0's implementation of chunk_while
1168
+ # see https://github.com/jruby/jruby/issues/4410
1169
+ # TODO: fix this after jruby fixes their bug.
1170
+ definitions = if definitions.respond_to?(:chunk_while) && RUBY_ENGINE != 'jruby'
1171
+ definitions.chunk_while { |defn, _| defn.prefix? }.to_a
1172
+ else # chunk_while is new to ruby 2.3+, so fallback to less efficient methods for older ruby
1173
+ result = []
1174
+ enumerator = definitions.to_enum
1175
+ loop do
1176
+ first = enumerator.next
1177
+ result << (first.prefix? ? [first, enumerator.next] : [first])
1178
+ end
1179
+ result
1180
+ end
1181
+ output_numerator = definitions.map { |element| element.map(&:display_name).join }
1038
1182
  end
1039
1183
 
1040
- if @denominator == UNITY_ARRAY
1041
- output_denominator = []
1042
- else
1043
- while defn = RubyUnits::Unit.definition(den.shift) do
1044
- if defn && defn.prefix?
1045
- if with_prefix
1046
- output_denominator << (defn.display_name + RubyUnits::Unit.definition(den.shift).display_name)
1047
- end
1048
- else
1049
- output_denominator << defn.display_name
1050
- end
1051
- end
1184
+ unless den == UNITY_ARRAY
1185
+ definitions = den.map { |element| RubyUnits::Unit.definition(element) }
1186
+ definitions.reject!(&:prefix?) unless with_prefix
1187
+ # there is a bug in jruby 9.1.6.0's implementation of chunk_while
1188
+ # see https://github.com/jruby/jruby/issues/4410
1189
+ # TODO: fix this after jruby fixes their bug.
1190
+ definitions = if definitions.respond_to?(:chunk_while) && RUBY_ENGINE != 'jruby'
1191
+ definitions.chunk_while { |defn, _| defn.prefix? }.to_a
1192
+ else # chunk_while is new to ruby 2.3+, so fallback to less efficient methods for older ruby
1193
+ result = []
1194
+ enumerator = definitions.to_enum
1195
+ loop do
1196
+ first = enumerator.next
1197
+ result << (first.prefix? ? [first, enumerator.next] : [first])
1198
+ end
1199
+ result
1200
+ end
1201
+ output_denominator = definitions.map { |element| element.map(&:display_name).join }
1052
1202
  end
1053
1203
 
1054
- on = output_numerator.uniq.
1055
- map { |x| [x, output_numerator.count(x)] }.
1056
- map { |element, power| ("#{element}".strip + (power > 1 ? "^#{power}" : '')) }
1057
- od = output_denominator.uniq.
1058
- map { |x| [x, output_denominator.count(x)] }.
1059
- map { |element, power| ("#{element}".strip + (power > 1 ? "^#{power}" : '')) }
1060
- out = "#{on.join('*')}#{od.empty? ? '' : '/' + od.join('*')}".strip
1061
- return out
1204
+ on = output_numerator
1205
+ .uniq
1206
+ .map { |x| [x, output_numerator.count(x)] }
1207
+ .map { |element, power| (element.to_s.strip + (power > 1 ? "^#{power}" : '')) }
1208
+ od = output_denominator
1209
+ .uniq
1210
+ .map { |x| [x, output_denominator.count(x)] }
1211
+ .map { |element, power| (element.to_s.strip + (power > 1 ? "^#{power}" : '')) }
1212
+ "#{on.join('*')}#{od.empty? ? '' : '/' + od.join('*')}".strip
1062
1213
  end
1063
1214
 
1064
1215
  # negates the scalar of the Unit
1065
1216
  # @return [Numeric,Unit]
1066
1217
  def -@
1067
- return -@scalar if self.unitless?
1068
- return (self.dup * -1)
1218
+ return -@scalar if unitless?
1219
+ dup * -1
1069
1220
  end
1070
1221
 
1071
1222
  # absolute value of a unit
1072
1223
  # @return [Numeric,Unit]
1073
1224
  def abs
1074
- return @scalar.abs if self.unitless?
1075
- return RubyUnits::Unit.new(@scalar.abs, @numerator, @denominator)
1225
+ return @scalar.abs if unitless?
1226
+ RubyUnits::Unit.new(@scalar.abs, @numerator, @denominator)
1076
1227
  end
1077
1228
 
1078
1229
  # ceil of a unit
1079
1230
  # @return [Numeric,Unit]
1080
1231
  def ceil
1081
- return @scalar.ceil if self.unitless?
1082
- return RubyUnits::Unit.new(@scalar.ceil, @numerator, @denominator)
1232
+ return @scalar.ceil if unitless?
1233
+ RubyUnits::Unit.new(@scalar.ceil, @numerator, @denominator)
1083
1234
  end
1084
1235
 
1085
1236
  # @return [Numeric,Unit]
1086
1237
  def floor
1087
- return @scalar.floor if self.unitless?
1088
- return RubyUnits::Unit.new(@scalar.floor, @numerator, @denominator)
1238
+ return @scalar.floor if unitless?
1239
+ RubyUnits::Unit.new(@scalar.floor, @numerator, @denominator)
1089
1240
  end
1090
1241
 
1091
1242
  # @return [Numeric,Unit]
1092
1243
  def round(ndigits = 0)
1093
- return @scalar.round(ndigits) if self.unitless?
1094
- return RubyUnits::Unit.new(@scalar.round(ndigits), @numerator, @denominator)
1244
+ return @scalar.round(ndigits) if unitless?
1245
+ RubyUnits::Unit.new(@scalar.round(ndigits), @numerator, @denominator)
1095
1246
  end
1096
1247
 
1097
1248
  # @return [Numeric, Unit]
1098
1249
  def truncate
1099
- return @scalar.truncate if self.unitless?
1100
- return RubyUnits::Unit.new(@scalar.truncate, @numerator, @denominator)
1250
+ return @scalar.truncate if unitless?
1251
+ RubyUnits::Unit.new(@scalar.truncate, @numerator, @denominator)
1101
1252
  end
1102
1253
 
1103
1254
  # returns next unit in a range. '1 mm'.to_unit.succ #=> '2 mm'.to_unit
@@ -1105,65 +1256,65 @@ module RubyUnits
1105
1256
  # @return [Unit]
1106
1257
  # @raise [ArgumentError] when scalar is not equal to an integer
1107
1258
  def succ
1108
- raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
1109
- return RubyUnits::Unit.new(@scalar.to_i.succ, @numerator, @denominator)
1259
+ raise ArgumentError, 'Non Integer Scalar' unless @scalar == @scalar.to_i
1260
+ RubyUnits::Unit.new(@scalar.to_i.succ, @numerator, @denominator)
1110
1261
  end
1111
1262
 
1112
- alias :next :succ
1263
+ alias next succ
1113
1264
 
1114
1265
  # returns previous unit in a range. '2 mm'.to_unit.pred #=> '1 mm'.to_unit
1115
1266
  # only works when the scalar is an integer
1116
1267
  # @return [Unit]
1117
1268
  # @raise [ArgumentError] when scalar is not equal to an integer
1118
1269
  def pred
1119
- raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
1120
- return RubyUnits::Unit.new(@scalar.to_i.pred, @numerator, @denominator)
1270
+ raise ArgumentError, 'Non Integer Scalar' unless @scalar == @scalar.to_i
1271
+ RubyUnits::Unit.new(@scalar.to_i.pred, @numerator, @denominator)
1121
1272
  end
1122
1273
 
1123
1274
  # Tries to make a Time object from current unit. Assumes the current unit hold the duration in seconds from the epoch.
1124
1275
  # @return [Time]
1125
1276
  def to_time
1126
- return Time.at(self)
1277
+ Time.at(self)
1127
1278
  end
1128
1279
 
1129
- alias :time :to_time
1280
+ alias time to_time
1130
1281
 
1131
1282
  # convert a duration to a DateTime. This will work so long as the duration is the duration from the zero date
1132
1283
  # defined by DateTime
1133
1284
  # @return [DateTime]
1134
1285
  def to_datetime
1135
- return DateTime.new!(self.convert_to('d').scalar)
1286
+ DateTime.new!(convert_to('d').scalar)
1136
1287
  end
1137
1288
 
1138
1289
  # @return [Date]
1139
1290
  def to_date
1140
- return Date.new0(self.convert_to('d').scalar)
1291
+ Date.new0(convert_to('d').scalar)
1141
1292
  end
1142
1293
 
1143
1294
  # true if scalar is zero
1144
1295
  # @return [Boolean]
1145
1296
  def zero?
1146
- return self.base_scalar.zero?
1297
+ base_scalar.zero?
1147
1298
  end
1148
1299
 
1149
1300
  # @example '5 min'.to_unit.ago
1150
1301
  # @return [Unit]
1151
1302
  def ago
1152
- return self.before
1303
+ before
1153
1304
  end
1154
1305
 
1155
1306
  # @example '5 min'.before(time)
1156
1307
  # @return [Unit]
1157
1308
  def before(time_point = ::Time.now)
1158
1309
  case time_point
1159
- when Time, Date, DateTime
1160
- return (time_point - self rescue time_point.to_datetime - self)
1161
- else
1162
- raise ArgumentError, "Must specify a Time, Date, or DateTime"
1310
+ when Time, Date, DateTime
1311
+ return (time_point - self rescue time_point.to_datetime - self)
1312
+ else
1313
+ raise ArgumentError, 'Must specify a Time, Date, or DateTime'
1163
1314
  end
1164
1315
  end
1165
1316
 
1166
- alias :before_now :before
1317
+ alias before_now before
1167
1318
 
1168
1319
  # @example 'min'.since(time)
1169
1320
  # @param [Time, Date, DateTime] time_point
@@ -1172,11 +1323,11 @@ module RubyUnits
1172
1323
  def since(time_point)
1173
1324
  case time_point
1174
1325
  when Time
1175
- return (Time.now - time_point).to_unit('s').convert_to(self)
1326
+ (Time.now - time_point).to_unit('s').convert_to(self)
1176
1327
  when DateTime, Date
1177
- return (DateTime.now - time_point).to_unit('d').convert_to(self)
1328
+ (DateTime.now - time_point).to_unit('d').convert_to(self)
1178
1329
  else
1179
- fail ArgumentError, 'Must specify a Time, Date, or DateTime'
1330
+ raise ArgumentError, 'Must specify a Time, Date, or DateTime'
1180
1331
  end
1181
1332
  end
1182
1333
 
@@ -1186,11 +1337,11 @@ module RubyUnits
1186
1337
  def until(time_point)
1187
1338
  case time_point
1188
1339
  when Time
1189
- return (time_point - Time.now).to_unit('s').convert_to(self)
1340
+ (time_point - Time.now).to_unit('s').convert_to(self)
1190
1341
  when DateTime, Date
1191
- return (time_point - DateTime.now).to_unit('d').convert_to(self)
1342
+ (time_point - DateTime.now).to_unit('d').convert_to(self)
1192
1343
  else
1193
- fail ArgumentError, 'Must specify a Time, Date, or DateTime'
1344
+ raise ArgumentError, 'Must specify a Time, Date, or DateTime'
1194
1345
  end
1195
1346
  end
1196
1347
 
@@ -1200,41 +1351,52 @@ module RubyUnits
1200
1351
  # @raise [ArgumentError] when passed argument is not a Time, Date, or DateTime
1201
1352
  def from(time_point)
1202
1353
  case time_point
1203
- when Time, DateTime, Date
1204
- return (time_point + self rescue time_point.to_datetime + self)
1205
- else
1206
- raise ArgumentError, "Must specify a Time, Date, or DateTime"
1354
+ when Time, DateTime, Date
1355
+ (time_point + self rescue time_point.to_datetime + self)
1356
+ else
1357
+ raise ArgumentError, 'Must specify a Time, Date, or DateTime'
1207
1358
  end
1208
1359
  end
1209
1360
 
1210
- alias :after :from
1211
- alias :from_now :from
1361
+ alias after from
1362
+ alias from_now from
1212
1363
 
1213
1364
  # automatically coerce objects to units when possible
1214
1365
  # if an object defines a 'to_unit' method, it will be coerced using that method
1215
1366
  # @param [Object, #to_unit]
1216
1367
  # @return [Array]
1217
1368
  def coerce(other)
1218
- if other.respond_to? :to_unit
1219
- return [other.to_unit, self]
1220
- end
1369
+ return [other.to_unit, self] if other.respond_to? :to_unit
1221
1370
  case other
1222
- when Unit
1223
- return [other, self]
1224
- else
1225
- return [RubyUnits::Unit.new(other), self]
1371
+ when Unit
1372
+ [other, self]
1373
+ else
1374
+ [RubyUnits::Unit.new(other), self]
1226
1375
  end
1227
1376
  end
1228
1377
 
1229
1378
  # returns a new unit that has been scaled to be more in line with typical usage.
1230
1379
  def best_prefix
1231
- return self.to_base if self.scalar == 0
1232
- _best_prefix = if (self.kind == :information)
1233
- @@PREFIX_VALUES.key(2**((Math.log(self.base_scalar,2) / 10.0).floor * 10))
1234
- else
1235
- @@PREFIX_VALUES.key(10**((Math.log10(self.base_scalar) / 3.0).floor * 3))
1236
- end
1237
- self.to(RubyUnits::Unit.new(@@PREFIX_MAP.key(_best_prefix)+self.units(false)))
1380
+ return to_base if scalar.zero?
1381
+ best_prefix = if kind == :information
1382
+ @@prefix_values.key(2**((Math.log(base_scalar, 2) / 10.0).floor * 10))
1383
+ else
1384
+ @@prefix_values.key(10**((Math.log10(base_scalar) / 3.0).floor * 3))
1385
+ end
1386
+ to(RubyUnits::Unit.new(@@prefix_map.key(best_prefix) + units(with_prefix: false)))
1387
+ end
1388
+
1389
+ # override hash method so objects with same values are considered equal
1390
+ def hash
1391
+ [
1392
+ @scalar,
1393
+ @numerator,
1394
+ @denominator,
1395
+ @base,
1396
+ @signature,
1397
+ @base_scalar,
1398
+ @unit_name
1399
+ ].hash
1238
1400
  end
1239
1401
 
1240
1402
  # Protected and Private Functions that should only be called from this class
@@ -1243,11 +1405,11 @@ module RubyUnits
1243
1405
  # figure out what the scalar part of the base unit for this unit is
1244
1406
  # @return [nil]
1245
1407
  def update_base_scalar
1246
- if self.is_base?
1408
+ if base?
1247
1409
  @base_scalar = @scalar
1248
1410
  @signature = unit_signature
1249
1411
  else
1250
- base = self.to_base
1412
+ base = to_base
1251
1413
  @base_scalar = base.scalar
1252
1414
  @signature = base.signature
1253
1415
  end
@@ -1257,7 +1419,7 @@ module RubyUnits
1257
1419
  # @return [Array]
1258
1420
  # @raise [ArgumentError] when exponent associated with a unit is > 20 or < -20
1259
1421
  def unit_signature_vector
1260
- return self.to_base.unit_signature_vector unless self.is_base?
1422
+ return to_base.unit_signature_vector unless base?
1261
1423
  vector = Array.new(SIGNATURE_VECTOR.size, 0)
1262
1424
  # it's possible to have a kind that misses the array... kinds like :counting
1263
1425
  # are more like prefixes, so don't use them to calculate the vector
@@ -1269,11 +1431,10 @@ module RubyUnits
1269
1431
  index = SIGNATURE_VECTOR.index(definition.kind)
1270
1432
  vector[index] -= 1 if index
1271
1433
  end
1272
- raise ArgumentError, "Power out of range (-20 < net power of a unit < 20)" if vector.any? { |x| x.abs >=20 }
1273
- return vector
1434
+ raise ArgumentError, 'Power out of range (-20 < net power of a unit < 20)' if vector.any? { |x| x.abs >= 20 }
1435
+ vector
1274
1436
  end
1275
1437
 
1276
-
1277
1438
  private
1278
1439
 
1279
1440
  # used by #dup to duplicate a Unit
@@ -1294,61 +1455,8 @@ module RubyUnits
1294
1455
  return @signature unless @signature.nil?
1295
1456
  vector = unit_signature_vector
1296
1457
  vector.each_with_index { |item, index| vector[index] = item * 20**index }
1297
- @signature=vector.inject(0) { |sum, n| sum+n }
1298
- return @signature
1299
- end
1300
-
1301
- # @param [Numeric] q quantity
1302
- # @param [Array] n numerator
1303
- # @param [Array] d denominator
1304
- # @return [Hash]
1305
- def self.eliminate_terms(q, n, d)
1306
- num = n.dup
1307
- den = d.dup
1308
-
1309
- num.delete_if { |v| v == UNITY }
1310
- den.delete_if { |v| v == UNITY }
1311
- combined = Hash.new(0)
1312
-
1313
- i = 0
1314
- loop do
1315
- break if i > num.size
1316
- if @@PREFIX_VALUES.has_key? num[i]
1317
- k = [num[i], num[i+1]]
1318
- i += 2
1319
- else
1320
- k = num[i]
1321
- i += 1
1322
- end
1323
- combined[k] += 1 unless k.nil? || k == UNITY
1324
- end
1325
-
1326
- j = 0
1327
- loop do
1328
- break if j > den.size
1329
- if @@PREFIX_VALUES.has_key? den[j]
1330
- k = [den[j], den[j+1]]
1331
- j += 2
1332
- else
1333
- k = den[j]
1334
- j += 1
1335
- end
1336
- combined[k] -= 1 unless k.nil? || k == UNITY
1337
- end
1338
-
1339
- num = []
1340
- den = []
1341
- for key, value in combined do
1342
- case
1343
- when value > 0
1344
- value.times { num << key }
1345
- when value < 0
1346
- value.abs.times { den << key }
1347
- end
1348
- end
1349
- num = UNITY_ARRAY if num.empty?
1350
- den = UNITY_ARRAY if den.empty?
1351
- return { :scalar => q, :numerator => num.flatten.compact, :denominator => den.flatten.compact }
1458
+ @signature = vector.inject(0) { |acc, elem| acc + elem }
1459
+ @signature
1352
1460
  end
1353
1461
 
1354
1462
  # parse a string into a unit object.
@@ -1364,17 +1472,12 @@ module RubyUnits
1364
1472
  # 8 lbs 8 oz -- recognized as 8 lbs + 8 ounces
1365
1473
  # @return [nil | Unit]
1366
1474
  # @todo This should either be a separate class or at least a class method
1367
- def parse(passed_unit_string="0")
1475
+ def parse(passed_unit_string = '0')
1368
1476
  unit_string = passed_unit_string.dup
1369
- if unit_string =~ /\$\s*(#{NUMBER_REGEX})/
1370
- unit_string = "#{$1} USD"
1371
- end
1477
+ unit_string = "#{$1} USD" if unit_string =~ /\$\s*(#{NUMBER_REGEX})/
1372
1478
  unit_string.gsub!("\u00b0".force_encoding('utf-8'), 'deg') if unit_string.encoding == Encoding::UTF_8
1373
1479
 
1374
- unit_string.gsub!(/%/, 'percent')
1375
- unit_string.gsub!(/'/, 'feet')
1376
- unit_string.gsub!(/"/, 'inch')
1377
- unit_string.gsub!(/#/, 'pound')
1480
+ unit_string.gsub!(/[%'"#]/, '%' => 'percent', "'" => 'feet', '"' => 'inch', '#' => 'pound')
1378
1481
 
1379
1482
  if defined?(Complex) && unit_string =~ COMPLEX_NUMBER
1380
1483
  real, imaginary, unit_s = unit_string.scan(COMPLEX_REGEX)[0]
@@ -1385,9 +1488,9 @@ module RubyUnits
1385
1488
 
1386
1489
  if defined?(Rational) && unit_string =~ RATIONAL_NUMBER
1387
1490
  sign, proper, numerator, denominator, unit_s = unit_string.scan(RATIONAL_REGEX)[0]
1388
- sign = (sign == '-') ? -1 : 1
1491
+ sign = sign == '-' ? -1 : 1
1389
1492
  rational = sign * (proper.to_i + Rational(numerator.to_i, denominator.to_i))
1390
- result = RubyUnits::Unit.new(unit_s || '1') * rational
1493
+ result = RubyUnits::Unit.new(unit_s || '1') * rational
1391
1494
  copy(result)
1392
1495
  return
1393
1496
  end
@@ -1395,7 +1498,7 @@ module RubyUnits
1395
1498
  unit_string =~ NUMBER_REGEX
1396
1499
  unit = @@cached_units[$2]
1397
1500
  mult = ($1.empty? ? 1.0 : $1.to_f) rescue 1.0
1398
- mult = mult.to_int if (mult.to_int == mult)
1501
+ mult = mult.to_int if mult.to_int == mult
1399
1502
  if unit
1400
1503
  copy(unit)
1401
1504
  @scalar *= mult
@@ -1403,15 +1506,15 @@ module RubyUnits
1403
1506
  return self
1404
1507
  end
1405
1508
 
1406
- while unit_string.gsub! /(<#{@@UNIT_REGEX})><(#{@@UNIT_REGEX}>)/, '\1*\2'
1509
+ while unit_string.gsub!(/(<#{@@unit_regex})><(#{@@unit_regex}>)/, '\1*\2')
1407
1510
  # collapse <x><y><z> into <x*y*z>...
1408
1511
  end
1409
1512
  # ... and then strip the remaining brackets for x*y*z
1410
- unit_string.gsub!(/[<>]/, "")
1513
+ unit_string.gsub!(/[<>]/, '')
1411
1514
 
1412
1515
  if unit_string =~ /:/
1413
1516
  hours, minutes, seconds, microseconds = unit_string.scan(TIME_REGEX)[0]
1414
- raise ArgumentError, "Invalid Duration" if [hours, minutes, seconds, microseconds].all? { |x| x.nil? }
1517
+ raise ArgumentError, 'Invalid Duration' if [hours, minutes, seconds, microseconds].all?(&:nil?)
1415
1518
  result = RubyUnits::Unit.new("#{hours || 0} h") +
1416
1519
  RubyUnits::Unit.new("#{minutes || 0} minutes") +
1417
1520
  RubyUnits::Unit.new("#{seconds || 0} seconds") +
@@ -1423,7 +1526,7 @@ module RubyUnits
1423
1526
  # Special processing for unusual unit strings
1424
1527
  # feet -- 6'5"
1425
1528
  feet, inches = unit_string.scan(FEET_INCH_REGEX)[0]
1426
- if (feet && inches)
1529
+ if feet && inches
1427
1530
  result = RubyUnits::Unit.new("#{feet} ft") + RubyUnits::Unit.new("#{inches} inches")
1428
1531
  copy(result)
1429
1532
  return
@@ -1431,164 +1534,74 @@ module RubyUnits
1431
1534
 
1432
1535
  # weight -- 8 lbs 12 oz
1433
1536
  pounds, oz = unit_string.scan(LBS_OZ_REGEX)[0]
1434
- if (pounds && oz)
1537
+ if pounds && oz
1435
1538
  result = RubyUnits::Unit.new("#{pounds} lbs") + RubyUnits::Unit.new("#{oz} oz")
1436
1539
  copy(result)
1437
1540
  return
1438
1541
  end
1439
1542
 
1543
+ # stone -- 3 stone 5, 2 stone, 14 stone 3 pounds, etc.
1544
+ stone, pounds = unit_string.scan(STONE_LB_REGEX)[0]
1545
+ if stone && pounds
1546
+ result = RubyUnits::Unit.new("#{stone} stone") + RubyUnits::Unit.new("#{pounds} lbs")
1547
+ copy(result)
1548
+ return
1549
+ end
1550
+
1440
1551
  # more than one per. I.e., "1 m/s/s"
1441
1552
  raise(ArgumentError, "'#{passed_unit_string}' Unit not recognized") if unit_string.count('/') > 1
1442
- raise(ArgumentError, "'#{passed_unit_string}' Unit not recognized") if unit_string.scan(/\s[02-9]/).size > 0
1443
- @scalar, top, bottom = unit_string.scan(UNIT_STRING_REGEX)[0] #parse the string into parts
1553
+ raise(ArgumentError, "'#{passed_unit_string}' Unit not recognized") if unit_string =~ /\s[02-9]/
1554
+ @scalar, top, bottom = unit_string.scan(UNIT_STRING_REGEX)[0] # parse the string into parts
1444
1555
  top.scan(TOP_REGEX).each do |item|
1445
1556
  n = item[1].to_i
1446
1557
  x = "#{item[0]} "
1447
- case
1448
- when n>=0
1449
- top.gsub!(/#{item[0]}(\^|\*\*)#{n}/) { |s| x * n }
1450
- when n<0
1451
- bottom = "#{bottom} #{x * -n}"; top.gsub!(/#{item[0]}(\^|\*\*)#{n}/, "")
1558
+ if n >= 0
1559
+ top.gsub!(/#{item[0]}(\^|\*\*)#{n}/) { x * n }
1560
+ elsif n < 0
1561
+ bottom = "#{bottom} #{x * -n}"
1562
+ top.gsub!(/#{item[0]}(\^|\*\*)#{n}/, '')
1452
1563
  end
1453
1564
  end
1454
1565
  if bottom
1455
- bottom.gsub!(BOTTOM_REGEX) { |s| "#{$1} " * $2.to_i }
1566
+ bottom.gsub!(BOTTOM_REGEX) { "#{$1} " * $2.to_i }
1456
1567
  # Separate leading decimal from denominator, if any
1457
1568
  bottom_scalar, bottom = bottom.scan(NUMBER_UNIT_REGEX)[0]
1458
1569
  end
1459
1570
 
1460
1571
  @scalar = @scalar.to_f unless @scalar.nil? || @scalar.empty?
1461
- @scalar = 1 unless @scalar.kind_of? Numeric
1462
- @scalar = @scalar.to_int if (@scalar.to_int == @scalar)
1572
+ @scalar = 1 unless @scalar.is_a? Numeric
1573
+ @scalar = @scalar.to_int if @scalar.to_int == @scalar
1463
1574
 
1464
- case
1465
- when bottom_scalar.nil? || bottom_scalar.empty?
1466
- when bottom_scalar.to_i == bottom_scalar
1467
- @scalar /= bottom_scalar.to_i
1468
- else
1469
- @scalar /= bottom_scalar.to_f
1470
- end
1575
+ bottom_scalar = 1 if bottom_scalar.nil? || bottom_scalar.empty?
1576
+ bottom_scalar = if bottom_scalar.to_i == bottom_scalar
1577
+ bottom_scalar.to_i
1578
+ else
1579
+ bottom_scalar.to_f
1580
+ end
1471
1581
 
1582
+ @scalar /= bottom_scalar
1472
1583
 
1473
1584
  @numerator ||= UNITY_ARRAY
1474
1585
  @denominator ||= UNITY_ARRAY
1475
- @numerator = top.scan(RubyUnits::Unit.unit_match_regex).delete_if { |x| x.empty? }.compact if top
1476
- @denominator = bottom.scan(RubyUnits::Unit.unit_match_regex).delete_if { |x| x.empty? }.compact if bottom
1586
+ @numerator = top.scan(RubyUnits::Unit.unit_match_regex).delete_if(&:empty?).compact if top
1587
+ @denominator = bottom.scan(RubyUnits::Unit.unit_match_regex).delete_if(&:empty?).compact if bottom
1477
1588
 
1478
1589
  # eliminate all known terms from this string. This is a quick check to see if the passed unit
1479
1590
  # contains terms that are not defined.
1480
- used = "#{top} #{bottom}".to_s.gsub(RubyUnits::Unit.unit_match_regex, '').gsub(/[\d\*, "'_^\/\$]/, '')
1591
+ used = "#{top} #{bottom}".to_s.gsub(RubyUnits::Unit.unit_match_regex, '').gsub(%r{[\d\*, "'_^\/\$]}, '')
1481
1592
  raise(ArgumentError, "'#{passed_unit_string}' Unit not recognized") unless used.empty?
1482
1593
 
1483
1594
  @numerator = @numerator.map do |item|
1484
- @@PREFIX_MAP[item[0]] ? [@@PREFIX_MAP[item[0]], @@UNIT_MAP[item[1]]] : [@@UNIT_MAP[item[1]]]
1485
- end.flatten.compact.delete_if { |x| x.empty? }
1595
+ @@prefix_map[item[0]] ? [@@prefix_map[item[0]], @@unit_map[item[1]]] : [@@unit_map[item[1]]]
1596
+ end.flatten.compact.delete_if(&:empty?)
1486
1597
 
1487
1598
  @denominator = @denominator.map do |item|
1488
- @@PREFIX_MAP[item[0]] ? [@@PREFIX_MAP[item[0]], @@UNIT_MAP[item[1]]] : [@@UNIT_MAP[item[1]]]
1489
- end.flatten.compact.delete_if { |x| x.empty? }
1599
+ @@prefix_map[item[0]] ? [@@prefix_map[item[0]], @@unit_map[item[1]]] : [@@unit_map[item[1]]]
1600
+ end.flatten.compact.delete_if(&:empty?)
1490
1601
 
1491
1602
  @numerator = UNITY_ARRAY if @numerator.empty?
1492
1603
  @denominator = UNITY_ARRAY if @denominator.empty?
1493
- return self
1494
- end
1495
-
1496
- # return an array of base units
1497
- # @return [Array]
1498
- def self.base_units
1499
- return @@base_units ||= @@definitions.dup.delete_if { |_, defn| !defn.base? }.keys.map { |u| RubyUnits::Unit.new(u) }
1500
- end
1501
-
1502
- # parse a string consisting of a number and a unit string
1503
- # NOTE: This does not properly handle units formatted like '12mg/6ml'
1504
- # @param [String] string
1505
- # @return [Array] consisting of [Numeric, "unit"]
1506
- # @private
1507
- def self.parse_into_numbers_and_units(string)
1508
- # scientific notation.... 123.234E22, -123.456e-10
1509
- sci = %r{[+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*}
1510
- # rational numbers.... -1/3, 1/5, 20/100, -6 1/2, -6-1/2
1511
- rational = %r{\(?[+-]?(?:\d+[ -])?\d+\/\d+\)?}
1512
- # complex numbers... -1.2+3i, +1.2-3.3i
1513
- complex = %r{#{sci}{2,2}i}
1514
- anynumber = %r{(?:(#{complex}|#{rational}|#{sci}))?\s?([^-\d\.].*)?}
1515
-
1516
- num, unit = string.scan(anynumber).first
1517
-
1518
- return [case num
1519
- when NilClass
1520
- 1
1521
- when complex
1522
- if num.respond_to?(:to_c)
1523
- num.to_c
1524
- else
1525
- #:nocov_19:
1526
- Complex(*num.scan(/(#{sci})(#{sci})i/).flatten.map { |n| n.to_i })
1527
- #:nocov_19:
1528
- end
1529
- when rational
1530
- # if it has whitespace, it will be of the form '6 1/2'
1531
- if num =~ RATIONAL_NUMBER
1532
- sign = ($1 == '-') ? -1 : 1
1533
- n = $2.to_i
1534
- f = Rational($3.to_i,$4.to_i)
1535
- sign * (n + f)
1536
- else
1537
- Rational(*num.split("/").map { |x| x.to_i })
1538
- end
1539
- else
1540
- num.to_f
1541
- end, unit.to_s.strip]
1542
- end
1543
-
1544
- # return a fragment of a regex to be used for matching units or reconstruct it if hasn't been used yet.
1545
- # Unit names are reverse sorted by length so the regexp matcher will prefer longer and more specific names
1546
- # @return [String]
1547
- # @private
1548
- def self.unit_regex
1549
- @@UNIT_REGEX ||= @@UNIT_MAP.keys.sort_by { |unit_name| [unit_name.length, unit_name] }.reverse.join('|')
1550
- end
1551
-
1552
- # return a regex used to match units
1553
- # @return [RegExp]
1554
- # @private
1555
- def self.unit_match_regex
1556
- @@UNIT_MATCH_REGEX ||= /(#{RubyUnits::Unit.prefix_regex})??(#{RubyUnits::Unit.unit_regex})\b/
1557
- end
1558
-
1559
- # return a regexp fragment used to match prefixes
1560
- # @return [String]
1561
- # @private
1562
- def self.prefix_regex
1563
- return @@PREFIX_REGEX ||= @@PREFIX_MAP.keys.sort_by { |prefix| [prefix.length, prefix] }.reverse.join('|')
1564
- end
1565
-
1566
- def self.temp_regex
1567
- @@TEMP_REGEX ||= Regexp.new "(?:#{
1568
- temp_units=%w(tempK tempC tempF tempR degK degC degF degR)
1569
- aliases =temp_units.map { |unit| d=RubyUnits::Unit.definition(unit); d && d.aliases }.flatten.compact
1570
- regex_str = aliases.empty? ? '(?!x)x' : aliases.join('|')
1571
- regex_str
1572
- })"
1573
- end
1574
-
1575
- # inject a definition into the internal array and set it up for use
1576
- # @private
1577
- def self.use_definition(definition)
1578
- @@UNIT_MATCH_REGEX = nil #invalidate the unit match regex
1579
- @@TEMP_REGEX = nil #invalidate the temp regex
1580
- if definition.prefix?
1581
- @@PREFIX_VALUES[definition.name] = definition.scalar
1582
- definition.aliases.each { |_alias| @@PREFIX_MAP[_alias] = definition.name }
1583
- @@PREFIX_REGEX = nil #invalidate the prefix regex
1584
- else
1585
- @@UNIT_VALUES[definition.name] = {}
1586
- @@UNIT_VALUES[definition.name][:scalar] = definition.scalar
1587
- @@UNIT_VALUES[definition.name][:numerator] = definition.numerator if definition.numerator
1588
- @@UNIT_VALUES[definition.name][:denominator] = definition.denominator if definition.denominator
1589
- definition.aliases.each { |_alias| @@UNIT_MAP[_alias] = definition.name }
1590
- @@UNIT_REGEX = nil #invalidate the unit regex
1591
- end
1604
+ self
1592
1605
  end
1593
1606
  end
1594
1607
  end