composite_unit_measurements 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e212c20da9787a853fef2791d5f95d0004e5e45225c3809088205a1375b600c
4
- data.tar.gz: 8b2c03cfdfba50cc0e13088bbfb8910afd249b98ec4048280441131f47a310f5
3
+ metadata.gz: e4450805d84ddd3b1cc972d152d187f44cbb1624d87dfece055c6db82ae70d20
4
+ data.tar.gz: 621ccd8a46402e7592dd93c8bf0683e414714e3f3fb67f1f4837cceabe96e5ab
5
5
  SHA512:
6
- metadata.gz: c1a6c264037d7db78d70676137d60a1085bb7126034674c6156de7806ef9c76263a8acd8bd4125fa3655d1c6c64c1ccec3bdd04d004dcc93dbd0e4ad6e056c86
7
- data.tar.gz: 394309d1a68c9eed5548f446fe3816ae467c7f2f6c4e66428d9957ae1ca5ed376659e4c7ff4fe1adc731cee85a8ea340577d120c65038b312398cf8f10dde6af
6
+ metadata.gz: 56937734d56198b02eb4c51c9b446558ec31a69bc123489ec371d8f36e7758efda6cf8d50ea85e947e57cf72f003e795a7897d850ff5a228f7505dc53b2aea36
7
+ data.tar.gz: d1e8a2129c276a186c8dd8bd7bf4cf5b29e2ff46c9dcff98b6928fba858b5d64441645c905ba273fd82bf547f43d14d1b0b25d2342d2219869d8d0a9b2a4d606
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.2.0 - 2023-11-17
2
+
3
+ ### What's new
4
+
5
+ - Added ability to parse composite measurements containing `real`, `scientific`,
6
+ `rational`, and `complex` numbers.
7
+ - Added ability to parse `foot-inch` length measurement.
8
+ - Added ability to parse `duration`.
9
+ - Added ability to parse `pound-ounce` and `stone-pound` weight measurement.
10
+
11
+ -----------
12
+
1
13
  ## 0.1.0 - 2023-11-09
2
14
 
3
15
  ### Initial release
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- composite_unit_measurements (0.1.0)
4
+ composite_unit_measurements (0.2.0)
5
5
  activesupport (~> 7.0)
6
6
  unit_measurements (~> 5)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activesupport (7.1.1)
11
+ activesupport (7.1.2)
12
12
  base64
13
13
  bigdecimal
14
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -54,7 +54,7 @@ GEM
54
54
  simplecov_json_formatter (0.1.4)
55
55
  tzinfo (2.0.6)
56
56
  concurrent-ruby (~> 1.0)
57
- unit_measurements (5.8.0)
57
+ unit_measurements (5.11.1)
58
58
  activesupport (~> 7.0)
59
59
 
60
60
  PLATFORMS
data/README.md CHANGED
@@ -37,6 +37,73 @@ Or otherwise simply install it yourself as:
37
37
 
38
38
  `$ gem install composite_unit_measurements`
39
39
 
40
+ ## Usage
41
+
42
+ Each packaged parser includes the `#parse` method to parse composite measurements.
43
+ You can use an appropriate parser to parse measurements. The final result of `#parse`
44
+ is returned in the leftmost unit of your measurement.
45
+
46
+ This gem internally uses [`unit_measurements`](https://github.com/shivam091/unit_measurements)
47
+ to perform conversions and arithmetic operations. You can use any
48
+ [alias of the units](https://github.com/shivam091/unit_measurements/blob/main/units.md)
49
+ to build a supported composite measurements.
50
+
51
+ ```ruby
52
+ CompositeUnitMeasurements::Length.parse("5 feet 6 inches")
53
+ #=> 5.5 ft
54
+ CompositeUnitMeasurements::Weight.parse("8 pound 12 ounce")
55
+ #=> 8.75 lb
56
+ CompositeUnitMeasurements::Time.parse("12:60:60,60")
57
+ #=> 13.0166666833333333666667 h
58
+ ```
59
+
60
+ Each parser has capability to parse `real`, `rational`, `scientific`, and `complex` numbers.
61
+
62
+ ```ruby
63
+ CompositeUnitMeasurements::Length.parse("1+2i ft 12 in")
64
+ #=> 2.0+2.0i ft
65
+ CompositeUnitMeasurements::Length.parse("1.5 ft 12e2 in")
66
+ #=> 101.5 ft
67
+ CompositeUnitMeasurements::Length.parse("1 1/2 ft 1+2i in")
68
+ #=> 1.5833333333333333+0.16666666666666669i ft
69
+ CompositeUnitMeasurements::Length.parse("2 ft 1+2i in")
70
+ #=> 2.0833333333333335+0.16666666666666669i ft
71
+ CompositeUnitMeasurements::Length.parse("1e-2 ft 1+2i in")
72
+ #=> 0.09333333333333334+0.16666666666666669i ft
73
+ ```
74
+
75
+ ## Packaged parsers & supported composite measurements
76
+
77
+ There are tons of composite measurements that are bundled with `composite_unit_measurements`.
78
+
79
+ **1. Unitify::CompositeMeasurements::Length**
80
+ - foot-inch (5 ft 6 in)
81
+
82
+ **2. Unitify::CompositeMeasurements::Weight**
83
+ - pound-ounce (8 lb 12 oz)
84
+ - stone-pound (2 st 6 lb)
85
+
86
+ **3. Unitify::CompositeMeasurements::Time**
87
+ - hour-minute-second-microsecond (12:60:60,60)
88
+
89
+ ### Specifing parsers
90
+
91
+ By default, `composite_unit_measurements` ships with all the packaged parsers and
92
+ this happens automatically when you require the gem in the following manner.
93
+
94
+ ```ruby
95
+ require "composite_unit_measurements"
96
+ ```
97
+
98
+ You can also use parsers in your application as per your need as:
99
+
100
+ ```ruby
101
+ require "composite_unit_measurements/base"
102
+
103
+ require "composite_unit_measurements/length"
104
+ require "composite_unit_measurements/weight"
105
+ ```
106
+
40
107
  ## Contributing
41
108
 
42
109
  1. Fork it
@@ -5,3 +5,53 @@
5
5
  require "unit_measurements/base"
6
6
 
7
7
  require "composite_unit_measurements/version"
8
+
9
+ module CompositeUnitMeasurements
10
+ # Matches real numbers in the form of 31, +72, or -12.
11
+ REAL_NUMBER = /
12
+ (?: # Start of non-capturing group
13
+ [+-]? # Optional plus (+) or minus (-) sign
14
+ \d+ # One or more digits
15
+ ) # End of non-capturing group
16
+ /x.freeze
17
+
18
+ # Matches a rational number in the form of a/b (fractional) or a b/c (mixed fractional).
19
+ RATIONAL_NUMBER = /
20
+ (?: # Start of optional non-capturing group
21
+ [+-]? # Optional plus (+) or minus (-) sign
22
+ \d+ # One or more digits
23
+ \s+ # One or more whitespace
24
+ )? # End of optional non-capturing group
25
+ ( # Start of capturing group for the fraction
26
+ (\d+) # Capture the numerator (one or more digits)
27
+ \/ # Match the forward slash, indicating division
28
+ (\d+) # Capture the denominator (one or more digits)
29
+ ) # End of capturing group for the fraction
30
+ /x.freeze
31
+
32
+ # Matches a scientific number in various formats like 1.23E+4 or -5.67e-8.
33
+ SCIENTIFIC_NUMBER = /
34
+ (?: # Start of non-capturing group
35
+ [+-]? # Optional plus (+) or minus (-) sign
36
+ \d* # Zero or more digits (integer part)
37
+ \.? # Optional decimal point
38
+ \d+ # One or more digits (fractional part)
39
+ (?: # Start of non-capturing group for exponent part
40
+ [Ee] # Match 'E' or 'e' for exponentiation
41
+ [+-]? # Optional plus (+) or minus (-) sign for exponent
42
+ \d+ # One or more digits (exponent value)
43
+ )? # End of non-capturing group of exponent part (optional)
44
+ ) # End of non-capturing group
45
+ /x.freeze
46
+
47
+ # Matches complex numbers in the form of a+bi, where both 'a' and 'b' can be
48
+ # in scientific notation. It captures the real and imaginary parts.
49
+ COMPLEX_NUMBER = /
50
+ #{SCIENTIFIC_NUMBER} # Pattern for scientific number
51
+ #{SCIENTIFIC_NUMBER} # Pattern for scientific number
52
+ i # Match the letter 'i' (the imaginary unit)
53
+ /x.freeze
54
+
55
+ # Matches any number, including scientific, complex, rational, and real numbers.
56
+ ANY_NUMBER = /(?<number>#{SCIENTIFIC_NUMBER}|#{COMPLEX_NUMBER}|#{RATIONAL_NUMBER}|#{REAL_NUMBER})/.freeze
57
+ end
@@ -0,0 +1,74 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ require "unit_measurements/unit_groups/length"
6
+
7
+ module CompositeUnitMeasurements
8
+ # A parser handling +length+ measurements, particularly for composite units
9
+ # like +foot-inch+, +kilometre-metre+, +mile-yard+, etc.
10
+ #
11
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
12
+ # @since 0.2.0
13
+ class Length
14
+ class << self
15
+ # Parses a given +string+ into a +UnitMeasurements::Length+ object.
16
+ #
17
+ # @param [String] string The string to parse for length measurement.
18
+ # @return [UnitMeasurements::Length]
19
+ # Returns a UnitMeasurements::Length object if parsing is successful.
20
+ # @raise [UnitMeasurements::ParseError]
21
+ # If the string does not match any known format.
22
+ #
23
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
24
+ # @since 0.2.0
25
+ def parse(string)
26
+ case string
27
+ when FOOT_INCH then parse_foot_inch(string)
28
+ else raise UnitMeasurements::ParseError, string
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # @private
35
+ # Parses a +string+ representing a length in the format of +foot-inch+.
36
+ #
37
+ # @param [String] string
38
+ # The string representing length measurement in the format of *foot-inch*.
39
+ # @return [UnitMeasurements::Length]
40
+ # Returns a UnitMeasurements::Length object if parsing is successful.
41
+ #
42
+ # @see FOOT_INCH
43
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
44
+ # @since 0.2.0
45
+ def parse_foot_inch(string)
46
+ foot, inch = string.match(FOOT_INCH)&.captures
47
+
48
+ if foot && inch
49
+ UnitMeasurements::Length.new(foot, :ft) + UnitMeasurements::Length.new(inch, :in)
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ # Regex pattern for aliases of +foot+ unit.
57
+ #
58
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
59
+ # @since 0.2.0
60
+ FOOT_ALIASES = /(?:'|ft|foot|feet)/.freeze
61
+
62
+ # Regex pattern for aliases of +inch+ unit.
63
+ #
64
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
65
+ # @since 0.2.0
66
+ INCH_ALIASES = /(?:"|in|inch(?:es)?)/.freeze
67
+
68
+ # Regex pattern for parsing a length measurement in the format of +foot-inch+.
69
+ #
70
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
71
+ # @since 0.2.0
72
+ FOOT_INCH = /\A#{ANY_NUMBER}\s*#{FOOT_ALIASES}\s*#{ANY_NUMBER}\s*#{INCH_ALIASES}\z/.freeze
73
+ end
74
+ end
@@ -0,0 +1,67 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ require "unit_measurements/unit_groups/time"
6
+
7
+ module CompositeUnitMeasurements
8
+ # A parser handling +time+ measurements, particularly for composite units
9
+ # like +hour:minute:second,microsecond+, +minute-second+, +hour-minute+, etc.
10
+ #
11
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
12
+ # @since 0.2.0
13
+ class Time
14
+ class << self
15
+ # Parses a given +string+ into a +UnitMeasurements::Time+ object.
16
+ #
17
+ # @param [String] string The string to parse for time measurement.
18
+ # @return [UnitMeasurements::Time]
19
+ # Returns a UnitMeasurements::Time object if parsing is successful.
20
+ # @raise [UnitMeasurements::ParseError]
21
+ # If the string does not match any known format.
22
+ #
23
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
24
+ # @since 0.2.0
25
+ def parse(string)
26
+ case string
27
+ when DURATION then parse_duration(string)
28
+ else raise UnitMeasurements::ParseError, string
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # @private
35
+ # Parses a +string+ representing time duration in the format of
36
+ # +hour:minute:second,microsecond+ or +hour:minute:second+.
37
+ #
38
+ # @param [String] string The string representing time duration.
39
+ # @return [UnitMeasurements::Time]
40
+ # Returns a UnitMeasurements::Time object if parsing is successful.
41
+ # @raise [ArgumentError]
42
+ # Raises an ArgumentError for an invalid duration format.
43
+ #
44
+ # @see DURATION
45
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
46
+ # @since 0.2.0
47
+ def parse_duration(string)
48
+ hour, minute, second, microsecond = string.match(DURATION)&.captures
49
+ raise ArgumentError, "Invalid Duration" if [hour, minute, second, microsecond].all?(&:nil?)
50
+
51
+ UnitMeasurements::Time.new((hour || 0), :h) +
52
+ UnitMeasurements::Time.new((minute || 0), :min) +
53
+ UnitMeasurements::Time.new((second || 0), :s) +
54
+ UnitMeasurements::Time.new((microsecond || 0), :μs)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ # Regex pattern for parsing duration in the format of +hour:minute:second+ or
61
+ # +hour:minute:second,microsecond+.
62
+ #
63
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
64
+ # @since 0.2.0
65
+ DURATION = /\A(?<hour>#{REAL_NUMBER}):(?<min>#{REAL_NUMBER}):(?:(?<sec>#{REAL_NUMBER}))?(?:,(?<msec>#{REAL_NUMBER}))?\z/.freeze
66
+ end
67
+ end
@@ -3,5 +3,6 @@
3
3
  # -*- warn_indent: true -*-
4
4
 
5
5
  module CompositeUnitMeasurements
6
- VERSION = "0.1.0"
6
+ # Current stable version
7
+ VERSION = "0.2.0"
7
8
  end
@@ -0,0 +1,106 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ require "unit_measurements/unit_groups/weight"
6
+
7
+ module CompositeUnitMeasurements
8
+ # A parser handling +weight+ measurements, particularly for composite units
9
+ # like +pound-ounce+, +stone-pound+, +kilogramme-gramme+, etc.
10
+ #
11
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
12
+ # @since 0.2.0
13
+ class Weight
14
+ class << self
15
+ # Parses a given +string+ into a +UnitMeasurements::Weight+ object.
16
+ #
17
+ # @param [String] string The string to parse for weight measurement.
18
+ # @return [UnitMeasurements::Weight]
19
+ # Returns a UnitMeasurements::Weight object if parsing is successful.
20
+ # @raise [UnitMeasurements::ParseError]
21
+ # If the string does not match any known format.
22
+ #
23
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
24
+ # @since 0.2.0
25
+ def parse(string)
26
+ case string
27
+ when POUND_OUNCE then parse_pound_ounce(string)
28
+ when STONE_POUND then parse_stone_pound(string)
29
+ else raise UnitMeasurements::ParseError, string
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ # @private
36
+ # Parses a +string+ representing a weight in the format of +pound-ounce+.
37
+ #
38
+ # @param [String] string
39
+ # The string representing weight measurement in the format of *pound-ounce*.
40
+ # @return [UnitMeasurements::Weight]
41
+ # Returns a UnitMeasurements::Weight object if parsing is successful.
42
+ #
43
+ # @see POUND_OUNCE
44
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
45
+ # @since 0.2.0
46
+ def parse_pound_ounce(string)
47
+ pound, ounce = string.match(POUND_OUNCE)&.captures
48
+
49
+ if pound && ounce
50
+ UnitMeasurements::Weight.new(pound, :lb) + UnitMeasurements::Weight.new(ounce, :oz)
51
+ end
52
+ end
53
+
54
+ # @private
55
+ # Parses a +string+ representing a weight in the format of +stone-pound+.
56
+ #
57
+ # @param [String] string
58
+ # The string representing weight measurement in the format of *stone-pound*.
59
+ # @return [UnitMeasurements::Weight]
60
+ # Returns a UnitMeasurements::Weight object if parsing is successful.
61
+ #
62
+ # @see STONE_POUND
63
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
64
+ # @since 0.2.0
65
+ def parse_stone_pound(string)
66
+ stone, pound = string.match(STONE_POUND)&.captures
67
+
68
+ if stone && pound
69
+ UnitMeasurements::Weight.new(stone, :st) + UnitMeasurements::Weight.new(pound, :lb)
70
+ end
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ # Regex pattern for aliases of +pound+ unit.
77
+ #
78
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
79
+ # @since 0.2.0
80
+ POUND_UNITS = /(?:#|lb|lbs|lbm|pound-mass|pound(?:s)?)/.freeze
81
+
82
+ # Regex pattern for aliases of +ounce+ unit.
83
+ #
84
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
85
+ # @since 0.2.0
86
+ OUNCE_UNITS = /(?:oz|ounce(?:s)?)/.freeze
87
+
88
+ # Regex pattern for aliases of +stone+ unit.
89
+ #
90
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
91
+ # @since 0.2.0
92
+ STONE_UNITS = /(?:st|stone(?:s)?)/.freeze
93
+
94
+ # Regex pattern for parsing a weight measurement in the format of +pound-ounce+.
95
+ #
96
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
97
+ # @since 0.2.0
98
+ POUND_OUNCE = /\A#{ANY_NUMBER}\s*#{POUND_UNITS}\s*#{ANY_NUMBER}\s*#{OUNCE_UNITS}\z/.freeze
99
+
100
+ # Regex pattern for parsing a weight measurement in the format of +stone-pound+.
101
+ #
102
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
103
+ # @since 0.2.0
104
+ STONE_POUND = /\A#{ANY_NUMBER}\s*#{STONE_UNITS}\s*#{ANY_NUMBER}\s*#{POUND_UNITS}\z/.freeze
105
+ end
106
+ end
@@ -2,8 +2,8 @@
2
2
  # -*- frozen_string_literal: true -*-
3
3
  # -*- warn_indent: true -*-
4
4
 
5
- require "composite_unit_measurements/version"
5
+ require "composite_unit_measurements/base"
6
6
 
7
- module CompositeUnitMeasurements
8
- class Error < StandardError; end
9
- end
7
+ require "composite_unit_measurements/length"
8
+ require "composite_unit_measurements/time"
9
+ require "composite_unit_measurements/weight"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composite_unit_measurements
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harshal LADHE
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-09 00:00:00.000000000 Z
11
+ date: 2023-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -121,7 +121,10 @@ files:
121
121
  - composite_unit_measurements.gemspec
122
122
  - lib/composite_unit_measurements.rb
123
123
  - lib/composite_unit_measurements/base.rb
124
+ - lib/composite_unit_measurements/length.rb
125
+ - lib/composite_unit_measurements/time.rb
124
126
  - lib/composite_unit_measurements/version.rb
127
+ - lib/composite_unit_measurements/weight.rb
125
128
  homepage: https://github.com/shivam091/composite_unit_measurements
126
129
  licenses:
127
130
  - MIT