lib/unit_measurements/measurement.rb in unit_measurements-5.1.1 vs lib/unit_measurements/measurement.rb in unit_measurements-5.2.0

- old
+ new

@@ -118,37 +118,46 @@ end # Converts the measurement to a +target_unit+ and returns new instance of the # measurement. # + # When +use_cache+ value is true, conversion factor between units are checked + # in cache file of the unit group. If cached conversion factor is present in + # the cache file, it is used for conversion otherwise conversion factor is + # stored in the cache before converting the measurement to the +target_unit+. + # # @example # UnitMeasurements::Length.new(1, "m").convert_to("cm") # => 100.0 cm # # UnitMeasurements::Length.new(1, "cm").convert_to("primitive") # => 0.01 m # + # UnitMeasurements::Length.new(1, "m").convert_to("cm", use_cache: true) + # => 100.0 cm + # # @param [String|Symbol] target_unit # The target unit for conversion. Specifing +primitive+ will convert the # measurement to a primitive unit of the unit group. + # @param [TrueClass|FalseClass] use_cache + # Indicates whether to use cached conversion factors. # # @return [Measurement] # A new +Measurement+ instance with the converted +quantity+ and # +target unit+. # # @author {Harshal V. Ladhe}[https://shivam091.github.io/] # @since 1.0.0 - def convert_to(target_unit) + def convert_to(target_unit, use_cache: false) target_unit = if target_unit.to_s.eql?("primitive") self.class.unit_group.primitive else unit_from_unit_or_name!(target_unit) end - return self if target_unit == unit - conversion_factor = (unit.conversion_factor / target_unit.conversion_factor) + conversion_factor = calculate_conversion_factor(target_unit, use_cache) self.class.new((quantity * conversion_factor), target_unit) end alias_method :to, :convert_to alias_method :in, :convert_to @@ -158,20 +167,30 @@ # # @example # UnitMeasurements::Length.new(1, "m").convert_to!("cm") # => 100.0 cm # - # @param [String|Symbol] target_unit The target unit for conversion. + # UnitMeasurements::Length.new(1, "cm").convert_to!("primitive") + # => 0.01 m # + # UnitMeasurements::Length.new(1, "m").convert_to!("cm", use_cache: true) + # => 100.0 cm + # + # @param [String|Symbol] target_unit + # The target unit for conversion. Specifing +primitive+ will convert the + # measurement to a primitive unit of the unit group. + # @param [TrueClass|FalseClass] use_cache + # Indicates whether to use cached conversion factors. + # # @return [Measurement] # The current +Measurement+ instance with updated +quantity+ and +unit+. # # @see #convert_to # @author {Harshal V. Ladhe}[https://shivam091.github.io/] # @since 1.0.0 - def convert_to!(target_unit) - measurement = convert_to(target_unit) + def convert_to!(target_unit, use_cache: false) + measurement = convert_to(target_unit, use_cache: use_cache) @quantity, @unit = measurement.quantity, measurement.unit self end alias_method :to!, :convert_to! @@ -217,29 +236,33 @@ class << self extend Forwardable # Methods delegated from the unit group. - def_delegators :unit_group, :primitive, :units, :unit_names, :unit_with_name_and_aliases, - :unit_names_with_aliases, :unit_for, :unit_for!, :defined?, - :unit_or_alias?, :[], :units_for, :units_for! + def_delegators :unit_group, :primitive, :units, :cache_file, :unit_names, + :unit_with_name_and_aliases, :unit_names_with_aliases, + :unit_for, :unit_for!, :defined?, :unit_or_alias?, :[], + :units_for, :units_for! # Parses an input string and returns a +Measurement+ instance depending on # the input string. This method first normalizes the +input+ internally, # using the +Normalizer+ before parsing it using the +Parser+. # + # You can separate *source* and *target* units from each other in +input+ + # using +to+, +in+, or +as+. + # # If only the source unit is provided, it returns a new +Measurement+ - # instance with the quantity in the source unit.If both source and target + # instance with the quantity in the source unit. If both source and target # units are provided in the input string, it returns a new +Measurement+ # instance with the quantity converted to the target unit. # # @example Parsing string representing a complex number and source unit: # UnitMeasurements::Length.parse("2+3i km") # => 2.0+3.0i km # # @example Parsing string representing a complex number, source, and target units: - # UnitMeasurements::Length.parse("2+3i km to m") + # UnitMeasurements::Length.parse("2+3i km in m") # => 2000.0+3000.0i m # # @example Parsing string representing a rational or mixed rational number and source unit: # UnitMeasurements::Length.parse("½ km") # => 0.5 km @@ -258,14 +281,14 @@ # => 500.0 km # # UnitMeasurements::Length.parse("2/3 km to m") # => 666.666666666667 m # - # UnitMeasurements::Length.parse("2 ½ km to m") + # UnitMeasurements::Length.parse("2 ½ km in m") # => 2500.0 m # - # UnitMeasurements::Length.parse("2 1/2 km to m") + # UnitMeasurements::Length.parse("2 1/2 km as m") # => 2500.0 m # # @example Parsing string representing a scientific number and source unit: # UnitMeasurements::Length.parse("2e² km") # => 200.0 km @@ -279,39 +302,72 @@ # @example Parsing string representing a scientific number, source, and target units: # # UnitMeasurements::Length.parse("2e+2 km to m") # => 200000.0 m # - # UnitMeasurements::Length.parse("2e⁻² km to m") + # UnitMeasurements::Length.parse("2e⁻² km as m") # => 20.0 m # # @example Parsing string representing a ratio and source unit: # UnitMeasurements::Length.parse("1:2 km") # => 0.5 km # # @example Parsing string representing a ratio, source, and target units: - # UnitMeasurements::Length.parse("1:2 km to m") + # UnitMeasurements::Length.parse("1:2 km in m") # => 500.0 m # # @param [String] input The input string to be parsed. + # @param [TrueClass|FalseClass] use_cache + # Indicates whether to use cached conversion factors. # # @return [Measurement] The +Measurement+ instance. # # @see Parser # @see Normalizer # @see CONVERSION_STRING_REGEXP # @see ._parse # @see #convert_to # @author {Harshal V. Ladhe}[https://shivam091.github.io/] # @since 1.0.0 - def parse(input) + def parse(input, use_cache: false) input = Normalizer.normalize(input) source, target = input.match(CONVERSION_STRING_REGEXP)&.captures - target ? _parse(source).convert_to(target) : _parse(source) + target ? _parse(source).convert_to(target, use_cache: use_cache) : _parse(source) end + # Returns the +Cache+ instance for the unit group to store and retrieve + # conversion factors. + # + # @return [Cache] The +Cache+ instance. + # + # @example + # UnitMeasurements::Length.cached + # => #<UnitMeasurements::Cache:0x00007fe407249750> + # + # @see Cache + # @author {Harshal V. Ladhe}[https://shivam091.github.io/] + # @since 5.2.0 + def cached + @cached ||= Cache.new(self) + end + + # Clears the cached conversion factors of the unit group. + # + # @return [void] + # + # @example + # UnitMeasurements::Length.clear_cache + # + # @see Cache#clear_cache + # + # @author {Harshal V. Ladhe}[https://shivam091.github.io/] + # @since 5.2.0 + def clear_cache + cached.clear_cache + end + private # @private # The class attribute representing an instance of +UnitGroup+. # @@ -381,8 +437,38 @@ # # @author {Harshal V. Ladhe}[https://shivam091.github.io/] # @since 1.0.0 def unit_from_unit_or_name!(value) value.is_a?(Unit) ? value : self.class.send(:unit_group).unit_for!(value) + end + + # Calculates the conversion factor between the current unit and the target + # unit. + # + # If caching is enabled and a cached factor is available, it will be used. + # Otherwise, the conversion factor will be computed and, if caching is + # enabled, stored in the cache. + # + # @param [Unit] target_unit The target unit for conversion. + # @param [TrueClass|FalseClass] use_cache + # Indicates whether caching should be used. + # + # @return [Numeric] The conversion factor. + # + # @see Unit + # @see #convert_to + # + # @note If caching is enabled, the calculated conversion factor will be stored in the cache. + # + # @author {Harshal V. Ladhe}[https://shivam091.github.io/] + # @since 5.2.0 + def calculate_conversion_factor(target_unit, use_cache) + if use_cache && (cached_factor = self.class.cached.get(unit.name, target_unit.name)) + cached_factor + else + factor = unit.conversion_factor / target_unit.conversion_factor + self.class.cached.set(unit.name, target_unit.name, factor) if use_cache + factor + end end end end