Skip to content

Commit

Permalink
Merge pull request #49 from shivam091/5.2.0
Browse files Browse the repository at this point in the history
5.2.0
  • Loading branch information
shivam091 authored Oct 22, 2023
2 parents 650004c + 14baa31 commit 6fcf04b
Show file tree
Hide file tree
Showing 15 changed files with 624 additions and 109 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/pkg/
/spec/reports/
/tmp/
/cache/*.json

# rspec failure tracking
.rspec_status
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## [5.2.0](https://github.com/shivam091/unit_measurements/compare/v5.1.1...v5.2.0) - 2023-10-22

### What's new

- Added ability to set name of the cache file for the unit group.
- Added support for caching conversion factors between units of the unit group.

----------

## [5.1.1](https://github.com/shivam091/unit_measurements/compare/v5.1.0...v5.1.1) - 2023-10-20

### What's updated
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
unit_measurements (5.1.1)
unit_measurements (5.2.0)
activesupport (~> 7.0)

GEM
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ The `unit_measurements` gem is designed to simplify the handling of units for sc
1. Provides easy conversion between units.
2. Lightweight and easily extensible to include other units and conversions.
3. Built in support for various [unit groups](https://github.com/shivam091/unit_measurements/blob/main/units.md).
4. Well organized and very descriptive documentation published [here](https://shivam091.github.io/unit_measurements).
5. Parses strings representing complex, fractional, mixed fractional, scientific numbers, and ratios.
4. Ability to parse strings representing complex, fractional, mixed fractional, scientific numbers, and ratios.
5. Well organized and descriptive documentation published [here](https://shivam091.github.io/unit_measurements).
6. Supports caching of conversion factors between different units of the unit group.

## Disclaimer

Expand Down Expand Up @@ -74,6 +75,8 @@ UnitMeasurements::Length.new(1, "km")
This gem allows you to convert among units of same unit group. You can convert measurement to other unit using `#convert_to`
(aliased as `#to`, `#in`, and `#as`) or `#convert_to!` (aliased as `#to!`, `#in!`, and `#as!`) methods.

These methods provide `use_cache` parameter which can be used to indicate whether the caching of conversion factors should happen.

You can use `#convert_to` as:

```ruby
Expand Down Expand Up @@ -104,6 +107,8 @@ UnitMeasurements::Length.new(100, "m").convert_to("ft").convert_to!("in")

**Parse string without having to split out the quantity and source unit:**

This method provides `use_cache` parameter which can be used to indicate whether the caching of conversion factors should happen.

```ruby
UnitMeasurements::Length.parse("1 km")
#=> 1.0 km
Expand Down Expand Up @@ -254,6 +259,12 @@ UnitMeasurements::Length.unit_or_alias?("metre")
#=> true
```

**Clear cached data for the unit group:**

```ruby
UnitMeasurements::Length.clear_cache
```

### Comparisons

You have ability to compare the measurements with the same or different units within the same unit group.
Expand Down Expand Up @@ -413,6 +424,9 @@ UnitMeasurements::Time = UnitMeasurements.build do
# You can also specify unit value as an array.
unit "h", value: [60, "min"], aliases: ["day", "days"]
end

# Sets the name of the cache file (optional).
cache "time_cache.json"
end
```

Expand Down
3 changes: 3 additions & 0 deletions lib/unit_measurements/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class << self
# system :imperial do
# unit "in", value: "25.4 mm", aliases: ['"', "inch", "inches"]
# end
#
# cache "length.json"
# end
#
# @param block
Expand Down Expand Up @@ -78,6 +80,7 @@ class << self
end

# The following requires load various components of the unit measurements library.
require "unit_measurements/cache"
require "unit_measurements/unit_group_builder"
require "unit_measurements/unit"
require "unit_measurements/unit_group"
Expand Down
173 changes: 173 additions & 0 deletions lib/unit_measurements/cache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# -*- encoding: utf-8 -*-
# -*- frozen_string_literal: true -*-
# -*- warn_indent: true -*-

module UnitMeasurements
# The +UnitMeasurements::Cache+ class manages caching of conversion factors
# between different units within a unit group. It provides methods to retrieve,
# set, and clear cached conversion factors.
#
# Cached conversion factors are stored in JSON file on the file system.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
class Cache
# The directory path where cache files are stored.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
CACHE_DIRECTORY = File.expand_path(File.join("..", "..", "cache"), __dir__).freeze

# Stores cached conversion factors between different units within a unit
# group.
#
# @return [Hash] The cached conversion factors.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
attr_reader :cached_data

# Initializes a new +Cache+ instance for a specific unit group.
#
# Initialization first ensures existence of the cache directory. If the cache
# directory does not exist, it gets created.
#
# @param [UnitGroup] unit_group The unit group associated with the cache.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
def initialize(unit_group)
ensure_cache_directory_exists
@cache_file = build_cache_file_path(unit_group)
@cached_data ||= load_cache
end

# Retrieves the conversion factor between source and target units from the
# cache.
#
# @param [String] source_unit The source unit name.
# @param [String] target_unit The target unit name.
#
# @return [Numeric|NilClass]
# The conversion factor, or +nil+ if not found in the cache.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
def get(source_unit, target_unit)
cached_data.dig(source_unit, target_unit)
end

# Sets the conversion factor between source and target units in the cache.
#
# @param [String] source_unit The source unit name.
# @param [String] target_unit The target unit name.
# @param [Numeric] conversion_factor The conversion factor.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
def set(source_unit, target_unit, conversion_factor)
cached_data[source_unit] ||= {}
cached_data[source_unit][target_unit] = conversion_factor

store_cache
end

# Clears the entire cache.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
def clear_cache
@cached_data = {}
store_cache
end

private

# @private
# Loads the cache from the cache file.
#
# @return [Hash] The loaded cache data.
#
# @raise [Errno::ENOENT] If the cache file does not exist.
# @raise [Errno::EACCES]
# If the cache file cannot be accessed due to insufficient permissions.
# @raise [JSON::ParserError] If there's an error parsing the cache file.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
def load_cache
return {} unless File.exist?(@cache_file)

begin
File.open(@cache_file, "r") { |file| JSON.load(file.read) }
rescue Errno::ENOENT, Errno::EACCES, JSON::ParserError => e
puts "Error loading cache"
{}
end
end

# @private
# Stores the current cache data to the cache file. +cached_data+ is stored in
# prettier form.
#
# @raise [Errno::ENOENT] If the cache file does not exist.
# @raise [Errno::EACCES]
# If the cache file cannot be accessed due to insufficient permissions.
# @raise [Errno::ENOSPC]
# If there's not enough space to write to the cache file.
# @raise [JSON::GeneratorError] If there's an error generating JSON data.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
def store_cache
begin
File.open(@cache_file, "w") do |file|
file.write(JSON.pretty_generate(cached_data))
end
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOSPC, JSON::GeneratorError => e
puts "Error saving cache: #{e.message}"
end
end

# @private
# Ensures that the cache directory exists. If the cache directory does not
# exist, it gets created.
#
# @raise [Errno::EEXIST] If the cache directory already exists.
# @raise [Errno::EACCES]
# If the cache directory cannot be accessed due to insufficient permissions.
# @raise [Errno::ENOSPC]
# If there's not enough space to create the cache directory.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
def ensure_cache_directory_exists
begin
Dir.mkdir(CACHE_DIRECTORY) unless Dir.exist?(CACHE_DIRECTORY)
rescue Errno::EACCES, Errno::ENOSPC => e
puts "Error creating cache directory: #{e.message}"
end
end

# @private
# Builds and returns an absolute path of the cache file.
#
# This method first checks if the cache file name is specified for the unit
# group. If yes, it builds absolute path of the cache file using specified
# cache file name. If not, it builds file name from of the name of the unit
# group. This file name is then used to build absolute path of the cache file.
#
# @param [UnitGroup] unit_group The unit group associated with the cache.
#
# @return [String] An absolute path of the cache file.
#
# @author {Harshal V. Ladhe}[https://shivam091.github.io/]
# @since 5.2.0
def build_cache_file_path(unit_group)
cache_file_name = unit_group.cache_file || unit_group.to_s.split("::").last.underscore
cache_file_name = File.basename(cache_file_name, ".json") + ".json"

Pathname.new(File.join(CACHE_DIRECTORY, cache_file_name)).cleanpath
end
end
end
Loading

0 comments on commit 6fcf04b

Please sign in to comment.