summaryrefslogtreecommitdiff
path: root/app/models/quantity.rb
blob: 1571adc93b957c6c7c8f41a0e89e5484cce00474 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class Quantity
  attr_reader :amount, :unit

  def initialize(amount, unit)
    @amount = amount
    @unit = UnitOfMeasure.for(unit)
  end

  def to(target_unit)
    Quantity.new(
      UnitOfMeasure.for(target_unit).convert(amount, unit),
      target_unit
    )
  end

  def to_f
    @amount.to_f
  end

  def +(other)
    Quantity.new(amount + amount_from(other), unit)
  end

  def -(other)
    Quantity.new(amount - amount_from(other), unit)
  end

  def /(other)
    Quantity.new(amount / amount_from(other), unit)
  end

  def *(other)
    Quantity.new(amount * amount_from(other), unit)
  end

  def >(other)
    amount > amount_from(other)
  end

  def >=(other)
    self.>(other) || eql?(other)
  end

  def <(other)
    amount < amount_from(other)
  end

  def coerce(other)
    [self, other]
  end

  def hash
    amount.hash + unit.class.hash
  end

  def eql?(other, delta = 0.1)
    (amount - amount_from(other)).abs <= delta
  end

  def ==(other)
    eql?(other)
  end

  def to_s
    to_f.to_s
  end

  def pretty_print
    "#{to_f} #{unit}"
  end

  def to_h
    { amount: amount, unit: unit.to_s }
  end

  def to_hash
    to_h
  end

  private

  def amount_from(quantity)
    quantity.respond_to?(:to) ? quantity.to(unit).amount : quantity
  end

  class UnitOfMeasure
    def self.for(unit)
      case unit
      when :lbs, :lb
        Pound.new
      when :kg, :kgs
        Kilogram.new
      else
        unit
      end
    end
  end

  class Pound < UnitOfMeasure
    def convert(amount, unit)
      case unit
      when Kilogram
        amount * 2.20462
      else
        amount
      end
    end

    def to_s
      "lbs"
    end
  end

  class Kilogram < UnitOfMeasure
    def convert(amount, unit)
      case unit
      when Pound
        amount * 0.453592
      else
        amount
      end
    end

    def to_s
      "kg"
    end
  end
end