master
1class Quantity
2 attr_reader :amount, :unit
3
4 def initialize(amount, unit)
5 @amount = amount
6 @unit = UnitOfMeasure.for(unit)
7 end
8
9 def to(target_unit)
10 Quantity.new(
11 UnitOfMeasure.for(target_unit).convert(amount, unit),
12 target_unit
13 )
14 end
15
16 def to_f
17 @amount.to_f
18 end
19
20 def +(other)
21 Quantity.new(amount + amount_from(other), unit)
22 end
23
24 def -(other)
25 Quantity.new(amount - amount_from(other), unit)
26 end
27
28 def /(other)
29 Quantity.new(amount / amount_from(other), unit)
30 end
31
32 def *(other)
33 Quantity.new(amount * amount_from(other), unit)
34 end
35
36 def >(other)
37 amount > amount_from(other)
38 end
39
40 def >=(other)
41 self.>(other) || eql?(other)
42 end
43
44 def <(other)
45 amount < amount_from(other)
46 end
47
48 def coerce(other)
49 [self, other]
50 end
51
52 def hash
53 amount.hash + unit.class.hash
54 end
55
56 def eql?(other, delta = 0.1)
57 (amount - amount_from(other)).abs <= delta
58 end
59
60 def ==(other)
61 eql?(other)
62 end
63
64 def to_s
65 to_f.to_s
66 end
67
68 def pretty_print
69 "#{to_f} #{unit}"
70 end
71
72 def to_h
73 { amount: amount, unit: unit.to_s }
74 end
75
76 def to_hash
77 to_h
78 end
79
80 private
81
82 def amount_from(quantity)
83 quantity.respond_to?(:to) ? quantity.to(unit).amount : quantity
84 end
85
86 class UnitOfMeasure
87 def self.for(unit)
88 case unit
89 when :lbs, :lb
90 Pound.new
91 when :kg, :kgs
92 Kilogram.new
93 else
94 unit
95 end
96 end
97 end
98
99 class Pound < UnitOfMeasure
100 def convert(amount, unit)
101 case unit
102 when Kilogram
103 amount * 2.20462
104 else
105 amount
106 end
107 end
108
109 def to_s
110 "lbs"
111 end
112 end
113
114 class Kilogram < UnitOfMeasure
115 def convert(amount, unit)
116 case unit
117 when Pound
118 amount * 0.453592
119 else
120 amount
121 end
122 end
123
124 def to_s
125 "kg"
126 end
127 end
128end