Module: UnitMeasurements::Rails::ActiveRecord

Defined in:
lib/unit_measurements/rails/active_record.rb

Overview

The UnitMeasurements::Rails::ActiveRecord module enhances ActiveRecord models by providing a convenient way to handle unit measurements. It facilitates defining measurement attributes in models with specific unit group support.

Author:

Since:

  • 1.0.0

Defined Under Namespace

Modules: Area, Density, Length, Temperature, Time, Volume, Weight

Class Method Summary collapse

Class Method Details

.define_reader_for_measured_attr(measured_attr, quantity_attr, unit_attr, unit_group) ⇒ void (private)

This method returns an undefined value.

Defines the method to read the measurement attribute.

Parameters:

  • measured_attr (String)

    The name of the measurement attribute.

  • quantity_attr (String)

    The name of the quantity attribute.

  • unit_attr (String)

    The name of the unit attribute.

  • unit_group (Class)

    The unit group class for the measurement.

Author:

Since:

  • 1.0.0



141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/unit_measurements/rails/active_record.rb', line 141

def define_reader_for_measured_attr(measured_attr, quantity_attr, unit_attr, unit_group)
  define_method(measured_attr) do
    column_exists?(quantity_attr) && column_exists?(unit_attr)

    quantity, unit = public_send(quantity_attr), public_send(unit_attr)

    begin
      unit_group.new(quantity, unit)
    rescue BlankQuantityError, BlankUnitError, ParseError, UnitError
      nil
    end
  end
end

.define_writer_for_measured_attr(measured_attr, quantity_attr, unit_attr, unit_group) ⇒ void (private)

This method returns an undefined value.

Defines the method to write the measurement attribute.

Parameters:

  • measured_attr (String)

    The name of the measurement attribute.

  • quantity_attr (String)

    The name of the quantity attribute.

  • unit_attr (String)

    The name of the unit attribute.

  • unit_group (Class)

    The unit group class for the measurement.

Author:

Since:

  • 1.0.0



168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/unit_measurements/rails/active_record.rb', line 168

def define_writer_for_measured_attr(measured_attr, quantity_attr, unit_attr, unit_group)
  define_method("#{measured_attr}=") do |measurement|
    column_exists?(quantity_attr) && column_exists?(unit_attr)

    if measurement.is_a?(unit_group)
      public_send("#{quantity_attr}=", measurement.quantity)
      public_send("#{unit_attr}=", measurement.unit.name)
    else
      public_send("#{quantity_attr}=", nil)
      public_send("#{unit_attr}=", nil)
    end
  end
end

.measured(unit_group, *measured_attrs, **options) ⇒ void

This method returns an undefined value.

Defines a reader and writer methods for the measurement attributes in the ActiveRecord model.

Examples:

Defining single measurement attribute:

class Cube < ActiveRecord::Base
  measured UnitMeasurements::Length, :height
end

Defining multiple measurement attributes:

class Package < ActiveRecord::Base
  measured UnitMeasurements::Weight, :item_weight, :total_weight
end

Parameters:

  • unit_group (Class|String)

    The unit group class or its name as a string.

  • measured_attrs (Array<String|Symbol>)

    An array of the names of measurement attributes.

  • options (Hash)

    A customizable set of options

Options Hash (**options):

  • :quantity_attribute_name (String|Symbol)

    The name of the quantity attribute.

  • :unit_attribute_name (String|Symbol)

    The name of the unit attribute.

Raises:

  • (BaseError)

    If unit_group is not a subclass of UnitMeasurements::Measurement or the attribute has already been measured.

See Also:

Author:

Since:

  • 1.0.0



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/unit_measurements/rails/active_record.rb', line 55

def measured(unit_group, *measured_attrs, **options)
  validate_unit_group!(unit_group)

  options = options.reverse_merge(quantity_attribute_name: nil, unit_attribute_name: nil)
  unit_group = unit_group.constantize if unit_group.is_a?(String)

  options[:unit_group] = unit_group

  measured_attrs.map(&:to_s).each do |measured_attr|
    raise BaseError, "The attribute '#{measured_attr}' has already been measured." if measured_attributes.key?(measured_attr)

    quantity_attr = options[:quantity_attribute_name]&.to_s || "#{measured_attr}_quantity"
    unit_attr = options[:unit_attribute_name]&.to_s || "#{measured_attr}_unit"

    measured_attributes[measured_attr] = options.merge(quantity_attribute_name: quantity_attr, unit_attribute_name: unit_attr)

    define_reader_for_measured_attr(measured_attr, quantity_attr, unit_attr, unit_group)
    define_writer_for_measured_attr(measured_attr, quantity_attr, unit_attr, unit_group)
    redefine_quantity_writer(quantity_attr)
    redefine_unit_writer(unit_attr, unit_group)
  end
end

.measured_attributesHash{String => Hash{Symbol => String|Class}}

Returns a hash containing information about the measurement attributes and their options.

Examples:

{
  "height" => {
    unit_group: UnitMeasurements::Length,
    quantity_attribute_name: "height_quantity",
    unit_attribute_name: "height_unit"
  },
  "weight" => {
    unit_group: UnitMeasurements::Length,
    quantity_attribute_name: "weight_quantity",
    unit_attribute_name: "weight_unit"
  }
}

Returns:

  • (Hash{String => Hash{Symbol => String|Class}})

    A hash where keys represent the names of the measurement attributes, and values are hashes containing the options for each measurement attribute.

Author:

Since:

  • 1.2.0



103
104
105
# File 'lib/unit_measurements/rails/active_record.rb', line 103

def measured_attributes
  @measured_attributes ||= {}
end

.redefine_quantity_writer(quantity_attr) ⇒ void (private)

This method returns an undefined value.

Redefines the writer method to set the quantity attribute.

Parameters:

  • quantity_attr (String)

    The name of the quantity attribute.

Author:

Since:

  • 1.0.0



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/unit_measurements/rails/active_record.rb', line 192

def redefine_quantity_writer(quantity_attr)
  redefine_method("#{quantity_attr}=") do |quantity|
    column_exists?(quantity_attr)

    quantity = BigDecimal(quantity, Float::DIG) if quantity.is_a?(String)
    if quantity
      db_column_props = self.column_for_attribute(quantity_attr)
      precision, scale = db_column_props.precision, db_column_props.scale

      quantity.round(scale)
    else
      nil
    end.tap { |value| write_attribute(quantity_attr, value) }
  end
end

.redefine_unit_writer(unit_attr, unit_group) ⇒ void (private)

This method returns an undefined value.

Redefines the writer method to set the unit attribute.

Parameters:

  • unit_attr (String)

    The name of the unit attribute.

  • unit_group (Class)

    The unit group class for the measurement.

Author:

Since:

  • 1.0.0



219
220
221
222
223
224
225
226
# File 'lib/unit_measurements/rails/active_record.rb', line 219

def redefine_unit_writer(unit_attr, unit_group)
  redefine_method("#{unit_attr}=") do |unit|
    column_exists?(unit_attr)

    unit_name = unit_group.unit_for(unit).try!(:name)
    write_attribute(unit_attr, (unit_name || unit))
  end
end

.validate_unit_group!(unit_group) ⇒ void (private)

This method returns an undefined value.

Validates whether unit_group is a subclass of UnitMeasurements::Measurement.

Parameters:

  • unit_group (Class)

    The unit group class to be validated.

Raises:

  • (BaseError)

    if unit group is not a subclass of UnitMeasurements::Measurement.

Author:

Since:

  • 1.0.0



122
123
124
125
126
# File 'lib/unit_measurements/rails/active_record.rb', line 122

def validate_unit_group!(unit_group)
  unless unit_group.is_a?(Class) && unit_group.ancestors.include?(Measurement)
    raise BaseError, "Expecting `#{unit_group}` to be a subclass of UnitMeasurements::Measurement"
  end
end