unit_measurements 5.1.1 → 5.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a5ba942c1c99f76efc0db51ce916d31d199646d5bd2a5ace71b34fadbadacb2
4
- data.tar.gz: 681c3ce17cebe41d715c78b25669afe6347d726405b4fcea74ffd8da3d5e9dc4
3
+ metadata.gz: 006027cb5f557c69f5ba083f6624cf9e424fcb00cc794b8f50912907538a3689
4
+ data.tar.gz: 4db6ec5c12a752191933c5aa6d99837c1e2dc5e8269e5d05339a6e9b631d3e7f
5
5
  SHA512:
6
- metadata.gz: 518cdb270f00447a0a7817e4db3bf8dbd83e6ed1ccdb82e58ef60579d774eff9f40544669b216ab42ee7133e23cf69f1ce149c7d051e8ffc3e147cc356361392
7
- data.tar.gz: 239e100a9b733cbe1792ffc54c812a5fce1abecc2e449bdd03ee6215b84e72ad98eaaa1abec9d9e389deee515fef04cdef022e7c8fe6ab765c8b436eb4f0b0b2
6
+ metadata.gz: fe13840ae30879e2068b4a0628e073259d8faab8efc615f0ff6da1b4fc7307b2a4285ebe44425fc4bedf26263e02a5f29e34df6ac792e3c16429f214262dbc31
7
+ data.tar.gz: d9f28aecb8b8dea511466a8e4f71288581c35407d841b20d65006a9bb8e3dad8d3ec53da4b2f83c0235d9a919a2dccf572371957c1010a8d449c7b11bdf1947f
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /cache/*.json
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [5.3.0](https://github.com/shivam091/unit_measurements/compare/v5.2.0...v5.3.0) - 2023-10-24
2
+
3
+ ### What's new
4
+
5
+ - Added ability set globally configurable options for **`unit_measurements`**.
6
+
7
+ ### What's improved
8
+
9
+ - Code coverage improvements.
10
+
11
+ ----------
12
+
13
+ ## [5.2.0](https://github.com/shivam091/unit_measurements/compare/v5.1.1...v5.2.0) - 2023-10-22
14
+
15
+ ### What's new
16
+
17
+ - Added ability to set name of the cache file for the unit group.
18
+ - Added support for caching conversion factors between units of the unit group.
19
+
20
+ ----------
21
+
1
22
  ## [5.1.1](https://github.com/shivam091/unit_measurements/compare/v5.1.0...v5.1.1) - 2023-10-20
2
23
 
3
24
  ### What's updated
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- unit_measurements (5.1.1)
4
+ unit_measurements (5.3.0)
5
5
  activesupport (~> 7.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -22,13 +22,16 @@ to numerous errors.
22
22
 
23
23
  The `unit_measurements` gem is designed to simplify the handling of units for scientific calculations.
24
24
 
25
- ## Features
26
-
27
- 1. Provides easy conversion between units.
28
- 2. Lightweight and easily extensible to include other units and conversions.
29
- 3. Built in support for various [unit groups](https://github.com/shivam091/unit_measurements/blob/main/units.md).
30
- 4. Well organized and very descriptive documentation published [here](https://shivam091.github.io/unit_measurements).
31
- 5. Parses strings representing complex, fractional, mixed fractional, scientific numbers, and ratios.
25
+ ## Key Features
26
+ 1. **Simplified Measurement Conversion:** Easily convert measurements between compatible units, reducing the likelihood of errors in scientific calculations.
27
+ 2. **Extensible Unit Groups:** Effortlessly build own unit groups with specific units and conversions tailored to your needs.
28
+ 3. **Built-in Unit Groups:** Comes bundled with a wide range of standard [unit groups](https://github.com/shivam091/unit_measurements/blob/main/units.md),
29
+ covering various units.
30
+ 4. **String Parsing Capabilities:** Effortlessly parse strings representing complex, fractional, mixed fractional, scientific numbers, and ratios directly
31
+ saving you the hassle of manually extracting and converting them.
32
+ 5. **Comprehensive Documentation:** Well-organized and descriptive [documentation](https://shivam091.github.io/unit_measurements) for quick reference and implementation guidance.
33
+ 6. **Configurable Options:** Fine-tune behavior with configurable options, including caching for enhanced performance.
34
+ 7. **Error Handling:** Robust error handling ensures stability and reliability during conversions.
32
35
 
33
36
  ## Disclaimer
34
37
 
@@ -56,6 +59,23 @@ Or otherwise simply install it yourself as:
56
59
 
57
60
  `$ gem install unit_measurements`
58
61
 
62
+ ## Configuration
63
+
64
+ `unit_measurements` is designed to work out of the box, but you can customize its behavior by placing
65
+ the configuration block in an initializer file before requiring the library files:
66
+
67
+ ```ruby
68
+ UnitMeasurements.configure do |config|
69
+ config.use_cache = false
70
+ end
71
+ ```
72
+
73
+ The current available configurable options are:
74
+
75
+ | Option | Default value | Description |
76
+ | ------ | ------------- | ----------- |
77
+ | **use_cache** | false | Set to `true` to enable caching during conversions. |
78
+
59
79
  ## Usage
60
80
 
61
81
  The **`UnitMeasurements::Measurement`** class is responsible for conversion of quantity to various compatible units
@@ -74,6 +94,8 @@ UnitMeasurements::Length.new(1, "km")
74
94
  This gem allows you to convert among units of same unit group. You can convert measurement to other unit using `#convert_to`
75
95
  (aliased as `#to`, `#in`, and `#as`) or `#convert_to!` (aliased as `#to!`, `#in!`, and `#as!`) methods.
76
96
 
97
+ These methods provide `use_cache` parameter which can be used to indicate whether the caching of conversion factors should happen.
98
+
77
99
  You can use `#convert_to` as:
78
100
 
79
101
  ```ruby
@@ -104,6 +126,8 @@ UnitMeasurements::Length.new(100, "m").convert_to("ft").convert_to!("in")
104
126
 
105
127
  **Parse string without having to split out the quantity and source unit:**
106
128
 
129
+ This method provides `use_cache` parameter which can be used to indicate whether the caching of conversion factors should happen.
130
+
107
131
  ```ruby
108
132
  UnitMeasurements::Length.parse("1 km")
109
133
  #=> 1.0 km
@@ -254,6 +278,12 @@ UnitMeasurements::Length.unit_or_alias?("metre")
254
278
  #=> true
255
279
  ```
256
280
 
281
+ **Clear cached data for the unit group:**
282
+
283
+ ```ruby
284
+ UnitMeasurements::Length.clear_cache
285
+ ```
286
+
257
287
  ### Comparisons
258
288
 
259
289
  You have ability to compare the measurements with the same or different units within the same unit group.
@@ -389,10 +419,9 @@ gem "unit_measurements", require: ["unit_measurements/base", "unit_measurements/
389
419
 
390
420
  ### Building new unit groups
391
421
 
392
- This library provides a simpler way to define your own unit groups. Use the
393
- `UnitMeasurements.build` method to define units within it. You can also group
394
- units by the unit system using the `system` method and set the primitive unit for
395
- each unit group using the `primitive` method.
422
+ This library provides a simpler way to define your own unit groups. Use the `UnitMeasurements.build` method to define
423
+ units within it. You can group units by the unit system using the `system` method and set the primitive unit for the
424
+ unit group using the `primitive` method. You can specify cache file name in unit group definition using the `cache` method.
396
425
 
397
426
  ```ruby
398
427
  UnitMeasurements::Time = UnitMeasurements.build do
@@ -413,6 +442,9 @@ UnitMeasurements::Time = UnitMeasurements.build do
413
442
  # You can also specify unit value as an array.
414
443
  unit "h", value: [60, "min"], aliases: ["day", "days"]
415
444
  end
445
+
446
+ # Sets the name of the cache file (optional).
447
+ cache "time_cache.json"
416
448
  end
417
449
  ```
418
450
 
@@ -7,6 +7,13 @@ require "unit_measurements/version"
7
7
 
8
8
  module UnitMeasurements
9
9
  class << self
10
+ # Allows setting an instance of +Configuration+ containing values of desired
11
+ # configurable options.
12
+ #
13
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
14
+ # @since 5.3.0
15
+ attr_writer :configuration
16
+
10
17
  # Creates a new unit group based on the provided +block+ of instructions.
11
18
  #
12
19
  # The +build+ method allows you to define and create a custom unit group with
@@ -36,11 +43,9 @@ module UnitMeasurements
36
43
  # system :imperial do
37
44
  # unit "in", value: "25.4 mm", aliases: ['"', "inch", "inches"]
38
45
  # end
39
- # end
40
46
  #
41
- # @param block
42
- # A block of instructions for defining units and their conversions within
43
- # the unit group.
47
+ # cache "length.json"
48
+ # end
44
49
  #
45
50
  # @yield [builder]
46
51
  # A block that defines the units to be added to the unit group.
@@ -74,10 +79,59 @@ module UnitMeasurements
74
79
  @unit_group = builder.build
75
80
  end
76
81
  end
82
+
83
+ # Returns an instance of +Configuration+ with the values of desired configurable
84
+ # options of +*unit_measurements*+. If instance is not present, it initializes
85
+ # a new instance of {Configuration}.
86
+ #
87
+ # @return [Configuration] An instance of +Configuration+.
88
+ #
89
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
90
+ # @since 5.3.0
91
+ def configuration
92
+ @configuration ||= Configuration.new
93
+ end
94
+
95
+ # Reset the configuration to its default state.
96
+ #
97
+ # @example
98
+ # UnitMeasurements.reset
99
+ #
100
+ # @return [Configuration] A new +Configuration+ object.
101
+ #
102
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
103
+ # @since 5.3.0
104
+ def reset
105
+ @configuration = Configuration.new
106
+ end
107
+
108
+ # Configures options of the +*UnitMeasurements*+ module using a block. It
109
+ # yields the current +Configuration+ instance for updating default values of
110
+ # options by new values specified within a block.
111
+ #
112
+ # @example
113
+ # UnitMeasurements.configure do |config|
114
+ # config.use_cache = false
115
+ # end
116
+ #
117
+ # @yield [configuration] The current +Configuration+ instance.
118
+ #
119
+ # @yieldparam [Configuration] configuration
120
+ # An instance of +Configuration+ with the new values of options.
121
+ #
122
+ # @yieldreturn [Configuration] The updated +Configuration+ instance.
123
+ #
124
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
125
+ # @since 5.3.0
126
+ def configure
127
+ yield configuration
128
+ end
77
129
  end
78
130
  end
79
131
 
80
132
  # The following requires load various components of the unit measurements library.
133
+ require "unit_measurements/configuration"
134
+ require "unit_measurements/cache"
81
135
  require "unit_measurements/unit_group_builder"
82
136
  require "unit_measurements/unit"
83
137
  require "unit_measurements/unit_group"
@@ -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_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 BaseError, "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
@@ -120,6 +120,11 @@ module UnitMeasurements
120
120
  # Converts the measurement to a +target_unit+ and returns new instance of the
121
121
  # measurement.
122
122
  #
123
+ # When +use_cache+ value is true, conversion factor between units are checked
124
+ # in cache file of the unit group. If cached conversion factor is present in
125
+ # the cache file, it is used for conversion otherwise conversion factor is
126
+ # stored in the cache before converting the measurement to the +target_unit+.
127
+ #
123
128
  # @example
124
129
  # UnitMeasurements::Length.new(1, "m").convert_to("cm")
125
130
  # => 100.0 cm
@@ -127,9 +132,14 @@ module UnitMeasurements
127
132
  # UnitMeasurements::Length.new(1, "cm").convert_to("primitive")
128
133
  # => 0.01 m
129
134
  #
135
+ # UnitMeasurements::Length.new(1, "m").convert_to("cm", use_cache: true)
136
+ # => 100.0 cm
137
+ #
130
138
  # @param [String|Symbol] target_unit
131
139
  # The target unit for conversion. Specifing +primitive+ will convert the
132
140
  # measurement to a primitive unit of the unit group.
141
+ # @param [TrueClass|FalseClass] use_cache
142
+ # Indicates whether to use cached conversion factors.
133
143
  #
134
144
  # @return [Measurement]
135
145
  # A new +Measurement+ instance with the converted +quantity+ and
@@ -137,16 +147,15 @@ module UnitMeasurements
137
147
  #
138
148
  # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
139
149
  # @since 1.0.0
140
- def convert_to(target_unit)
150
+ def convert_to(target_unit, use_cache: false)
141
151
  target_unit = if target_unit.to_s.eql?("primitive")
142
152
  self.class.unit_group.primitive
143
153
  else
144
154
  unit_from_unit_or_name!(target_unit)
145
155
  end
146
-
147
156
  return self if target_unit == unit
148
157
 
149
- conversion_factor = (unit.conversion_factor / target_unit.conversion_factor)
158
+ conversion_factor = calculate_conversion_factor(target_unit, use_cache)
150
159
 
151
160
  self.class.new((quantity * conversion_factor), target_unit)
152
161
  end
@@ -160,7 +169,17 @@ module UnitMeasurements
160
169
  # UnitMeasurements::Length.new(1, "m").convert_to!("cm")
161
170
  # => 100.0 cm
162
171
  #
163
- # @param [String|Symbol] target_unit The target unit for conversion.
172
+ # UnitMeasurements::Length.new(1, "cm").convert_to!("primitive")
173
+ # => 0.01 m
174
+ #
175
+ # UnitMeasurements::Length.new(1, "m").convert_to!("cm", use_cache: true)
176
+ # => 100.0 cm
177
+ #
178
+ # @param [String|Symbol] target_unit
179
+ # The target unit for conversion. Specifing +primitive+ will convert the
180
+ # measurement to a primitive unit of the unit group.
181
+ # @param [TrueClass|FalseClass] use_cache
182
+ # Indicates whether to use cached conversion factors.
164
183
  #
165
184
  # @return [Measurement]
166
185
  # The current +Measurement+ instance with updated +quantity+ and +unit+.
@@ -168,8 +187,8 @@ module UnitMeasurements
168
187
  # @see #convert_to
169
188
  # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
170
189
  # @since 1.0.0
171
- def convert_to!(target_unit)
172
- measurement = convert_to(target_unit)
190
+ def convert_to!(target_unit, use_cache: false)
191
+ measurement = convert_to(target_unit, use_cache: use_cache)
173
192
  @quantity, @unit = measurement.quantity, measurement.unit
174
193
 
175
194
  self
@@ -219,16 +238,20 @@ module UnitMeasurements
219
238
  extend Forwardable
220
239
 
221
240
  # Methods delegated from the unit group.
222
- def_delegators :unit_group, :primitive, :units, :unit_names, :unit_with_name_and_aliases,
223
- :unit_names_with_aliases, :unit_for, :unit_for!, :defined?,
224
- :unit_or_alias?, :[], :units_for, :units_for!
241
+ def_delegators :unit_group, :primitive, :units, :cache_file, :unit_names,
242
+ :unit_with_name_and_aliases, :unit_names_with_aliases,
243
+ :unit_for, :unit_for!, :defined?, :unit_or_alias?, :[],
244
+ :units_for, :units_for!
225
245
 
226
246
  # Parses an input string and returns a +Measurement+ instance depending on
227
247
  # the input string. This method first normalizes the +input+ internally,
228
248
  # using the +Normalizer+ before parsing it using the +Parser+.
229
249
  #
250
+ # You can separate *source* and *target* units from each other in +input+
251
+ # using +to+, +in+, or +as+.
252
+ #
230
253
  # If only the source unit is provided, it returns a new +Measurement+
231
- # instance with the quantity in the source unit.If both source and target
254
+ # instance with the quantity in the source unit. If both source and target
232
255
  # units are provided in the input string, it returns a new +Measurement+
233
256
  # instance with the quantity converted to the target unit.
234
257
  #
@@ -237,7 +260,7 @@ module UnitMeasurements
237
260
  # => 2.0+3.0i km
238
261
  #
239
262
  # @example Parsing string representing a complex number, source, and target units:
240
- # UnitMeasurements::Length.parse("2+3i km to m")
263
+ # UnitMeasurements::Length.parse("2+3i km in m")
241
264
  # => 2000.0+3000.0i m
242
265
  #
243
266
  # @example Parsing string representing a rational or mixed rational number and source unit:
@@ -260,10 +283,10 @@ module UnitMeasurements
260
283
  # UnitMeasurements::Length.parse("2/3 km to m")
261
284
  # => 666.666666666667 m
262
285
  #
263
- # UnitMeasurements::Length.parse("2 ½ km to m")
286
+ # UnitMeasurements::Length.parse("2 ½ km in m")
264
287
  # => 2500.0 m
265
288
  #
266
- # UnitMeasurements::Length.parse("2 1/2 km to m")
289
+ # UnitMeasurements::Length.parse("2 1/2 km as m")
267
290
  # => 2500.0 m
268
291
  #
269
292
  # @example Parsing string representing a scientific number and source unit:
@@ -281,7 +304,7 @@ module UnitMeasurements
281
304
  # UnitMeasurements::Length.parse("2e+2 km to m")
282
305
  # => 200000.0 m
283
306
  #
284
- # UnitMeasurements::Length.parse("2e⁻² km to m")
307
+ # UnitMeasurements::Length.parse("2e⁻² km as m")
285
308
  # => 20.0 m
286
309
  #
287
310
  # @example Parsing string representing a ratio and source unit:
@@ -289,10 +312,12 @@ module UnitMeasurements
289
312
  # => 0.5 km
290
313
  #
291
314
  # @example Parsing string representing a ratio, source, and target units:
292
- # UnitMeasurements::Length.parse("1:2 km to m")
315
+ # UnitMeasurements::Length.parse("1:2 km in m")
293
316
  # => 500.0 m
294
317
  #
295
318
  # @param [String] input The input string to be parsed.
319
+ # @param [TrueClass|FalseClass] use_cache
320
+ # Indicates whether to use cached conversion factors.
296
321
  #
297
322
  # @return [Measurement] The +Measurement+ instance.
298
323
  #
@@ -303,11 +328,42 @@ module UnitMeasurements
303
328
  # @see #convert_to
304
329
  # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
305
330
  # @since 1.0.0
306
- def parse(input)
331
+ def parse(input, use_cache: false)
307
332
  input = Normalizer.normalize(input)
308
333
  source, target = input.match(CONVERSION_STRING_REGEXP)&.captures
309
334
 
310
- target ? _parse(source).convert_to(target) : _parse(source)
335
+ target ? _parse(source).convert_to(target, use_cache: use_cache) : _parse(source)
336
+ end
337
+
338
+ # Returns the +Cache+ instance for the unit group to store and retrieve
339
+ # conversion factors.
340
+ #
341
+ # @return [Cache] The +Cache+ instance.
342
+ #
343
+ # @example
344
+ # UnitMeasurements::Length.cached
345
+ # => #<UnitMeasurements::Cache:0x00007fe407249750>
346
+ #
347
+ # @see Cache
348
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
349
+ # @since 5.2.0
350
+ def cached
351
+ @cached ||= Cache.new(self)
352
+ end
353
+
354
+ # Clears the cached conversion factors of the unit group.
355
+ #
356
+ # @return [void]
357
+ #
358
+ # @example
359
+ # UnitMeasurements::Length.clear_cache
360
+ #
361
+ # @see Cache#clear_cache
362
+ #
363
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
364
+ # @since 5.2.0
365
+ def clear_cache
366
+ cached.clear_cache
311
367
  end
312
368
 
313
369
  private
@@ -384,5 +440,37 @@ module UnitMeasurements
384
440
  def unit_from_unit_or_name!(value)
385
441
  value.is_a?(Unit) ? value : self.class.send(:unit_group).unit_for!(value)
386
442
  end
443
+
444
+ # Calculates the conversion factor between the current unit and the target
445
+ # unit.
446
+ #
447
+ # If caching is enabled and a cached factor is available, it will be used.
448
+ # Otherwise, the conversion factor will be computed and, if caching is
449
+ # enabled, stored in the cache.
450
+ #
451
+ # @param [Unit] target_unit The target unit for conversion.
452
+ # @param [TrueClass|FalseClass] use_cache
453
+ # Indicates whether caching should be used.
454
+ #
455
+ # @return [Numeric] The conversion factor.
456
+ #
457
+ # @see Unit
458
+ # @see #convert_to
459
+ #
460
+ # @note If caching is enabled, the calculated conversion factor will be stored in the cache.
461
+ #
462
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
463
+ # @since 5.2.0
464
+ def calculate_conversion_factor(target_unit, use_cache)
465
+ use_cache = (UnitMeasurements.configuration.use_cache || use_cache)
466
+
467
+ if use_cache && (cached_factor = self.class.cached.get(unit.name, target_unit.name))
468
+ cached_factor
469
+ else
470
+ factor = unit.conversion_factor / target_unit.conversion_factor
471
+ self.class.cached.set(unit.name, target_unit.name, factor) if use_cache
472
+ factor
473
+ end
474
+ end
387
475
  end
388
476
  end
@@ -36,16 +36,30 @@ module UnitMeasurements
36
36
  # @since 1.0.0
37
37
  attr_reader :units
38
38
 
39
+ # The name of the cache file.
40
+ #
41
+ # @example
42
+ # UnitMeasurements::Length.cache_file
43
+ # => "length.json"
44
+ #
45
+ # @return [String] The name of the cache file.
46
+ #
47
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
48
+ # @since 5.2.0
49
+ attr_reader :cache_file
50
+
39
51
  # Initializes a new +UnitGroup+ instance.
40
52
  #
41
53
  # @param [String|Symbol, optional] primitive The name of the primitive unit.
42
54
  # @param [Array<Unit>] units An array of +Unit+ instances.
55
+ # @param [String] cache_file The name of the cache file.
43
56
  #
44
57
  # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
45
58
  # @since 1.0.0
46
- def initialize(primitive, units)
59
+ def initialize(primitive, units, cache_file)
47
60
  @units = units.map { |unit| unit.with(unit_group: self) }
48
61
  @primitive = unit_for!(primitive) if primitive
62
+ @cache_file = cache_file
49
63
  end
50
64
 
51
65
  # Returns the unit instance for a given unit name. It returns +nil+ if unit
@@ -82,7 +82,7 @@ module UnitMeasurements
82
82
  # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
83
83
  # @since 1.0.0
84
84
  def build
85
- UnitGroup.new(@primitive, @units)
85
+ UnitGroup.new(@primitive, @units, @cache_file)
86
86
  end
87
87
 
88
88
  # Defines the +unit system+ within the unit group and evaluates the provided
@@ -130,6 +130,19 @@ module UnitMeasurements
130
130
  @primitive = primitive
131
131
  end
132
132
 
133
+ # Sets the name of the cache file for the unit group.
134
+ #
135
+ # @example
136
+ # cache "conversion_cache.json"
137
+ #
138
+ # @param [String] cache_file The name of the cache file.
139
+ #
140
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
141
+ # @since 5.2.0
142
+ def cache(cache_file)
143
+ @cache_file = cache_file
144
+ end
145
+
133
146
  private
134
147
 
135
148
  # @private
@@ -4,5 +4,5 @@
4
4
 
5
5
  module UnitMeasurements
6
6
  # Current stable version.
7
- VERSION = "5.1.1"
7
+ VERSION = "5.3.0"
8
8
  end
data/units.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Bundled unit groups and units
2
2
 
3
- As there are lots of units bundled with `unit_measurements`, we recommend you to check below list of
4
- bundled units before converting your measurements.
3
+ As there are lots of units bundled with `unit_measurements`, we recommend you to
4
+ check below list of bundled units before converting your measurements.
5
5
 
6
6
  **Notes:**
7
- 1. Unit names suffixed with `*` support all [Decimal SI prefixes](README.md#decimal-si-prefixes).
8
- 2. Unit names suffixed with `**` support all [Decimal SI prefixes](README.md#decimal-si-prefixes)
9
- and [Binary SI prefixes](README.md#binary-si-prefixes).
7
+ 1. Unit names suffixed with `*` support [Decimal SI prefixes](README.md#decimal-si-prefixes).
8
+ 2. Unit names suffixed with `**` support [Binary SI prefixes](README.md#binary-si-prefixes)
9
+ in addition to [Decimal SI prefixes](README.md#decimal-si-prefixes).
10
10
  3. Primitive unit of the unit group is in _emphasised typeface_.
11
11
 
12
12
  ## 1. Length/Distance
@@ -125,7 +125,7 @@ These units are defined in `UnitMeasurements::Area`.
125
125
  | 12 | fur² | fur^2, sq fur, square furlong, square furlongs |
126
126
  | 13 | rod² | rod^2, sq rod, square rod, square rods |
127
127
 
128
- ## 9. Volume
128
+ ## 9. Volume & Capacity
129
129
 
130
130
  These units are defined in `UnitMeasurements::Volume`.
131
131
 
@@ -328,7 +328,7 @@ These units are defined in `UnitMeasurements::ElectricalElastance`.
328
328
  | _1_ | _D*_ | _F⁻¹, daraf, darafs, reciprocal farad, reciprocal farads_ |
329
329
  | 2 | V/C | V·C⁻¹, volt/coulomb, volts/coulomb, volt per coulomb, volts per coulomb |
330
330
 
331
- ## 24. Electrical resistance
331
+ ## 24. Electrical resistance/Impedance
332
332
 
333
333
  These units are defined in `UnitMeasurements::ElectricalResistance`.
334
334
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unit_measurements
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.1
4
+ version: 5.3.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-10-20 00:00:00.000000000 Z
11
+ date: 2023-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -107,7 +107,9 @@ files:
107
107
  - lib/unit_measurements.rb
108
108
  - lib/unit_measurements/arithmetic.rb
109
109
  - lib/unit_measurements/base.rb
110
+ - lib/unit_measurements/cache.rb
110
111
  - lib/unit_measurements/comparison.rb
112
+ - lib/unit_measurements/configuration.rb
111
113
  - lib/unit_measurements/conversion.rb
112
114
  - lib/unit_measurements/errors/parse_error.rb
113
115
  - lib/unit_measurements/errors/primitive_unit_already_set_error.rb