-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrate_converter.rb
102 lines (88 loc) · 2.63 KB
/
rate_converter.rb
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
require "rubygems"
require "bigdecimal"
require "nokogiri"
require "set"
class RateConverter
attr_reader :conversions, :all_conversions
def initialize(xml)
@conversions = parse_xml(xml)
@all_conversions = calculate_usd_conversions(@conversions)
end
def method_missing(method, *args, &block)
if method.to_s =~ /to_([A-Z]{3})$/
currency = $1
@all_conversions["USD"][currency]
else
super
end
end
def to_USD(currency)
if currency == "USD"
BigDecimal("1.0")
else
@all_conversions["USD"][currency]
end
end
def to_s
str = ""
@all_conversions["USD"].keys.each do |currency|
rate = @all_conversions["USD"][currency].to_f
str << "#{currency} -> USD : #{rate}\n"
end
str
end
private
def parse_xml(xml)
doc = Nokogiri::XML(xml)
conversions ||= {}
doc.xpath("/rates/rate").each do |rate|
from = rate.xpath("from")[0].content
to = rate.xpath("to")[0].content
rate = BigDecimal.new(rate.xpath("conversion")[0].content)
add_conversion(conversions, from, to, rate)
end
conversions
# add_supplements(conversions)
end
def add_supplements(conversions)
conversions.each do |from, to_rate|
to_rate.each do |to, rate|
unless conversions[to][from]
conversions[to][from] = BigDecimal.new("1.0") / rate
end
end
end
conversions
end
def add_conversion(conversions, from, to, rate)
from_rate = conversions[to] || {}
if from_rate[from].nil?
from_rate[from] = rate
end
conversions[to] = from_rate
end
def calculate_usd_conversions(conversions)
all_conversions = {}
all_conversions["USD"] = conversions["USD"].clone
ring = Set.new(conversions["USD"].keys)
visited = Set.new(["USD"])
visited.merge ring
while ring.size > 0
new_ring = Set.new
ring.each do |currency|
conversions[currency].each do |from, rate|
unless visited.include? from
all_conversions["USD"][from] = all_conversions["USD"][currency] * rate
new_ring << from
visited << from
end
end
end
ring = new_ring
end
all_conversions
end
def adjust_precision(f)
(f * 10000).to_i / 10000.0
end
end