unit_measurements 4.8.0 → 4.10.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.
@@ -3,6 +3,28 @@
3
3
  # -*- warn_indent: true -*-
4
4
 
5
5
  module UnitMeasurements
6
+ # @abstract
7
+ # The +UnitMeasurements::Measurement+ is the abstract class and serves as superclass
8
+ # for all the unit groups. It includes several modules that provide mathematical
9
+ # operations, comparison, conversion, formatting, and other functionalities.
10
+ #
11
+ # This class provides a comprehensive set of methods and functionality for working
12
+ # with measurements in different units. It includes robust error handling and
13
+ # supports conversion between units. Additionally, it ensures that measurements
14
+ # are consistently represented.
15
+ #
16
+ # You should not directly initialize a +Measurement+ instance. Instead, create
17
+ # specialized measurement types by subclassing +Measurement+ and providing
18
+ # specific units and conversions through the +build+ method defined in the
19
+ # +UnitMeasurements+ module.
20
+ #
21
+ # @see Arithmetic
22
+ # @see Comparison
23
+ # @see Conversion
24
+ # @see Formatter
25
+ # @see Math
26
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
27
+ # @since 1.0.0
6
28
  class Measurement
7
29
  include Arithmetic
8
30
  include Comparison
@@ -10,10 +32,61 @@ module UnitMeasurements
10
32
  include Formatter
11
33
  include Math
12
34
 
35
+ # Regular expression to match conversion strings.
13
36
  CONVERSION_STRING_REGEXP = /(.+?)\s?(?:\s+(?:in|to|as)\s+(.+)|\z)/i.freeze
14
37
 
15
- attr_reader :quantity, :unit
38
+ # Quantity of the measurement.
39
+ attr_reader :quantity
16
40
 
41
+ # The unit associated with the measurement.
42
+ attr_reader :unit
43
+
44
+ # Initializes a new instance of +Measurement+ with a specified +quantity+
45
+ # and +unit+.
46
+ #
47
+ # This method is intended to be overridden by subclasses and serves as a
48
+ # placeholder for common initialization logic. It raises an error if called
49
+ # directly on the abstract +Measurement+ class.
50
+ #
51
+ # @example Initializing the measurement with scientific number and unit:
52
+ # UnitMeasurements::Length.new(BigDecimal(2), "km")
53
+ # => 2.0 km
54
+ #
55
+ # UnitMeasurements::Length.new("2e+2", "km")
56
+ # => 200.0 km
57
+ #
58
+ # @example Initializing the measurement with complex number and unit:
59
+ # UnitMeasurements::Length.new(Complex(2, 3), "km")
60
+ # => 2+3i km
61
+ #
62
+ # UnitMeasurements::Length.new("2+3i", "km")
63
+ # => 2.0+3.0i km
64
+ #
65
+ # @example Initializing the measurement with rational or mixed rational number and unit:
66
+ # UnitMeasurements::Length.new(Rational(2, 3), "km")
67
+ # => 0.6666666666666666 km
68
+ #
69
+ # UnitMeasurements::Length.new(2/3r, "km")
70
+ # => 2/3 km
71
+ #
72
+ # UnitMeasurements::Length.new("2/3", "km")
73
+ # => 0.6666666666666666 km
74
+ #
75
+ # UnitMeasurements::Length.new("½", "km")
76
+ # => 0.5 km
77
+ #
78
+ # @example Initializing the measurement with ratio and unit:
79
+ # UnitMeasurements::Length.new("1:2", "km")
80
+ # => 0.5 km
81
+ #
82
+ # @param [Numeric|String] quantity The quantity of the measurement.
83
+ # @param [String|Unit] unit The unit of the measurement.
84
+ #
85
+ # @raise [BaseError] If +quantity+ or +unit+ is blank.
86
+ #
87
+ # @see BaseError
88
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
89
+ # @since 1.0.0
17
90
  def initialize(quantity, unit)
18
91
  raise BaseError, "Quantity cannot be blank." if quantity.blank?
19
92
  raise BaseError, "Unit cannot be blank." if unit.blank?
@@ -22,6 +95,20 @@ module UnitMeasurements
22
95
  @unit = unit_from_unit_or_name!(unit)
23
96
  end
24
97
 
98
+ # Converts the measurement to a +target_unit+.
99
+ #
100
+ # @example
101
+ # UnitMeasurements::Length.new(1, "m").convert_to("cm")
102
+ # => 100.0 cm
103
+ #
104
+ # @param [String|Symbol] target_unit The target unit for conversion.
105
+ #
106
+ # @return [Measurement]
107
+ # A new +Measurement+ instance with the converted +quantity+ and
108
+ # +target unit+.
109
+ #
110
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
111
+ # @since 1.0.0
25
112
  def convert_to(target_unit)
26
113
  target_unit = unit_from_unit_or_name!(target_unit)
27
114
 
@@ -31,24 +118,61 @@ module UnitMeasurements
31
118
 
32
119
  self.class.new((quantity * conversion_factor), target_unit)
33
120
  end
34
- [:to, :in, :as].each { |method_alias| alias_method method_alias, :convert_to }
121
+ alias_method :to, :convert_to
122
+ alias_method :in, :convert_to
123
+ alias_method :as, :convert_to
35
124
 
125
+ # Converts the measurement to a +target_unit+ and updates the current instance.
126
+ #
127
+ # @example
128
+ # UnitMeasurements::Length.new(1, "m").convert_to!("cm")
129
+ # => 100.0 cm
130
+ #
131
+ # @param [String|Symbol] target_unit The target unit for conversion.
132
+ #
133
+ # @return [Measurement]
134
+ # The current +Measurement+ instance with updated +quantity+ and +unit+.
135
+ #
136
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
137
+ # @since 1.0.0
36
138
  def convert_to!(target_unit)
37
139
  measurement = convert_to(target_unit)
38
140
  @quantity, @unit = measurement.quantity, measurement.unit
39
141
 
40
142
  self
41
143
  end
42
- [:to!, :in!, :as!].each { |method_alias| alias_method method_alias, :convert_to! }
144
+ alias_method :to!, :convert_to!
145
+ alias_method :in!, :convert_to!
146
+ alias_method :as!, :convert_to!
43
147
 
148
+ # Returns an object representation of the +Measurement+.
149
+ #
150
+ # @param [TrueClass|FalseClass] dump If +true+, returns the dump representation.
151
+ #
152
+ # @return [Object] An object representation of the +Measurement+.
153
+ #
154
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
155
+ # @since 1.0.0
44
156
  def inspect(dump: false)
45
157
  dump ? super() : to_s
46
158
  end
47
159
 
160
+ # Returns a string representation of the +Measurement+.
161
+ #
162
+ # @return [String] A string representation of the +Measurement+.
163
+ #
164
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
165
+ # @since 1.0.0
48
166
  def to_s
49
167
  "#{quantity} #{unit}"
50
168
  end
51
169
 
170
+ # Returns the +quantity+ of the +measurement+.
171
+ #
172
+ # @return [Numeric] Quantity of the measurement.
173
+ #
174
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
175
+ # @since 1.5.0
52
176
  def quantity
53
177
  case @quantity
54
178
  when Rational
@@ -61,14 +185,90 @@ module UnitMeasurements
61
185
  class << self
62
186
  extend Forwardable
63
187
 
64
- def unit_group
65
- raise BaseError, "`Measurement` does not have a `unit_group` object. You cannot directly use `Measurement`. Instead, build a new unit group by calling `UnitMeasurements.build`."
66
- end
67
-
188
+ # Methods delegated from the unit group.
68
189
  def_delegators :unit_group, :primitive, :units, :unit_names, :unit_with_name_and_aliases,
69
190
  :unit_names_with_aliases, :unit_for, :unit_for!, :defined?,
70
191
  :unit_or_alias?, :[]
71
192
 
193
+ # Parses an input string and returns a +Measurement+ instance depending on
194
+ # the input string. This method first normalizes the +input+ internally,
195
+ # using the +Normalizer+ before parsing it using the +Parser+.
196
+ #
197
+ # If only the source unit is provided, it returns a new +Measurement+
198
+ # instance with the quantity in the source unit.If both source and target
199
+ # units are provided in the input string, it returns a new +Measurement+
200
+ # instance with the quantity converted to the target unit.
201
+ #
202
+ # @example Parsing string representing a complex number and source unit:
203
+ # UnitMeasurements::Length.parse("2+3i km")
204
+ # => 2.0+3.0i km
205
+ #
206
+ # @example Parsing string representing a complex number, source, and target units:
207
+ # UnitMeasurements::Length.parse("2+3i km to m")
208
+ # => 2000.0+3000.0i m
209
+ #
210
+ # @example Parsing string representing a rational or mixed rational number and source unit:
211
+ # UnitMeasurements::Length.parse("½ km")
212
+ # => 0.5 km
213
+ #
214
+ # UnitMeasurements::Length.parse("2/3 km")
215
+ # => 0.666666666666667 km
216
+ #
217
+ # UnitMeasurements::Length.parse("2 ½ km")
218
+ # => 2.5 km
219
+ #
220
+ # UnitMeasurements::Length.parse("2 1/2 km")
221
+ # => 2.5 km
222
+ #
223
+ # @example Parsing string representing a rational or mixed rational number, source, and target units:
224
+ # UnitMeasurements::Length.parse("½ km to m")
225
+ # => 500.0 km
226
+ #
227
+ # UnitMeasurements::Length.parse("2/3 km to m")
228
+ # => 666.666666666667 m
229
+ #
230
+ # UnitMeasurements::Length.parse("2 ½ km to m")
231
+ # => 2500.0 m
232
+ #
233
+ # UnitMeasurements::Length.parse("2 1/2 km to m")
234
+ # => 2500.0 m
235
+ #
236
+ # @example Parsing string representing a scientific number and source unit:
237
+ # UnitMeasurements::Length.parse("2e² km")
238
+ # => 200.0 km
239
+ #
240
+ # UnitMeasurements::Length.parse("2e+2 km")
241
+ # => 200.0 km
242
+ #
243
+ # UnitMeasurements::Length.parse("2e⁻² km")
244
+ # => 0.02 km
245
+ #
246
+ # @example Parsing string representing a scientific number, source, and target units:
247
+ #
248
+ # UnitMeasurements::Length.parse("2e+2 km to m")
249
+ # => 200000.0 m
250
+ #
251
+ # UnitMeasurements::Length.parse("2e⁻² km to m")
252
+ # => 20.0 m
253
+ #
254
+ # @example Parsing string representing a ratio and source unit:
255
+ # UnitMeasurements::Length.parse("1:2 km")
256
+ # => 0.5 km
257
+ #
258
+ # @example Parsing string representing a ratio, source, and target units:
259
+ # UnitMeasurements::Length.parse("1:2 km to m")
260
+ # => 500.0 m
261
+ #
262
+ # @param [String] input The input string to be parsed.
263
+ #
264
+ # @return [Measurement] The +Measurement+ instance.
265
+ #
266
+ # @see Parser
267
+ # @see Normalizer
268
+ # @see CONVERSION_STRING_REGEXP
269
+ # @see _parse
270
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
271
+ # @since 1.0.0
72
272
  def parse(input)
73
273
  input = Normalizer.normalize(input)
74
274
  source, target = input.match(CONVERSION_STRING_REGEXP)&.captures
@@ -78,6 +278,30 @@ module UnitMeasurements
78
278
 
79
279
  private
80
280
 
281
+ # @private
282
+ # The class attribute representing an instance of +UnitGroup+.
283
+ #
284
+ # @return [UnitGroup] An instance of +UnitGroup+.
285
+ #
286
+ # @raise [BaseError]
287
+ # If directly invoked on +Measurement+ rather than its subclasses.
288
+ #
289
+ # @see UnitGroup
290
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
291
+ # @since 1.0.0
292
+ def unit_group
293
+ raise BaseError, "`Measurement` does not have a `unit_group` instance. You cannot directly use `Measurement`. Instead, build a new unit group by calling `UnitMeasurements.build`."
294
+ end
295
+
296
+ # @private
297
+ # Parses the normalized string to return the +Measurement+ instance.
298
+ #
299
+ # @param [String] string String to be parsed.
300
+ #
301
+ # @return [Measurement] The +Measurement+ instance.
302
+ #
303
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
304
+ # @since 1.0.0
81
305
  def _parse(string)
82
306
  quantity, unit = Parser.parse(string)
83
307
 
@@ -87,6 +311,15 @@ module UnitMeasurements
87
311
 
88
312
  private
89
313
 
314
+ # @private
315
+ # Converts the measurement quantity to a suitable format for internal use.
316
+ #
317
+ # @param [Numeric|String] quantity The quantity of the measurement.
318
+ #
319
+ # @return [Numeric] The converted quantity.
320
+ #
321
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
322
+ # @since 1.0.0
90
323
  def convert_quantity(quantity)
91
324
  case quantity
92
325
  when Float
@@ -103,8 +336,18 @@ module UnitMeasurements
103
336
  end
104
337
  end
105
338
 
339
+ # @private
340
+ # Returns the +Unit+ instance associated with the +value+ provided.
341
+ #
342
+ # @param [String|Unit] value
343
+ # The value representing a unit name or +Unit+ instance.
344
+ #
345
+ # @return [Unit] The +Unit+ instance associated with +value+.
346
+ #
347
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
348
+ # @since 1.0.0
106
349
  def unit_from_unit_or_name!(value)
107
- value.is_a?(Unit) ? value : self.class.unit_group.unit_for!(value)
350
+ value.is_a?(Unit) ? value : self.class.send(:unit_group).unit_for!(value)
108
351
  end
109
352
  end
110
353
  end
@@ -3,7 +3,22 @@
3
3
  # -*- warn_indent: true -*-
4
4
 
5
5
  module UnitMeasurements
6
+ # The +UnitMeasurements::Normalizer+ class defines useful methods for handling
7
+ # input strings that may contain non-standard representations of numbers.
8
+ #
9
+ # It maps special symbols of exponents and fractions to their standard
10
+ # counterparts. The class ensures consistent handling of input strings within
11
+ # the library.
12
+ #
13
+ # @see Measurement
14
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
15
+ # @since 1.0.0
6
16
  class Normalizer
17
+ # A mapping of superscript numbers, plus, and minus signs to their regular
18
+ # counterparts.
19
+ #
20
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
21
+ # @since 1.0.0
7
22
  EXPONENTS_SYMBOLS = {
8
23
  "⁰" => "0",
9
24
  "¹" => "1",
@@ -19,6 +34,10 @@ module UnitMeasurements
19
34
  "⁻" => "-",
20
35
  }.freeze
21
36
 
37
+ # A mapping of special fraction symbols to their equivalent numeric fractions.
38
+ #
39
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
40
+ # @since 1.0.0
22
41
  FRACTIONS_SYMBOLS = {
23
42
  "¼" => "1/4",
24
43
  "½" => "1/2",
@@ -41,11 +60,81 @@ module UnitMeasurements
41
60
  "↉" => "0/3",
42
61
  }.freeze
43
62
 
44
- EXPONENT_REGEX = /([\d]+[Ee]?[+-]?)(#{EXPONENTS_SYMBOLS.keys.join("|")})/.freeze
45
- FRACTION_REGEX = /(#{FRACTIONS_SYMBOLS.keys.join("|")})/.freeze
46
- RATIO_REGEX = /([\d]+):([\d]+)/.freeze
63
+ # Matches a combination of digits, optional exponent notation, and optional plus or minus sign.
64
+ #
65
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
66
+ # @since 1.0.0
67
+ EXPONENT_REGEX = /
68
+ ( # Start of the first capturing group
69
+ [\d]+ # One or more digits
70
+ [Ee]? # Match an optional 'E' or 'e' for scientific notation
71
+ [+-]? # Match an optional plus or minus sign
72
+ ) # End of the first capturing group
73
+ ( # Start of the second capturing group
74
+ #{EXPONENTS_SYMBOLS.keys.join("|")} # Match any of the exponent symbols defined in EXPONENTS_SYMBOLS
75
+ ) # End of the second capturing group
76
+ /x.freeze
77
+
78
+ # Matches special fraction symbols.
79
+ #
80
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
81
+ # @since 1.0.0
82
+ FRACTION_REGEX = /
83
+ ( # Start of the capturing group
84
+ #{FRACTIONS_SYMBOLS.keys.join("|")} # Match any of the fraction symbols defined in FRACTIONS_SYMBOLS
85
+ ) # End of the capturing group
86
+ /x.freeze
87
+
88
+ # Matches a ratio in the format of +numerator:denominator+.
89
+ #
90
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
91
+ # @since 1.0.0
92
+ RATIO_REGEX = /
93
+ ( # Start of the first capturing group
94
+ [\d]+ # One or more digits, representing the numerator
95
+ ) # End of the first capturing group
96
+ : # Match a colon (:)
97
+ ( # Start of the second capturing group
98
+ [\d]+ # One or more digits, representing the denominator
99
+ ) # End of the second capturing group
100
+ /x.freeze
101
+
47
102
 
48
103
  class << self
104
+ # Normalizes a string containing special symbols of exponents and fractions,
105
+ # and ratio.
106
+ #
107
+ # The +normalize+ method in the +UnitMeasurements::Normalizer+ module processes
108
+ # the input string to replace special symbols with their equivalent representations,
109
+ # such as converting superscript numbers to regular numbers, special fraction
110
+ # symbols to their numeric fractions, and ratios to fractional format, and
111
+ # remove leading and trailing whitespaces from the +string+.
112
+ #
113
+ # @example Normalizing special symbol of fraction:
114
+ # UnitMeasurements::Normalizer.normalize("¾")
115
+ # => "3/4"
116
+ #
117
+ # UnitMeasurements::Normalizer.normalize(" 1¾ ")
118
+ # => "1 3/4"
119
+ #
120
+ # @example Normalizing ratio:
121
+ # UnitMeasurements::Normalizer.normalize("1:2")
122
+ # => "1/2"
123
+ #
124
+ # @example Normalizing special symbol of exponents:
125
+ # UnitMeasurements::Normalizer.normalize("1e⁺²")
126
+ # => "1e+2"
127
+ #
128
+ # UnitMeasurements::Normalizer.normalize("1e⁻²")
129
+ # => "1e-2"
130
+ #
131
+ # @param [String] string The input string to be normalized.
132
+ #
133
+ # @return [String] The normalized version of the input string.
134
+ #
135
+ # @see Measurement
136
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
137
+ # @since 1.0.0
49
138
  def normalize(string)
50
139
  string.dup.tap do |str|
51
140
  if str =~ Regexp.new(EXPONENT_REGEX)
@@ -3,18 +3,144 @@
3
3
  # -*- warn_indent: true -*-
4
4
 
5
5
  module UnitMeasurements
6
+ # The +UnitMeasurements::Parser+ class provides methods for parsing strings to
7
+ # extract quantity and associated unit. It can handle various formats, including
8
+ # complex numbers, scientific numbers, and rational numbers for the +quantity+.
9
+ #
10
+ # @see Measurement
11
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
12
+ # @since 1.0.0
6
13
  class Parser
7
- UNIT_REGEX = /([^\d\s\/].*)/.freeze
14
+ # Matches any character sequence that does not consist of digits, whitespace,
15
+ # or forward slashes.
16
+ #
17
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
18
+ # @since 1.0.0
19
+ UNIT_REGEX = /
20
+ ( # Start of the capturing group
21
+ [^\d\s\/] # Match any character that is not a digit, whitespace, or a forward slash
22
+ .* # Match zero or more of any character (except for a newline)
23
+ ) # End of the capturing group
24
+ /x.freeze
8
25
 
9
- SCIENTIFIC_NUMBER = /([+-]?\d*\.?\d+(?:[Ee][+-]?)?\d*)/.freeze
10
- RATIONAL_NUMBER = /([+-]?\d+\s+)?((\d+)\/(\d+))/.freeze
11
- COMPLEX_NUMBER = /#{SCIENTIFIC_NUMBER}#{SCIENTIFIC_NUMBER}i/.freeze
26
+ # Matches scientific numbers (e.g., +1.23e-4+).
27
+ #
28
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
29
+ # @since 1.0.0
30
+ SCIENTIFIC_NUMBER = /
31
+ ( # Start of a capturing group (denoted by parentheses)
32
+ [+-]? # Match an optional plus or minus sign (+ or -)
33
+ \d* # Match zero or more digits
34
+ \.? # Match an optional dot (.)
35
+ \d+ # Match one or more digits
36
+ (?: # Start of a non-capturing group
37
+ [Ee] # Match either 'E' or 'e'
38
+ [+-]? # Match an optional plus or minus sign (+ or -)
39
+ )? # End of the non-capturing group; ? makes it optional
40
+ \d* # Match zero or more digits
41
+ ) # End of the capturing group
42
+ /x.freeze
12
43
 
13
- SCIENTIFIC_REGEX = /\A#{SCIENTIFIC_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
14
- RATIONAL_REGEX = /\A#{RATIONAL_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
15
- COMPLEX_REGEX = /\A#{COMPLEX_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
44
+ # Matches rational numbers (e.g., +1/2+).
45
+ #
46
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
47
+ # @since 1.0.0
48
+ RATIONAL_NUMBER = /
49
+ ( # Start of the first capturing group
50
+ [+-]? # Match an optional plus or minus sign (+ or -)
51
+ \d+ # Match one or more digits
52
+ \s+ # Match one or more whitespace characters
53
+ )? # End of the first capturing group
54
+ ( # Start of the second capturing group (the fraction part)
55
+ (\d+) # Start of the third capturing group (one or more digits, the numerator)
56
+ \/ # Match a forward slash - the division symbol
57
+ (\d+) # Start of the fourth capturing group (one or more digits, the denominator)
58
+ ) # End of the second capturing group (the fraction part)
59
+ /x.freeze
60
+
61
+ # Matches complex numbers (e.g., +1-2i+).
62
+ #
63
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
64
+ # @since 1.0.0
65
+ COMPLEX_NUMBER = /
66
+ #{SCIENTIFIC_NUMBER} # Pattern for scientific number
67
+ #{SCIENTIFIC_NUMBER} # Pattern for scientific number
68
+ i # Match the letter 'i' (the imaginary unit)
69
+ /x.freeze
70
+
71
+ # Matches strings containing scientific numbers and unit.
72
+ #
73
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
74
+ # @since 1.0.0
75
+ SCIENTIFIC_REGEX = /
76
+ \A # Anchor at the start of the string
77
+ #{SCIENTIFIC_NUMBER} # Match a scientific number (as defined earlier)
78
+ \s* # Match zero or more whitespace characters
79
+ #{UNIT_REGEX}? # Match a unit, the '?' makes it optional
80
+ \z # Anchor at the end of the string
81
+ /x.freeze
82
+
83
+ # Matches strings containing rational numbers and unit.
84
+ #
85
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
86
+ # @since 1.0.0
87
+ RATIONAL_REGEX = /
88
+ \A # Anchor at the start of the string
89
+ #{RATIONAL_NUMBER} # Match a rational number (as defined earlier)
90
+ \s* # Match zero or more whitespace characters
91
+ #{UNIT_REGEX}? # Match a unit, the '?' makes it optional
92
+ \z
93
+ /x.freeze
94
+
95
+ # Matches strings containing complex numbers and unit.
96
+ #
97
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
98
+ # @since 1.0.0
99
+ COMPLEX_REGEX = /
100
+ \A # Anchor at the start of the string
101
+ #{COMPLEX_NUMBER} # Match a complex number (as defined earlier)
102
+ \s* # Match zero or more whitespace characters
103
+ #{UNIT_REGEX}? # Match a unit, the '?' makes it optional
104
+ \z
105
+ /x.freeze
16
106
 
17
107
  class << self
108
+ # Parses a string to extract a +quantity+ and its associated +unit+. This
109
+ # method first extracts a +quantity+ and converts it to +Float+ before
110
+ # returning it.
111
+ #
112
+ # To get the correct parsed results, you must first normalize the +string+
113
+ # with +Normalizer+ if using the parser standalone.
114
+ #
115
+ # @example Parsing string representing a complex number:
116
+ # UnitMeasurements::Parser.parse("1+2i m")
117
+ # => [(1.0+2.0i), "m"]
118
+ #
119
+ # @example Parsing string representing a rational number:
120
+ # UnitMeasurements::Parser.parse("1/2 m")
121
+ # => [0.5, "m"]
122
+ #
123
+ # @example Parsing string representing a mixed rational number:
124
+ # UnitMeasurements::Parser.parse("2 1/2 km")
125
+ # => [2.5, "km"]
126
+ #
127
+ # @example Parsing string representing a scientific number:
128
+ # UnitMeasurements::Parser.parse("1e+2 km")
129
+ # => [100.0, "km"]
130
+ #
131
+ # @param [String] string
132
+ # The input string containing a +quantity+ and an optional +unit+.
133
+ #
134
+ # @return [Array<Numeric, String|NilClass>]
135
+ # The parsed +quantity+ and the +unit+ associated with it (or +nil+ if
136
+ # no unit is specified in the +string+).
137
+ #
138
+ # @raise [ParseError] If the string is invalid and cannot be parsed.
139
+ #
140
+ # @see Measurement
141
+ # @see Normalizer
142
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
143
+ # @since 1.0.0
18
144
  def parse(string)
19
145
  case string
20
146
  when COMPLEX_REGEX then parse_complex(string)
@@ -26,6 +152,18 @@ module UnitMeasurements
26
152
 
27
153
  private
28
154
 
155
+ # @private
156
+ # Parses a string representing a complex number with an optional unit.
157
+ #
158
+ # @param [String] string
159
+ # The input string containing a complex number and an optional unit.
160
+ #
161
+ # @return [Array<Numeric, String|NilClass>]
162
+ # The parsed complex number and the associated unit (or +nil+ if no unit
163
+ # is specified in the string).
164
+ #
165
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
166
+ # @since 1.0.0
29
167
  def parse_complex(string)
30
168
  real, imaginary, unit = string.match(COMPLEX_REGEX)&.captures
31
169
  quantity = Complex(real.to_f, imaginary.to_f)
@@ -33,6 +171,18 @@ module UnitMeasurements
33
171
  [quantity, unit]
34
172
  end
35
173
 
174
+ # @private
175
+ # Parses a string representing a scientific number with an optional unit.
176
+ #
177
+ # @param [String] string
178
+ # The input string containing a scientific number and an optional unit.
179
+ #
180
+ # @return [Array<Numeric, String|NilClass>]
181
+ # The parsed scientific number and the associated unit (or +nil+ if no unit
182
+ # is specified in the string).
183
+ #
184
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
185
+ # @since 1.0.0
36
186
  def parse_scientific(string)
37
187
  whole, unit = string.match(SCIENTIFIC_REGEX)&.captures
38
188
  quantity = whole.to_f
@@ -40,6 +190,18 @@ module UnitMeasurements
40
190
  [quantity, unit]
41
191
  end
42
192
 
193
+ # @private
194
+ # Parses a string representing a rational number with an optional unit.
195
+ #
196
+ # @param [String] string
197
+ # The input string containing a rational number and an optional unit.
198
+ #
199
+ # @return [Array<Numeric, String|NilClass>]
200
+ # The parsed rational number and the associated unit (or +nil+ if no unit
201
+ # is specified in the string).
202
+ #
203
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
204
+ # @since 1.0.0
43
205
  def parse_rational(string)
44
206
  whole, _, numerator, denominator, unit = string.match(RATIONAL_REGEX)&.captures
45
207