Class: Weighable::Weight

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/weighable/weight.rb

Constant Summary collapse

UNIT =
{
  gram:        0,
  ounce:       1,
  pound:       2,
  milligram:   3,
  kilogram:    4,
  unit:        5,
  fluid_ounce: 6
}.freeze
UNIT_ABBREVIATION =
{
  gram:        'g',
  ounce:       'oz',
  pound:       'lb',
  milligram:   'mg',
  kilogram:    'kg',
  fluid_ounce: 'fl oz',
  unit:        nil
}.freeze
ABBREVIATION_ALIASES =
{
  'g'     => :gram,
  'oz'    => :ounce,
  'lb'    => :pound,
  'mg'    => :milligram,
  'kg'    => :kilogram,
  'fl oz' => :fluid_ounce,
  'ea'    => :unit,
  'each'  => :unit,
  nil     => :unit
}.freeze
GRAMS_PER_OUNCE =
BigDecimal('28.34952')
GRAMS_PER_POUND =
BigDecimal('453.59237')
OUNCES_PER_POUND =
BigDecimal('16')
MILLIGRAMS_PER_GRAM =
BigDecimal('1000')
KILOGRAMS_PER_GRAM =
BigDecimal('0.001')
IDENTITY =
BigDecimal('1')
MILLIGRAMS_PER_OUNCE =
GRAMS_PER_OUNCE * MILLIGRAMS_PER_GRAM
KILOGRAMS_PER_OUNCE =
GRAMS_PER_OUNCE * KILOGRAMS_PER_GRAM
MILLIGRAMS_PER_POUND =
GRAMS_PER_POUND * MILLIGRAMS_PER_GRAM
KILOGRAMS_PER_POUND =
GRAMS_PER_POUND * KILOGRAMS_PER_GRAM
MILLIGRAMS_PER_KILOGRAM =
MILLIGRAMS_PER_GRAM**2
FLUID_OUNCE_PER_OUNCE =
IDENTITY
CONVERSIONS =
{
  UNIT[:unit] => {
    UNIT[:unit] => [:*, IDENTITY]
  },
  UNIT[:gram] => {
    UNIT[:gram]        => [:*, IDENTITY],
    UNIT[:ounce]       => [:/, GRAMS_PER_OUNCE],
    UNIT[:pound]       => [:/, GRAMS_PER_POUND],
    UNIT[:milligram]   => [:*, MILLIGRAMS_PER_GRAM],
    UNIT[:kilogram]    => [:*, KILOGRAMS_PER_GRAM],
    UNIT[:fluid_ounce] => [:/, GRAMS_PER_OUNCE]
  },
  UNIT[:ounce] => {
    UNIT[:gram]      => [:*, GRAMS_PER_OUNCE],
    UNIT[:ounce]     => [:*, IDENTITY],
    UNIT[:pound]     => [:/, OUNCES_PER_POUND],
    UNIT[:milligram] => [:*, MILLIGRAMS_PER_OUNCE],
    UNIT[:kilogram]  => [:*, KILOGRAMS_PER_OUNCE],
    UNIT[:fluid_ounce] => [:*, IDENTITY]
  },
  UNIT[:pound] => {
    UNIT[:gram]      => [:*, GRAMS_PER_POUND],
    UNIT[:ounce]     => [:*, OUNCES_PER_POUND],
    UNIT[:pound]     => [:*, IDENTITY],
    UNIT[:milligram] => [:*, MILLIGRAMS_PER_POUND],
    UNIT[:kilogram]  => [:*, KILOGRAMS_PER_POUND],
    UNIT[:fluid_ounce] => [:*, OUNCES_PER_POUND]
  },
  UNIT[:milligram] => {
    UNIT[:gram]      => [:/, MILLIGRAMS_PER_GRAM],
    UNIT[:ounce]     => [:/, MILLIGRAMS_PER_OUNCE],
    UNIT[:pound]     => [:/, MILLIGRAMS_PER_POUND],
    UNIT[:milligram] => [:*, IDENTITY],
    UNIT[:kilogram]  => [:/, MILLIGRAMS_PER_KILOGRAM],
    UNIT[:fluid_ounce] => [:/, MILLIGRAMS_PER_OUNCE]
  },
  UNIT[:kilogram] => {
    UNIT[:gram]      => [:/, KILOGRAMS_PER_GRAM],
    UNIT[:ounce]     => [:/, KILOGRAMS_PER_OUNCE],
    UNIT[:pound]     => [:/, KILOGRAMS_PER_POUND],
    UNIT[:milligram] => [:*, MILLIGRAMS_PER_KILOGRAM],
    UNIT[:kilogram]  => [:*, IDENTITY],
    UNIT[:fluid_ounce] => [:/, KILOGRAMS_PER_OUNCE]
  },
  UNIT[:fluid_ounce] => {
    UNIT[:fluid_ounce] => [:*, IDENTITY],
    UNIT[:gram]        => [:*, GRAMS_PER_OUNCE],
    UNIT[:ounce]       => [:*, IDENTITY],
    UNIT[:pound]       => [:/, OUNCES_PER_POUND],
    UNIT[:milligram]   => [:*, MILLIGRAMS_PER_OUNCE],
    UNIT[:kilogram]    => [:*, KILOGRAMS_PER_OUNCE],
  }
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value, unit) ⇒ Weight

Returns a new instance of Weight.



133
134
135
136
# File 'lib/weighable/weight.rb', line 133

def initialize(value, unit)
  @value = value.to_d
  @unit  = unit.is_a?(Fixnum) ? unit : unit_from_symbol(unit.to_sym)
end

Instance Attribute Details

#unitObject (readonly)

Returns the value of attribute unit.



7
8
9
# File 'lib/weighable/weight.rb', line 7

def unit
  @unit
end

#valueObject (readonly)

Returns the value of attribute value.



7
8
9
# File 'lib/weighable/weight.rb', line 7

def value
  @value
end

Class Method Details

.from_value_and_unit(value, unit) ⇒ Object



121
122
123
124
125
# File 'lib/weighable/weight.rb', line 121

def self.from_value_and_unit(value, unit)
  unit = parse_unit(unit)
  fail ArgumentError, 'invalid weight' if unit.nil? || value.to_s.strip.empty?
  Weight.new(value, unit)
end

.parse(string) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
# File 'lib/weighable/weight.rb', line 109

def self.parse(string)
  trimmed = string.strip
  unit_start = trimmed.index(' ')
  if unit_start
    unit = trimmed.slice!(unit_start + 1..-1)
    value = trimmed.slice!(0..unit_start - 1)
    from_value_and_unit(value, unit)
  else
    from_value_and_unit(trimmed, nil)
  end
end

.parse_unit(unit) ⇒ Object



127
128
129
130
131
# File 'lib/weighable/weight.rb', line 127

def self.parse_unit(unit)
  unit = ActiveSupport::Inflector.singularize(unit.downcase) unless unit.nil?
  unit_symbol = unit ? unit.tr(' ', '_').to_sym : unit
  UNIT[unit_symbol] || ABBREVIATION_ALIASES[unit]
end

Instance Method Details

#*(other) ⇒ Object



178
179
180
# File 'lib/weighable/weight.rb', line 178

def *(other)
  Weight.new(@value * to_math_value(other), unit_name)
end

#+(other) ⇒ Object



168
169
170
171
# File 'lib/weighable/weight.rb', line 168

def +(other)
  other = other.to(unit_name)
  Weight.new(@value + other.value, unit_name)
end

#-(other) ⇒ Object



173
174
175
176
# File 'lib/weighable/weight.rb', line 173

def -(other)
  other = other.to(unit_name)
  Weight.new(@value - other.value, unit_name)
end

#/(other) ⇒ Object



182
183
184
185
186
187
188
189
# File 'lib/weighable/weight.rb', line 182

def /(other)
  if !other.is_a?(Weight) || (other.is_unit? && other.unit != @unit)
    Weight.new(@value / to_math_value(other), unit_name)
  else
    other = other.to(unit_name)
    BigDecimal(@value / other.value)
  end
end

#<(other) ⇒ Object



195
196
197
198
# File 'lib/weighable/weight.rb', line 195

def <(other)
  other = other.to(unit_name)
  @value < other.value
end

#<=(other) ⇒ Object



200
201
202
203
# File 'lib/weighable/weight.rb', line 200

def <=(other)
  other = other.to(unit_name)
  @value <= other.value
end

#<=>(other) ⇒ Object



215
216
217
218
# File 'lib/weighable/weight.rb', line 215

def <=>(other)
  other = other.to(unit_name)
  @value <=> other.value
end

#==(other) ⇒ Object



191
192
193
# File 'lib/weighable/weight.rb', line 191

def ==(other)
  other.class == self.class && other.value == @value && other.unit == @unit
end

#>(other) ⇒ Object



205
206
207
208
# File 'lib/weighable/weight.rb', line 205

def >(other)
  other = other.to(unit_name)
  @value > other.value
end

#>=(other) ⇒ Object



210
211
212
213
# File 'lib/weighable/weight.rb', line 210

def >=(other)
  other = other.to(unit_name)
  @value >= other.value
end

#absObject



232
233
234
# File 'lib/weighable/weight.rb', line 232

def abs
  Weight.new(@value.abs, @unit)
end

#ceil(precision = 0) ⇒ Object



224
225
226
# File 'lib/weighable/weight.rb', line 224

def ceil(precision = 0)
  Weight.new(@value.ceil(precision), @unit)
end

#floor(precision = 0) ⇒ Object



228
229
230
# File 'lib/weighable/weight.rb', line 228

def floor(precision = 0)
  Weight.new(@value.floor(precision), @unit)
end

#round(precision = 0) ⇒ Object



220
221
222
# File 'lib/weighable/weight.rb', line 220

def round(precision = 0)
  Weight.new(@value.round(precision), @unit)
end

#to(unit) ⇒ Object



138
139
140
141
142
143
# File 'lib/weighable/weight.rb', line 138

def to(unit)
  new_unit = unit.is_a?(Fixnum) ? unit : unit_from_symbol(unit.to_sym)
  operator, conversion = conversion(@unit, new_unit)
  new_value = @value.public_send(operator, conversion)
  Weight.new(new_value, unit)
end

#to_s(only_unit: false) ⇒ Object



159
160
161
162
163
164
165
166
# File 'lib/weighable/weight.rb', line 159

def to_s(only_unit: false)
  if only_unit
    unit_abbreviation.to_s
  else
    value = @value.to_f == @value.to_i ? @value.to_i : @value.to_f
    [value, unit_abbreviation].compact.join(' ')
  end
end

#zero?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'lib/weighable/weight.rb', line 236

def zero?
  @value.zero?
end