ruby-units 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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