unit_measurements_us_complete 5.17.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.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +31 -0
  3. data/.github/workflows/pages.yml +31 -0
  4. data/.gitignore +12 -0
  5. data/.rspec +3 -0
  6. data/.yardopts +2 -0
  7. data/CHANGELOG.md +645 -0
  8. data/Gemfile +7 -0
  9. data/Gemfile.lock +56 -0
  10. data/LICENSE.md +21 -0
  11. data/README.md +511 -0
  12. data/Rakefile +10 -0
  13. data/lib/unit_measurements/arithmetic.rb +225 -0
  14. data/lib/unit_measurements/base.rb +177 -0
  15. data/lib/unit_measurements/cache.rb +173 -0
  16. data/lib/unit_measurements/comparison.rb +64 -0
  17. data/lib/unit_measurements/configuration.rb +64 -0
  18. data/lib/unit_measurements/conversion.rb +92 -0
  19. data/lib/unit_measurements/errors/blank_quantity_error.rb +21 -0
  20. data/lib/unit_measurements/errors/blank_unit_error.rb +21 -0
  21. data/lib/unit_measurements/errors/missing_primitive_unit_error.rb +24 -0
  22. data/lib/unit_measurements/errors/parse_error.rb +37 -0
  23. data/lib/unit_measurements/errors/primitive_unit_already_set_error.rb +22 -0
  24. data/lib/unit_measurements/errors/unit_already_defined_error.rb +37 -0
  25. data/lib/unit_measurements/errors/unit_error.rb +36 -0
  26. data/lib/unit_measurements/extras/conversion_methods.rb +87 -0
  27. data/lib/unit_measurements/extras/numeric_methods.rb +85 -0
  28. data/lib/unit_measurements/formatter.rb +58 -0
  29. data/lib/unit_measurements/math.rb +120 -0
  30. data/lib/unit_measurements/measurement.rb +539 -0
  31. data/lib/unit_measurements/normalizer.rb +155 -0
  32. data/lib/unit_measurements/parser.rb +220 -0
  33. data/lib/unit_measurements/unit.rb +243 -0
  34. data/lib/unit_measurements/unit_group.rb +297 -0
  35. data/lib/unit_measurements/unit_group_builder.rb +224 -0
  36. data/lib/unit_measurements/unit_groups/acceleration.rb +36 -0
  37. data/lib/unit_measurements/unit_groups/all.rb +56 -0
  38. data/lib/unit_measurements/unit_groups/amount_of_substance.rb +13 -0
  39. data/lib/unit_measurements/unit_groups/angular_acceleration.rb +14 -0
  40. data/lib/unit_measurements/unit_groups/angular_velocity.rb +25 -0
  41. data/lib/unit_measurements/unit_groups/area.rb +49 -0
  42. data/lib/unit_measurements/unit_groups/catalytic_activity.rb +13 -0
  43. data/lib/unit_measurements/unit_groups/density.rb +33 -0
  44. data/lib/unit_measurements/unit_groups/dynamic_viscosity.rb +22 -0
  45. data/lib/unit_measurements/unit_groups/electric_charge.rb +20 -0
  46. data/lib/unit_measurements/unit_groups/electric_conductance.rb +15 -0
  47. data/lib/unit_measurements/unit_groups/electric_current.rb +19 -0
  48. data/lib/unit_measurements/unit_groups/electric_dipole_moment.rb +13 -0
  49. data/lib/unit_measurements/unit_groups/electric_potential.rb +17 -0
  50. data/lib/unit_measurements/unit_groups/electric_quadrupole_moment.rb +14 -0
  51. data/lib/unit_measurements/unit_groups/electrical_capacitance.rb +15 -0
  52. data/lib/unit_measurements/unit_groups/electrical_elastance.rb +13 -0
  53. data/lib/unit_measurements/unit_groups/electrical_inductance.rb +15 -0
  54. data/lib/unit_measurements/unit_groups/electrical_resistance.rb +16 -0
  55. data/lib/unit_measurements/unit_groups/energy.rb +58 -0
  56. data/lib/unit_measurements/unit_groups/force.rb +47 -0
  57. data/lib/unit_measurements/unit_groups/frequency.rb +16 -0
  58. data/lib/unit_measurements/unit_groups/illuminance.rb +18 -0
  59. data/lib/unit_measurements/unit_groups/information_entropy.rb +15 -0
  60. data/lib/unit_measurements/unit_groups/kinetic_viscosity.rb +18 -0
  61. data/lib/unit_measurements/unit_groups/length.rb +67 -0
  62. data/lib/unit_measurements/unit_groups/luminance.rb +21 -0
  63. data/lib/unit_measurements/unit_groups/luminous_flux.rb +11 -0
  64. data/lib/unit_measurements/unit_groups/luminous_intensity.rb +13 -0
  65. data/lib/unit_measurements/unit_groups/magnetic_field.rb +13 -0
  66. data/lib/unit_measurements/unit_groups/magnetic_flux.rb +15 -0
  67. data/lib/unit_measurements/unit_groups/magnetic_induction.rb +13 -0
  68. data/lib/unit_measurements/unit_groups/magnetomotive_force.rb +13 -0
  69. data/lib/unit_measurements/unit_groups/mass_flow_rate.rb +49 -0
  70. data/lib/unit_measurements/unit_groups/plane_angle.rb +30 -0
  71. data/lib/unit_measurements/unit_groups/power.rb +54 -0
  72. data/lib/unit_measurements/unit_groups/pressure.rb +60 -0
  73. data/lib/unit_measurements/unit_groups/quantity.rb +14 -0
  74. data/lib/unit_measurements/unit_groups/radiation_absorbed_dose.rb +14 -0
  75. data/lib/unit_measurements/unit_groups/radiation_equivalent_dose.rb +13 -0
  76. data/lib/unit_measurements/unit_groups/radiation_exposure.rb +15 -0
  77. data/lib/unit_measurements/unit_groups/radioactivity.rb +14 -0
  78. data/lib/unit_measurements/unit_groups/solid_angle.rb +18 -0
  79. data/lib/unit_measurements/unit_groups/sound_level.rb +13 -0
  80. data/lib/unit_measurements/unit_groups/temperature.rb +19 -0
  81. data/lib/unit_measurements/unit_groups/time.rb +29 -0
  82. data/lib/unit_measurements/unit_groups/torque.rb +40 -0
  83. data/lib/unit_measurements/unit_groups/velocity.rb +37 -0
  84. data/lib/unit_measurements/unit_groups/volume.rb +67 -0
  85. data/lib/unit_measurements/unit_groups/volumetric_flow_rate.rb +35 -0
  86. data/lib/unit_measurements/unit_groups/weight.rb +55 -0
  87. data/lib/unit_measurements/version.rb +8 -0
  88. data/lib/unit_measurements.rb +7 -0
  89. data/unit_measurements.gemspec +43 -0
  90. data/units.md +843 -0
  91. metadata +216 -0
@@ -0,0 +1,225 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module UnitMeasurements
6
+ # The +UnitMeasurements::Arithmetic+ mixin module provides methods for
7
+ # performing arithmetic operations (addition, subtraction, multiplication,
8
+ # division, etc) on measurements of the same unit group. In case the
9
+ # measurements represents different units, the left hand side takes precedence
10
+ # while performing the arithmetic operation on them.
11
+ #
12
+ # This module is included in the +Measurement+ class to allow arithmetic
13
+ # operations on the measurements.
14
+ #
15
+ # @see Measurement
16
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
17
+ # @since 1.4.0
18
+ module Arithmetic
19
+ extend Forwardable
20
+
21
+ # Methods delegated from the Numeric.
22
+ def_delegators :@quantity, :zero?, :positive?, :negative?, :finite?, :infinite?
23
+
24
+ # Adds the quantity of the other measurement or a numeric value to the
25
+ # quantity of the current measurement.
26
+ #
27
+ # @param [Numeric|Measurement] other
28
+ # The value to be added. It can be a numeric value or another measurement.
29
+ #
30
+ # @example
31
+ # UnitMeasurements::Length.new(1, "km") + UnitMeasurements::Length.new(1, "m")
32
+ # => 1.001 km
33
+ #
34
+ # UnitMeasurements::Length.new(1, "km") + 4.5
35
+ # => 5.5 km
36
+ #
37
+ # @return [Measurement] A new +Measurement+ instance with the combined quantity.
38
+ #
39
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
40
+ # @since 1.4.0
41
+ def +(other)
42
+ arithmetic_operation(other, :+)
43
+ end
44
+ alias_method :add, :+
45
+
46
+ # Subtracts the quantity of the other measurement or a numeric value from the
47
+ # quantity of the current measurement.
48
+ #
49
+ # @param [Numeric|Measurement] other
50
+ # The value to be subtracted. It can be a numeric value or another measurement.
51
+ #
52
+ # @example
53
+ # UnitMeasurements::Length.new(1, "km") - UnitMeasurements::Length.new(2, "in")
54
+ # => 0.9999492 km
55
+ #
56
+ # UnitMeasurements::Length.new(2, "km") - 1e+2
57
+ # => -98.0 km
58
+ #
59
+ # @return [Measurement] A new +Measurement+ instance with the subtracted quantity.
60
+ #
61
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
62
+ # @since 1.4.0
63
+ def -(other)
64
+ arithmetic_operation(other, :-)
65
+ end
66
+ alias_method :subtract, :-
67
+
68
+ # Multiplies the quantity of the current measurement by the quantity of the
69
+ # other measurement or a numeric value.
70
+ #
71
+ # @param [Numeric|Measurement] other
72
+ # The value to be multiplied. It can be a numeric value or another measurement.
73
+ #
74
+ # @example
75
+ # UnitMeasurements::Length.new(2, "km") * UnitMeasurements::Length.new(3, "in")
76
+ # => 0.0001524 km
77
+ #
78
+ # UnitMeasurements::Length.new(2, "km") * 2+2i
79
+ # => 4+2i km
80
+ #
81
+ # @return [Measurement] A new +Measurement+ instance with the multiplied quantity.
82
+ #
83
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
84
+ # @since 1.4.0
85
+ def *(other)
86
+ arithmetic_operation(other, :*)
87
+ end
88
+ alias_method :scale, :*
89
+ alias_method :times, :*
90
+ alias_method :multiply, :*
91
+
92
+ # Divides the quantity of the current measurement by the quantity of the other
93
+ # measurement or a numeric value.
94
+ #
95
+ # @param [Numeric|Measurement] other
96
+ # The value to be divided. It can be a numeric value or another measurement.
97
+ #
98
+ # @example
99
+ # UnitMeasurements::Length.new(4, "km") / UnitMeasurements::Length.new(2, "km")
100
+ # => 2 km
101
+ #
102
+ # UnitMeasurements::Length.new(2, "km") / 2
103
+ # => 1 km
104
+ #
105
+ # @return [Measurement] A new +Measurement+ instance with the divided quantity.
106
+ #
107
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
108
+ # @since 1.4.0
109
+ def /(other)
110
+ arithmetic_operation(other, :/)
111
+ end
112
+ alias_method :divide, :/
113
+
114
+ # Raises the quantity of the current measurement to the power of the quantity of
115
+ # the other measurement or numeric value.
116
+ #
117
+ # When +other+ is an instance of +Measurement+, the quantity to raise
118
+ # is calculated by converting the +other+ measurement to the unit of the +current+
119
+ # measurement, and then the quantity of the +current+ measurement is raised to
120
+ # the converted quantity.
121
+ #
122
+ # @param [Numeric|Measurement] other
123
+ # The value to be raised. It can be a numeric value or another measurement.
124
+ #
125
+ # @example
126
+ # UnitMeasurements::Length.new(2, "km") ** UnitMeasurements::Length.new(3, "m")
127
+ # => 1.00208160507963279 km
128
+ #
129
+ # UnitMeasurements::Length.new(2, "km") ** 3
130
+ # => 8 km
131
+ #
132
+ # UnitMeasurements::Length.new(8, "km") ** 1/3r
133
+ # => 2 km
134
+ #
135
+ # @return [Measurement] A new +Measurement+ instance with the raised quantity.
136
+ #
137
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
138
+ # @since 5.1.0
139
+ def **(other)
140
+ arithmetic_operation(other, :**)
141
+ end
142
+ alias_method :pow, :**
143
+ alias_method :^, :**
144
+
145
+ # Negates the quantity of the measurement.
146
+ #
147
+ # @example
148
+ # -UnitMeasurements::Length.new(2, "km")
149
+ # => -2 km
150
+ #
151
+ # -UnitMeasurements::Length.new(-2, "km")
152
+ # => 2 km
153
+ #
154
+ # @return [Measurement] A new +Measurement+ instance with the negated quantity.
155
+ #
156
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
157
+ # @since 5.1.0
158
+ def -@
159
+ self.class.new(-self.quantity, self.unit)
160
+ end
161
+ alias_method :inverse, :-@
162
+ alias_method :negate, :-@
163
+
164
+ # Checks whether the quantity of the measurement is nonzero.
165
+ #
166
+ # @example
167
+ # UnitMeasurements::Length.new(2, "km").nonzero?
168
+ # => true
169
+ #
170
+ # UnitMeasurements::Length.new(0, "km").nonzero?
171
+ # => false
172
+ #
173
+ # @return [TrueClass|FalseClass]
174
+ # +true+ if the quantity is nonzero otherwise it returns +false+.
175
+ #
176
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
177
+ # @since 5.1.0
178
+ def nonzero?
179
+ quantity.nonzero? ? true : false
180
+ end
181
+
182
+ private
183
+
184
+ # @private
185
+ # Coerces a numeric value or another measurement for arithmetic operations.
186
+ #
187
+ # @param [Numeric|Measurement] other
188
+ # The value to be coerced. It can be a numeric value or another measurement.
189
+ #
190
+ # @return [Array<Measurement>] An array containing the coerced values.
191
+ #
192
+ # @raise [TypeError]
193
+ # If the coercion is not possible due to incompatible types.
194
+ #
195
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
196
+ # @since 1.4.0
197
+ def coerce(other)
198
+ case other
199
+ when Numeric then [self.class.new(other, self.unit), self]
200
+ when self.class then [other, self]
201
+ else raise TypeError, "Cannot coerce #{other.class} to #{self.class}"
202
+ end
203
+ end
204
+
205
+ # @private
206
+ # Performs an arithmetic operation (addition, subtraction, multiplication,
207
+ # or division) on the current measurement and another numeric value.
208
+ #
209
+ # @param [Numeric|Measurement] other
210
+ # The value to be used in the arithmetic operation. It can be a numeric value
211
+ # or another measurement.
212
+ # @param [Symbol] operator The operator to be used for the operation.
213
+ #
214
+ # @return [Measurement]
215
+ # A new +Measurement+ instance with the result of the arithmetic operation.
216
+ #
217
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
218
+ # @since 1.4.0
219
+ def arithmetic_operation(other, operator)
220
+ other, _ = coerce(other)
221
+
222
+ self.class.new(self.quantity.public_send(operator, other.convert_to(self.unit).quantity), self.unit)
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,177 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ require "active_support/all"
6
+ require "unit_measurements/version"
7
+
8
+ # The +UnitMeasurements+ module serves as a container for various functionalities
9
+ # related to unit measurements. It provides methods for creating custom unit
10
+ # groups, defining units, performing arithmetic operations, comparison between
11
+ # measurements, conversions, normalization of input strings, parsing measurements
12
+ # from strings, and more. It is a fundamental part of the unit measurements library.
13
+ #
14
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
15
+ # @since 0.1.0
16
+ module UnitMeasurements
17
+ # This is the base class for custom errors in the +UnitMeasurements+ module.
18
+ #
19
+ # @see UnitError
20
+ # @see ParseError
21
+ # @see BlankUnitError
22
+ # @see BlankQuantityError
23
+ # @see UnitAlreadyDefinedError
24
+ # @see MissingPrimitiveUnitError
25
+ # @see PrimitiveUnitAlreadySetError
26
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
27
+ # @since 1.1.0
28
+ class BaseError < StandardError; end
29
+
30
+ class << self
31
+ # Allows setting an instance of +Configuration+ containing values of desired
32
+ # configurable options.
33
+ #
34
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
35
+ # @since 5.3.0
36
+ attr_writer :configuration
37
+
38
+ # Creates a new unit group based on the provided +block+ of instructions.
39
+ #
40
+ # The +build+ method allows you to define and create a custom unit group with
41
+ # units and their conversions. It takes a block of instructions as an argument,
42
+ # which is evaluated by an instance of +UnitGroupBuilder+.
43
+ #
44
+ # Within the +block+, you can use various methods provided by +UnitGroupBuilder+
45
+ # to define units, group them into unit system, and set primitive unit of
46
+ # the unit group. These methods include +primitive+, +system+, +si_unit+,
47
+ # and +unit+.
48
+ #
49
+ # The resulting unit group is encapsulated in a new subclass of +Measurement+.
50
+ # This subclass will have access to the defined units and their conversions
51
+ # through the +unit_group+ class attribute.
52
+ #
53
+ # This method provides a powerful way to create specialized unit groups tailored
54
+ # to specific measurement domains.
55
+ #
56
+ # @example
57
+ # UnitMeasurements.build do
58
+ # primitive "m"
59
+ #
60
+ # system :metric do
61
+ # si_unit "m", aliases: ["meter", "metre", "meters", "metres"]
62
+ # end
63
+ #
64
+ # system :imperial do
65
+ # unit "in", value: "25.4 mm", aliases: ['"', "inch", "inches"]
66
+ # end
67
+ #
68
+ # cache "length.json"
69
+ # end
70
+ #
71
+ # @yield [builder]
72
+ # A block that defines the units to be added to the unit group.
73
+ # The block takes a {UnitGroupBuilder} instance as a parameter.
74
+ #
75
+ # @yieldparam builder [UnitGroupBuilder]
76
+ # The {UnitGroupBuilder} instance to define units within the unit group.
77
+ #
78
+ # @yieldreturn [UnitGroup]
79
+ # Returns an instance of {UnitGroup} containing the units and their conversions
80
+ # defined within the block.
81
+ #
82
+ # @return [Class]
83
+ # A new subclass of +Measurement+ with the defined units and conversions.
84
+ #
85
+ # @see Unit
86
+ # @see UnitGroup
87
+ # @see UnitGroupBuilder
88
+ # @see Measurement
89
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
90
+ # @since 1.0.0
91
+ def build(&block)
92
+ builder = UnitGroupBuilder.new
93
+ builder.instance_eval(&block)
94
+
95
+ Class.new(Measurement) do
96
+ class << self
97
+ attr_reader :unit_group
98
+ end
99
+
100
+ @unit_group = builder.build
101
+ end
102
+ end
103
+
104
+ # Returns an instance of +Configuration+ with the values of desired configurable
105
+ # options of +*unit_measurements*+. If instance is not present, it initializes
106
+ # a new instance of {Configuration}.
107
+ #
108
+ # @return [Configuration] An instance of +Configuration+.
109
+ #
110
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
111
+ # @since 5.3.0
112
+ def configuration
113
+ @configuration ||= Configuration.new
114
+ end
115
+
116
+ # Reset the configuration to its default state.
117
+ #
118
+ # @example
119
+ # UnitMeasurements.reset
120
+ #
121
+ # @return [Configuration] A new +Configuration+ object.
122
+ #
123
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
124
+ # @since 5.3.0
125
+ def reset
126
+ @configuration = Configuration.new
127
+ end
128
+
129
+ # Configures options of the +*UnitMeasurements*+ module using a block. It
130
+ # yields the current +Configuration+ instance for updating default values of
131
+ # options by new values specified within a block.
132
+ #
133
+ # @example
134
+ # UnitMeasurements.configure do |config|
135
+ # config.use_cache = false
136
+ # end
137
+ #
138
+ # @yield [configuration] The current +Configuration+ instance.
139
+ #
140
+ # @yieldparam [Configuration] configuration
141
+ # An instance of +Configuration+ with the new values of options.
142
+ #
143
+ # @yieldreturn [Configuration] The updated +Configuration+ instance.
144
+ #
145
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
146
+ # @since 5.3.0
147
+ def configure
148
+ yield configuration
149
+ end
150
+ end
151
+ end
152
+
153
+ # The following requires load various components of the unit measurements library.
154
+ require "unit_measurements/extras/numeric_methods"
155
+ require "unit_measurements/extras/conversion_methods"
156
+
157
+ require "unit_measurements/configuration"
158
+ require "unit_measurements/cache"
159
+ require "unit_measurements/unit_group_builder"
160
+ require "unit_measurements/unit"
161
+ require "unit_measurements/unit_group"
162
+ require "unit_measurements/arithmetic"
163
+ require "unit_measurements/comparison"
164
+ require "unit_measurements/conversion"
165
+ require "unit_measurements/math"
166
+ require "unit_measurements/normalizer"
167
+ require "unit_measurements/parser"
168
+ require "unit_measurements/formatter"
169
+ require "unit_measurements/measurement"
170
+
171
+ require "unit_measurements/errors/unit_error"
172
+ require "unit_measurements/errors/parse_error"
173
+ require "unit_measurements/errors/unit_already_defined_error"
174
+ require "unit_measurements/errors/primitive_unit_already_set_error"
175
+ require "unit_measurements/errors/blank_quantity_error"
176
+ require "unit_measurements/errors/blank_unit_error"
177
+ require "unit_measurements/errors/missing_primitive_unit_error"
@@ -0,0 +1,173 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module UnitMeasurements
6
+ # The +UnitMeasurements::Cache+ class manages caching of conversion factors
7
+ # between different units within a unit group. It provides methods to retrieve,
8
+ # set, and clear cached conversion factors.
9
+ #
10
+ # Cached conversion factors are stored in JSON file on the file system.
11
+ #
12
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
13
+ # @since 5.2.0
14
+ class Cache
15
+ # The directory path where cache files are stored.
16
+ #
17
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
18
+ # @since 5.2.0
19
+ CACHE_DIRECTORY = File.expand_path(File.join("..", "..", "cache"), __dir__).freeze
20
+
21
+ # Stores cached conversion factors between different units within a unit
22
+ # group.
23
+ #
24
+ # @return [Hash] The cached conversion factors.
25
+ #
26
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
27
+ # @since 5.2.0
28
+ attr_reader :cached_data
29
+
30
+ # Initializes a new +Cache+ instance for a specific unit group.
31
+ #
32
+ # Initialization first ensures existence of the cache directory. If the cache
33
+ # directory does not exist, it gets created.
34
+ #
35
+ # @param [UnitGroup] unit_group The unit group associated with the cache.
36
+ #
37
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
38
+ # @since 5.2.0
39
+ def initialize(unit_group)
40
+ ensure_cache_directory_exists
41
+ @cache_file = build_cache_file_path(unit_group)
42
+ @cached_data ||= load_cache
43
+ end
44
+
45
+ # Retrieves the conversion factor between source and target units from the
46
+ # cache.
47
+ #
48
+ # @param [String] source_unit The source unit name.
49
+ # @param [String] target_unit The target unit name.
50
+ #
51
+ # @return [Numeric|NilClass]
52
+ # The conversion factor, or +nil+ if not found in the cache.
53
+ #
54
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
55
+ # @since 5.2.0
56
+ def get(source_unit, target_unit)
57
+ cached_data.dig(source_unit, target_unit)
58
+ end
59
+
60
+ # Sets the conversion factor between source and target units in the cache.
61
+ #
62
+ # @param [String] source_unit The source unit name.
63
+ # @param [String] target_unit The target unit name.
64
+ # @param [Numeric] conversion_factor The conversion factor.
65
+ #
66
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
67
+ # @since 5.2.0
68
+ def set(source_unit, target_unit, conversion_factor)
69
+ cached_data[source_unit] ||= {}
70
+ cached_data[source_unit][target_unit] = conversion_factor
71
+
72
+ store_cache
73
+ end
74
+
75
+ # Clears the entire cache.
76
+ #
77
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
78
+ # @since 5.2.0
79
+ def clear_cache
80
+ @cached_data = {}
81
+ store_cache
82
+ end
83
+
84
+ private
85
+
86
+ # @private
87
+ # Loads the cache from the cache file.
88
+ #
89
+ # @return [Hash] The loaded cache data.
90
+ #
91
+ # @raise [Errno::ENOENT] If the cache file does not exist.
92
+ # @raise [Errno::EACCES]
93
+ # If the cache file cannot be accessed due to insufficient permissions.
94
+ # @raise [JSON::ParserError] If there's an error parsing the cache file.
95
+ #
96
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
97
+ # @since 5.2.0
98
+ def load_cache
99
+ return {} unless File.exist?(@cache_file)
100
+
101
+ begin
102
+ File.open(@cache_file, "r") { |file| JSON.load(file.read) }
103
+ rescue Errno::ENOENT, Errno::EACCES, JSON::ParserError => e
104
+ puts "Error loading cache"
105
+ {}
106
+ end
107
+ end
108
+
109
+ # @private
110
+ # Stores the current cache data to the cache file. +cached_data+ is stored in
111
+ # prettier form.
112
+ #
113
+ # @raise [Errno::ENOENT] If the cache file does not exist.
114
+ # @raise [Errno::EACCES]
115
+ # If the cache file cannot be accessed due to insufficient permissions.
116
+ # @raise [Errno::ENOSPC]
117
+ # If there's not enough space to write to the cache file.
118
+ # @raise [JSON::GeneratorError] If there's an error generating JSON data.
119
+ #
120
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
121
+ # @since 5.2.0
122
+ def store_cache
123
+ begin
124
+ File.open(@cache_file, "w") do |file|
125
+ file.write(JSON.pretty_generate(cached_data))
126
+ end
127
+ rescue Errno::ENOENT, Errno::EACCES, Errno::ENOSPC, JSON::GeneratorError => e
128
+ puts "Error saving cache: #{e.message}"
129
+ end
130
+ end
131
+
132
+ # @private
133
+ # Ensures that the cache directory exists. If the cache directory does not
134
+ # exist, it gets created.
135
+ #
136
+ # @raise [Errno::EEXIST] If the cache directory already exists.
137
+ # @raise [Errno::EACCES]
138
+ # If the cache directory cannot be accessed due to insufficient permissions.
139
+ # @raise [Errno::ENOSPC]
140
+ # If there's not enough space to create the cache directory.
141
+ #
142
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
143
+ # @since 5.2.0
144
+ def ensure_cache_directory_exists
145
+ begin
146
+ Dir.mkdir(CACHE_DIRECTORY) unless Dir.exist?(CACHE_DIRECTORY)
147
+ rescue Errno::EACCES, Errno::ENOSPC => e
148
+ puts "Error creating cache directory: #{e.message}"
149
+ end
150
+ end
151
+
152
+ # @private
153
+ # Builds and returns an absolute path of the cache file.
154
+ #
155
+ # This method first checks if the cache file name is specified for the unit
156
+ # group. If yes, it builds absolute path of the cache file using specified
157
+ # cache file name. If not, it builds file name from of the name of the unit
158
+ # group. This file name is then used to build absolute path of the cache file.
159
+ #
160
+ # @param [UnitGroup] unit_group The unit group associated with the cache.
161
+ #
162
+ # @return [String] An absolute path of the cache file.
163
+ #
164
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
165
+ # @since 5.2.0
166
+ def build_cache_file_path(unit_group)
167
+ cache_file_name = unit_group.cache_file || unit_group.to_s.split("::").last.underscore
168
+ cache_file_name = File.basename(cache_file_name, ".json") + ".json"
169
+
170
+ Pathname.new(File.join(CACHE_DIRECTORY, cache_file_name)).cleanpath
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,64 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module UnitMeasurements
6
+ # The +UnitMeasurements::Comparison+ mixin module is included in measurement
7
+ # classes to enable comparison operations (e.g., less than, equal to, greater
8
+ # than, etc.) between two measurements of the same unit group.
9
+ #
10
+ # This module is included in the +Measurement+ class to allow comparison of two
11
+ # measurements.
12
+ #
13
+ # @see Measurement
14
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
15
+ # @since 1.3.0
16
+ module Comparison
17
+ include Comparable
18
+
19
+ # This method is used to compare the quantity of two measurements. It
20
+ # implements the comparison logic based on the +<=>+ method defined in the
21
+ # +Comparable+ module.
22
+ #
23
+ # @example
24
+ # UnitMeasurements::Length.new(1, "km") == UnitMeasurements::Length.new(1, :km)
25
+ # => true
26
+ #
27
+ # UnitMeasurements::Length.parse("1 km") == UnitMeasurements::Length.parse("1000 m")
28
+ # => true
29
+ #
30
+ # UnitMeasurements::Length.parse("1 km") != UnitMeasurements::Length.parse("1 m")
31
+ # => true
32
+ #
33
+ # UnitMeasurements::Length.parse("1 km") < UnitMeasurements::Length.parse("0.5 km")
34
+ # => false
35
+ #
36
+ # UnitMeasurements::Length.parse("1 km") > UnitMeasurements::Length.parse("0.5 km")
37
+ # => true
38
+ #
39
+ # UnitMeasurements::Length.parse("1 km") <= UnitMeasurements::Length.parse("0.5 km")
40
+ # => false
41
+ #
42
+ # UnitMeasurements::Length.parse("1 km") >= UnitMeasurements::Length.parse("0.5 km")
43
+ # => true
44
+ #
45
+ # UnitMeasurements::Length.new(1, "ft").between?(UnitMeasurements::Length.new(12, "in"), UnitMeasurements::Length.new(24, "in"))
46
+ # => true
47
+ #
48
+ # @param [Measurement] object The measurement instance to compare with.
49
+ #
50
+ # @return
51
+ # +nil+ if the comparison is not possible (e.g., if the +object+ is not of
52
+ # the same unit group). A negative integer if +self+ is less than +object+.
53
+ # +Zero+ if self is equal to +object+. A positive integer if +self+ is
54
+ # greater than +object+.
55
+ #
56
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
57
+ # @since 1.3.0
58
+ def <=>(object)
59
+ return nil unless object.is_a?(self.class)
60
+
61
+ quantity <=> object.convert_to(unit.name).quantity
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,64 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_stringing_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module UnitMeasurements
6
+ # The +UnitMeasurements::Configuration+ class maintains and manages the globally
7
+ # configurable options of +*unit_measurements*+.
8
+ #
9
+ # @note
10
+ # This class is responsible for configuring globally configurable options of
11
+ # +*unit_measurements*+.
12
+ #
13
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
14
+ # @since 5.3.0
15
+ class Configuration
16
+ # Get the current value of the +use_cache+ option.
17
+ #
18
+ # @note
19
+ # This option controls whether caching is enabled for converting measurements.
20
+ # Defaults to +false+.
21
+ #
22
+ # @return [TrueClass|FalseClass]
23
+ # Returns +true+ if caching is enabled, otherwise +false+.
24
+ #
25
+ # @see Cache
26
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
27
+ # @since 5.3.0
28
+ attr_reader :use_cache
29
+
30
+ # Initializes a new +Configuration+ instance with default values of configurable
31
+ # options.
32
+ #
33
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
34
+ # @since 5.3.0
35
+ def initialize
36
+ self.use_cache = false
37
+ end
38
+
39
+ # Sets a value for the +use_cache+ option.
40
+ #
41
+ # It controls whether caching is enabled for converting measurements. When
42
+ # caching is enabled, previously computed conversion factors are stored for
43
+ # future use, improving conversion performance.
44
+ #
45
+ # @param [TrueClass|FalseClass] use_cache
46
+ # +true+ if caching should be used while converting the measurement otherwise
47
+ # +false+.
48
+ #
49
+ # @return [TrueClass|FalseClass] The updated value of +use_cache+.
50
+ #
51
+ # @raise [BaseError] if +use_cache+ is not a boolean value.
52
+ #
53
+ # @see Cache
54
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
55
+ # @since 5.3.0
56
+ def use_cache=(use_cache)
57
+ unless [true, false].include?(use_cache)
58
+ raise ArgumentError, "Configuration#use_cache= only accepts true or false, but received #{use_cache}"
59
+ end
60
+
61
+ @use_cache = use_cache
62
+ end
63
+ end
64
+ end