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
|