diff --git a/CHANGELOG.md b/CHANGELOG.md index 06164f8..20b08a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,16 @@ +## [5.15.0](https://github.com/shivam091/unit_measurements/compare/v5.14.0...v5.15.0) - 2023-12-01 + +### What's new + +- Added `.define_conversion_methods` method to define conversion helper methods for units. + +---------- + ## [5.14.0](https://github.com/shivam091/unit_measurements/compare/v5.13.0...v5.14.0) - 2023-11-29 ### What's new -- Added `.define_numeric_methods` support to define numeric extension methods for units. +- Added `.define_numeric_methods` method to define numeric extension methods for units. ---------- diff --git a/Gemfile.lock b/Gemfile.lock index 4704e43..39aec7c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unit_measurements (5.14.0) + unit_measurements (5.15.0) activesupport (~> 7.0) GEM diff --git a/README.md b/README.md index f8f225e..e78db44 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,12 @@ Users are advised to cross-verify conversions for their specific use cases._ ## Minimum Requirements -* Ruby 3.2.2+ (https://www.ruby-lang.org/en/downloads/branches/) +* Ruby 3.2.2+ ([Download Ruby](https://www.ruby-lang.org/en/downloads/branches/)) ## Installation -If using bundler, first add this line to your application's Gemfile: +To use `unit_measurements` in your Rails application, add the +following line to your Gemfile: ```ruby gem "unit_measurements" @@ -269,8 +270,7 @@ UnitMeasurements::Length.units_for("metric") **Finding units within the unit group:** You can use `#unit_for` or `#unit_for!` (aliased as `#[]`) methods to find units -within the unit group. `#unit_for!` method returns an error if a unit is not present -in the unit group. +within the unit group. `#unit_for!` method returns an error if a unit system is not defined within the unit group. ```ruby UnitMeasurements::Length.unit_for("m") @@ -484,13 +484,12 @@ Volume = UnitMeasurements::Volume ## Extras -### Numeric extension methods +### Numeric methods -The `.define_numeric_methods` method allows you to instantiate measurements in a -manner similar to how `ActiveSupport::Duration` objects are created in Rails, -providing a familiar syntax and functionality. +The `.define_numeric_methods` method allows you to define numeric methods that help you to initialize measurements in a +manner similar to how `ActiveSupport::Duration` objects are created in Rails, providing a familiar syntax and functionality. -To define numeric extension methods for specific units within a unit group, use +To define numeric methods for specific units within the unit group, use the following syntax: ```ruby @@ -500,21 +499,46 @@ UnitMeasurements::Length.define_numeric_methods("metre", "foot", "inch") This will enable the usage of these units as methods to instantiate and use measurements: ```ruby -1.m #=> Instantiate a measurement representing 1 metre. -5.feet #=> Instantiate a measurement representing 5 feet. -10.inches #=> Instantiate a measurement representing 10 inches. -1.foot == 12.inches #=> equality comparison between two measurements. -1.ft + 12.in #=> adds quantity of two measurements. +# Initialize a measurement +1.m #=> 1 m +5.feet #=> 5 ft +10.inches #=> 10 in + +# Usage +## Equality comparison +1.foot == 12.inches #=> true +## Arithmetic operation +1.ft + 12.in #=> 2.0 ft +``` + +### Conversion methods + +The `.define_conversion_methods` method allows you to define conversion methods that +help you to easily convert measurements to a different unit. + +To define conversion methods for specific units within the unit group, use the following syntax: + +```ruby +UnitMeasurements::Length.define_conversion_methods("metre", "foot", "inch") +``` + +This will enable you to convert units as: + +```ruby +UnitMeasurements::Length.new(1, "ft").in_inches #=> 12.0 in +12.in.in_foot #=> 1.0 ft ``` ## Contributing -1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) +Contributions to this project are welcomed! To contribute: + +1. Fork this repository +2. Create a new branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am "Add some feature"`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request +4. Push the changes to your branch (`git push origin my-new-feature`) +5. Create new **Pull Request** ## License -Copyright 2023 [Harshal V. LADHE]((https://shivam091.github.io)), Released under the [MIT License](http://opensource.org/licenses/MIT). +Copyright 2023 [Harshal V. LADHE](https://shivam091.github.io), Released under the [MIT License](http://opensource.org/licenses/MIT). diff --git a/lib/unit_measurements/base.rb b/lib/unit_measurements/base.rb index 81859b2..def3f33 100644 --- a/lib/unit_measurements/base.rb +++ b/lib/unit_measurements/base.rb @@ -152,6 +152,7 @@ def configure # The following requires load various components of the unit measurements library. require "unit_measurements/extras/numeric_methods" +require "unit_measurements/extras/conversion_methods" require "unit_measurements/configuration" require "unit_measurements/cache" diff --git a/lib/unit_measurements/extras/conversion_methods.rb b/lib/unit_measurements/extras/conversion_methods.rb new file mode 100644 index 0000000..bf33cc7 --- /dev/null +++ b/lib/unit_measurements/extras/conversion_methods.rb @@ -0,0 +1,87 @@ +# -*- encoding: utf-8 -*- +# -*- frozen_string_literal: true -*- +# -*- warn_indent: true -*- + +module UnitMeasurements + # This module provides functionality to define conversion methods for a list + # of units within a unit group. If units are empty, it defaults to defining + # methods for all units in the unit group. These methods allow easy conversion + # between different units within a given unit group. + # + # This module is included in the +Measurement+ class to allow defining conversion + # methods for specified units. + # + # @see Measurement + # + # @author {Harshal V. Ladhe}[https://shivam091.github.io/] + # @since 5.15.0 + module ConversionMethods + # @scope class + # Defines conversion methods for specified +units+ within the unit group. + # If +units+ are empty, it defaults to defining methods for all units within + # the unit group. + # + # @example Define conversion methods for metres, centimetres, and millimetres: + # UnitMeasurements::Length.define_conversion_methods("metres", :cm, :mm) + # + # @example Define conversion methods for all units within the unit group: + # UnitMeasurements::Length.define_conversion_methods + # + # @param [Array, optional] units + # An array of units' names for which conversion methods need to be defined. + # If empty, methods will be defined for all units within the unit group. + # + # @return [Array] + # An array of units for which the conversion methods were defined. + # + # @note + # This method defines a conversion methods specifically for units that contain + # alphabetic characters in their names. + # + # @see .define_conversion_method_for + # @author {Harshal V. Ladhe}[https://shivam091.github.io/] + # @since 5.15.0 + def define_conversion_methods(*units) + unit_group = self + units = units.empty? ? unit_group.units : units + + units.inject([]) do |units, unit| + units << define_conversion_method_for(unit, unit_group) + end + end + + private + + # @private + # @scope class + # Defines conversion methods for a specific +unit+ within a +unit_group+. + # These methods are defined dynamically using +define_method+. + # + # @param [String|Symbol|Unit] unit + # The unit (or its name) for which the conversion methods need to be defined. + # @param [UnitGroup] unit_group The unit group to which the unit belongs. + # + # @return [Unit] + # The unit instance for which the conversion methods were defined. + # + # @see .define_conversion_methods + # @author {Harshal V. Ladhe}[https://shivam091.github.io/] + # @since 5.15.0 + def define_conversion_method_for(unit, unit_group) + unit = unit.is_a?(Unit) ? unit : unit_group.unit_for!(unit) + + unit.names.each do |method_name| + # Check if the name contains alphabetic characters + next unless method_name =~ /^[a-zA-Z]+$/ + + define_method("in_#{method_name}") do |use_cache: false| + convert_to(unit, use_cache: use_cache) + end + alias_method "to_#{method_name}", "in_#{method_name}" + alias_method "as_#{method_name}", "in_#{method_name}" + end + + unit + end + end +end diff --git a/lib/unit_measurements/extras/numeric_methods.rb b/lib/unit_measurements/extras/numeric_methods.rb index 991cf9f..a84ff4f 100644 --- a/lib/unit_measurements/extras/numeric_methods.rb +++ b/lib/unit_measurements/extras/numeric_methods.rb @@ -3,12 +3,12 @@ # -*- warn_indent: true -*- module UnitMeasurements - # This module provides methods to define +Numeric+ extension methods for a list - # of units within a unit group. If units are empty, it defaults to defining - # methods for all units in the unit group. + # This module provides methods to define +Numeric+ methods for a list of units + # within a unit group. If units are empty, it defaults to defining methods for + # all units in the unit group. # # This module is included in the +Measurement+ class to allow defining numeric - # extension methods for specified units. + # methods for specified units. # # @see Measurement # @@ -16,23 +16,27 @@ module UnitMeasurements # @since 5.14.0 module NumericMethods # @scope class - # Defines +Numeric+ extension methods for specified units within the unit - # group. If units are empty, it defaults to defining methods for all units - # within the unit group. - # - # @param [Array] units - # An array of units' names for which numeric methods need to be defined. - # If empty, methods will be defined for all units in the unit group. - # - # @return [Array] An array of units for which methods are defined. + # Defines +Numeric+ methods for specified +units+ within the unit group. If + # +units+ are empty, it defaults to defining methods for all units within + # the unit group. # # @example Define numeric methods for metres, centimetres, and millimetres: # UnitMeasurements::Length.define_numeric_methods("metres", :cm, :mm) # - # @example Define numeric methods for all units in the unit group: + # @example Define numeric methods for all units within the unit group: # UnitMeasurements::Length.define_numeric_methods # - # @see #define_numeric_method_for + # @param [Array, optional] units + # An array of units' names for which numeric methods need to be defined. + # If empty, methods will be defined for all units within the unit group. + # + # @return [Array] An array of units for which numeric methods were defined. + # + # @note + # This method defines a numeric methods specifically for units that contain + # alphabetic characters in their names. + # + # @see .define_numeric_method_for # # @author {Harshal V. Ladhe}[https://shivam091.github.io/] # @since 5.14.0 @@ -49,17 +53,17 @@ def define_numeric_methods(*units) # @private # @scope class - # This method defines a numeric method for a specific unit within a unit group. - # The method is defined dynamically using +define_method+ and associates the - # unit with the numeric value. + # Defines a numeric method for a specific +unit+ within a +unit_group+. The + # method is defined dynamically using +define_method+ and associates the unit + # with the numeric value. # # @param [String|Symbol|Unit] unit - # The unit for which the numeric method is defined. + # The unit (or its name) for which the numeric method needs to be defined. # @param [UnitGroup] unit_group The unit group to which the unit belongs. # # @return [Unit] The unit instance for which the method was defined. # - # @see #define_numeric_methods + # @see .define_numeric_methods # # @author {Harshal V. Ladhe}[https://shivam091.github.io/] # @since 5.14.0 diff --git a/lib/unit_measurements/measurement.rb b/lib/unit_measurements/measurement.rb index 9cf5c10..7074d45 100644 --- a/lib/unit_measurements/measurement.rb +++ b/lib/unit_measurements/measurement.rb @@ -34,6 +34,7 @@ class Measurement include Math extend NumericMethods + extend ConversionMethods # Regular expression to match conversion strings. # diff --git a/lib/unit_measurements/version.rb b/lib/unit_measurements/version.rb index 9138ba0..90976a8 100644 --- a/lib/unit_measurements/version.rb +++ b/lib/unit_measurements/version.rb @@ -4,5 +4,5 @@ module UnitMeasurements # Current stable version. - VERSION = "5.14.0" + VERSION = "5.15.0" end diff --git a/spec/unit_measurements/extras/conversion_methods_spec.rb b/spec/unit_measurements/extras/conversion_methods_spec.rb new file mode 100644 index 0000000..4ccb519 --- /dev/null +++ b/spec/unit_measurements/extras/conversion_methods_spec.rb @@ -0,0 +1,41 @@ +# -*- encoding: utf-8 -*- +# -*- frozen_string_literal: true -*- +# -*- warn_indent: true -*- + +# spec/unit_measurements/extras/conversion_methods_spec.rb + +RSpec.describe UnitMeasurements::ConversionMethods do + let(:unit_group) { UnitMeasurements::Length } + let(:measurement) { unit_group.new(1, "ft") } + + context "when units are specified" do + it "defines methods for specified units" do + unit_group.define_conversion_methods("m", "cm") + + expect(unit_group.method_defined?("to_m")).to be_truthy + expect(unit_group.method_defined?("to_cm")).to be_truthy + + expect(measurement).to respond_to(:to_m) + expect(measurement).to respond_to(:in_m) + expect(measurement).to respond_to(:as_m) + + expect(measurement.to_m.to_s).to eq("0.3048 m") + end + end + + context "when units are specified" do + + it "defines methods for all units within the unit group" do + unit_group.define_conversion_methods + + expect(unit_group.method_defined?("to_ft")).to be_truthy + expect(unit_group.method_defined?("to_in")).to be_truthy + + expect(measurement).to respond_to(:to_in) + expect(measurement).to respond_to(:to_inch) + expect(measurement).to respond_to(:to_inches) + + expect(measurement.to_in.to_s).to eq("12.0 in") + end + end +end diff --git a/spec/unit_measurements/extras/numeric_methods_spec.rb b/spec/unit_measurements/extras/numeric_methods_spec.rb index 506a367..b9f5ce2 100644 --- a/spec/unit_measurements/extras/numeric_methods_spec.rb +++ b/spec/unit_measurements/extras/numeric_methods_spec.rb @@ -10,7 +10,7 @@ let(:cm) { unit_group.unit_for!(:cm) } context "when units are specified" do - it "defines extension methods for specified units" do + it "defines methods for specified units" do unit_group.define_numeric_methods("m", "cm") expect(Numeric.method_defined?("m")).to be_truthy @@ -21,7 +21,7 @@ end context "when units are specified" do - it "defines extension methods for all units within the unit group" do + it "defines methods for all units within the unit group" do unit_group.define_numeric_methods expect(Numeric.method_defined?("ft")).to be_truthy