ecb-currency_converter 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +29 -7
- data/ecb-currency_converter.gemspec +2 -1
- data/lib/ecb/currency_converter/exceptions.rb +17 -6
- data/lib/ecb/currency_converter/version.rb +1 -1
- data/lib/ecb/currency_converter.rb +157 -28
- data/spec/ecb/currency_converter_spec.rb +173 -61
- data/spec/fixtures/eurofxref-hist-90d.xml +2144 -0
- data/spec/spec_helper.rb +11 -2
- metadata +22 -18
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ODIyZmJiZWIxOGNhYTMyZDBkNjE4ZmYyOWQ3ZmNmNzUyZDBhMjJmMg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MjQxYTA2YjBkYzE0N2M0NWQ1MmUwODkyNjgzMzgzYTNiNTE1MDA1Ng==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YzY5YWZiYTg3YTViY2MyNDAxMjgzZDVmYzE0NGU2YTRlN2VmZmU3MGViZDdl
|
10
|
+
Y2MxOTJiMTY0MDE0MWExNDBkYjA1NDY5ZmFhODU4ZTU5NDVhNTI0OTg0MDJj
|
11
|
+
Y2U3ZTFiMjgwZjcwMzVkZDg1NTE0NjA1NzJkNjNmNzNmYzA5NTQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MGE1OTQ3OTQyODNkZDY4ZTUzZDE4MmEzYmQxODAyYTg4ZTk4YjA5ZjE4MWJh
|
14
|
+
ZWVlMDg5Y2Y3MTdiNWYxYzk2N2QyMjY3NmNkZTMwMjkzZjY1ZmI3ZjM5MjBm
|
15
|
+
N2MwNzZhZjc0ZTkzMWQzMzk5YmEwNDM1NTgyNTRhMDdmMmExNzk=
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# ECB::CurrencyConverter
|
2
2
|
|
3
|
-
Currency Conversion using the European Central Bank's Euro foreign exchange
|
3
|
+
Currency Conversion using the European Central Bank's Euro foreign exchange
|
4
|
+
reference rates. All calculations are performed and returned in +BigDecimal+.
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -20,19 +21,40 @@ Or install it yourself as:
|
|
20
21
|
|
21
22
|
To convert between two currencies:
|
22
23
|
|
23
|
-
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD')
|
24
|
-
=> 133.
|
24
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD').to_f
|
25
|
+
=> 133.74
|
25
26
|
|
26
|
-
ECB::CurrencyConverter.exchange(100, 'USD', 'EUR')
|
27
|
+
ECB::CurrencyConverter.exchange(100, 'USD', 'EUR').to_f
|
27
28
|
=> 74.77194556602363
|
28
29
|
|
30
|
+
To convert between two currencies using historical data:
|
31
|
+
|
32
|
+
date = Date.parse('2013-05-17')
|
33
|
+
|
34
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD', date).to_f
|
35
|
+
=> 128.69
|
36
|
+
|
37
|
+
ECB::CurrencyConverter.exchange(100, 'USD', 'EUR', date).to_f
|
38
|
+
=> 77.70611547128759
|
39
|
+
|
40
|
+
|
29
41
|
To get the most recent exchange rate between two currencies:
|
30
42
|
|
31
|
-
ECB::CurrencyConverter.rate('EUR', 'USD')
|
43
|
+
ECB::CurrencyConverter.rate('EUR', 'USD').to_f
|
32
44
|
=> 1.3374
|
33
45
|
|
34
|
-
ECB::CurrencyConverter.rate('USD', 'EUR')
|
35
|
-
=> 0.
|
46
|
+
ECB::CurrencyConverter.rate('USD', 'EUR').to_f
|
47
|
+
=> 0.7477194556602362
|
48
|
+
|
49
|
+
To get the historical exchange rate between two currencies:
|
50
|
+
|
51
|
+
date = Date.parse('2013-05-17')
|
52
|
+
|
53
|
+
ECB::CurrencyConverter.rate('EUR', 'USD', date).to_f
|
54
|
+
=> 1.2869
|
55
|
+
|
56
|
+
ECB::CurrencyConverter.rate('USD', 'EUR', date).to_f
|
57
|
+
=> 0.777061154712876
|
36
58
|
|
37
59
|
## Contributing
|
38
60
|
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['developer@sepcot.com']
|
11
11
|
spec.description = %q{Currency Conversion using the European Central Bank's Euro foreign exchange reference rates.}
|
12
12
|
spec.summary = %q{Currency Conversion using the European Central Bank's Euro foreign exchange reference rates.}
|
13
|
-
spec.homepage = ''
|
13
|
+
spec.homepage = 'https://github.com/msepcot/ecb-currency_converter'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency 'fakeweb'
|
23
23
|
spec.add_development_dependency 'rake'
|
24
24
|
spec.add_development_dependency 'rspec'
|
25
|
+
spec.add_development_dependency 'timecop'
|
25
26
|
|
26
27
|
spec.add_dependency 'httparty', '~> 0.11.0'
|
27
28
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module ECB # :nodoc:
|
2
|
-
# = ECB
|
2
|
+
# = ECB Missing Date Error
|
3
3
|
#
|
4
|
-
# Raised when
|
4
|
+
# Raised when the data for a date is +nil+.
|
5
5
|
#
|
6
|
-
# * +
|
7
|
-
class
|
8
|
-
def initialize(
|
9
|
-
super("#{
|
6
|
+
# * +date+ - the missing date.
|
7
|
+
class MissingDateError < StandardError
|
8
|
+
def initialize(date)
|
9
|
+
super("Foreign exchange reference rate for #{date.to_s} is missing.")
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -20,4 +20,15 @@ module ECB # :nodoc:
|
|
20
20
|
super("Foreign exchange reference rate for #{currency_code} is missing.")
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
# = ECB Unknown Currency Error
|
25
|
+
#
|
26
|
+
# Raised when we try to grab data for an unsupported currency code.
|
27
|
+
#
|
28
|
+
# * +currency_code+ - the unsupported ISO 4217 Currency Code.
|
29
|
+
class UnknownCurrencyError < StandardError
|
30
|
+
def initialize(currency_code)
|
31
|
+
super("#{currency_code} is not supported.")
|
32
|
+
end
|
33
|
+
end
|
23
34
|
end
|
@@ -1,9 +1,46 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'bigdecimal/util'
|
3
|
+
require 'date'
|
1
4
|
require 'ecb/currency_converter/exceptions'
|
2
5
|
require 'ecb/currency_converter/version'
|
3
6
|
require 'httparty'
|
4
7
|
|
5
|
-
|
6
|
-
|
8
|
+
# = European Central Bank
|
9
|
+
#
|
10
|
+
# The reference rates are usually updated by 3 p.m. CET. They are based on a
|
11
|
+
# regular daily concertation procedure between central banks across Europe and
|
12
|
+
# worldwide, which normally takes place at 2.15 p.m. CET.
|
13
|
+
#
|
14
|
+
# The procedure for computation of Euro Foreign Exchange Reference Rates is
|
15
|
+
# stated in: http://www.ecb.int/press/pr/date/2010/html/pr101203.en.html
|
16
|
+
#
|
17
|
+
# The current procedure for the computation and publication of the foreign
|
18
|
+
# exchange reference rates will also apply to the currency that is to be added
|
19
|
+
# to the list:
|
20
|
+
#
|
21
|
+
# * The reference rates are based on the daily concertation procedure between
|
22
|
+
# central banks within and outside the European System of Central Banks,
|
23
|
+
# which normally takes place at 2.15 p.m. CET. The reference exchange rates
|
24
|
+
# are published both by electronic market information providers and on the
|
25
|
+
# ECB's website shortly after the concertation procedure has been completed.
|
26
|
+
# * Only one reference exchange rate (i.e. the mid-rate) is published for each
|
27
|
+
# currency, using the "certain" method (i.e. EUR 1 = x foreign currency
|
28
|
+
# units).
|
29
|
+
# * The number of significant digits used may vary according to the currency,
|
30
|
+
# reflecting market conventions. In most cases, however, five significant
|
31
|
+
# digits are used.
|
32
|
+
# * The euro area national central banks may publish more comprehensive lists
|
33
|
+
# of euro reference exchange rates than that published by the ECB.
|
34
|
+
#
|
35
|
+
# The ECB pays due attention to ensuring that the published exchange rates
|
36
|
+
# reflect the market conditions prevailing at the time of the daily
|
37
|
+
# concertation procedure. Since the exchange rates of the above currencies
|
38
|
+
# against the euro are averages of buying and selling rates, they do not
|
39
|
+
# necessarily reflect the rates at which actual market transactions have been
|
40
|
+
# made. The exchange rates against the euro published by the ECB are released
|
41
|
+
# for reference purposes only.
|
42
|
+
module ECB
|
43
|
+
# = Currency Converter
|
7
44
|
#
|
8
45
|
# Convert values or find the most recent foreign exchange reference rate
|
9
46
|
# between any of the +CURRENCIES+ supported by the European Central Bank.
|
@@ -13,8 +50,12 @@ module ECB # :nodoc:
|
|
13
50
|
#
|
14
51
|
# http://www.ecb.int/stats/exchange/eurofxref/html/index.en.html
|
15
52
|
#
|
53
|
+
# All calculations are performed using +BigDecimal+.
|
16
54
|
module CurrencyConverter
|
17
|
-
|
55
|
+
BASE_URI = 'http://www.ecb.europa.eu/stats/eurofxref'
|
56
|
+
DAILY_URI = "#{BASE_URI}/eurofxref-daily.xml"
|
57
|
+
NINTY_URI = "#{BASE_URI}/eurofxref-hist-90d.xml"
|
58
|
+
|
18
59
|
CURRENCIES = [
|
19
60
|
'AUD', # Australian Dollar (A$)
|
20
61
|
'BGN', # Bulgarian Lev (BGN)
|
@@ -55,52 +96,140 @@ module ECB # :nodoc:
|
|
55
96
|
class << self # Class methods
|
56
97
|
# Converts the +value+ between +from+ and +to+ currencies.
|
57
98
|
#
|
58
|
-
# = Example
|
99
|
+
# = Example (Date.today == 2013-06-18)
|
59
100
|
#
|
60
|
-
# ECB::CurrencyConverter.exchange(100, 'EUR', 'USD')
|
61
|
-
# => 133.
|
101
|
+
# ECB::CurrencyConverter.exchange(100, 'EUR', 'USD').to_f
|
102
|
+
# => 133.74
|
62
103
|
#
|
63
|
-
# ECB::CurrencyConverter.exchange(100, 'USD', 'EUR')
|
104
|
+
# ECB::CurrencyConverter.exchange(100, 'USD', 'EUR').to_f
|
64
105
|
# => 74.77194556602363
|
65
|
-
|
66
|
-
|
106
|
+
#
|
107
|
+
# = Historical Example (2013-05-17)
|
108
|
+
#
|
109
|
+
# date = Date.parse('2013-05-17')
|
110
|
+
#
|
111
|
+
# ECB::CurrencyConverter.exchange(100, 'EUR', 'USD', date).to_f
|
112
|
+
# => 128.69
|
113
|
+
#
|
114
|
+
# ECB::CurrencyConverter.exchange(100, 'USD', 'EUR', date).to_f
|
115
|
+
# => 77.70611547128759
|
116
|
+
def exchange(value, from, to, date = Date.today)
|
117
|
+
value.to_d * rate(from, to, date)
|
67
118
|
end
|
68
119
|
|
69
120
|
# Return the exchange rate between +from+ and +to+ currencies.
|
70
121
|
#
|
71
|
-
# = Example
|
122
|
+
# = Example (Date.today == 2013-06-18)
|
72
123
|
#
|
73
|
-
# ECB::CurrencyConverter.rate('EUR', 'USD')
|
124
|
+
# ECB::CurrencyConverter.rate('EUR', 'USD').to_f
|
74
125
|
# => 1.3374
|
75
126
|
#
|
76
|
-
# ECB::CurrencyConverter.rate('USD', 'EUR')
|
77
|
-
# => 0.
|
78
|
-
|
79
|
-
|
80
|
-
|
127
|
+
# ECB::CurrencyConverter.rate('USD', 'EUR').to_f
|
128
|
+
# => 0.7477194556602362
|
129
|
+
#
|
130
|
+
# = Historical Example (2013-05-17)
|
131
|
+
#
|
132
|
+
# date = Date.parse('2013-05-17')
|
133
|
+
#
|
134
|
+
# ECB::CurrencyConverter.rate('EUR', 'USD', date).to_f
|
135
|
+
# => 1.2869
|
136
|
+
#
|
137
|
+
# ECB::CurrencyConverter.rate('USD', 'EUR', date).to_f
|
138
|
+
# => 0.777061154712876
|
139
|
+
def rate(from, to, date = Date.today)
|
140
|
+
load_data!(date)
|
141
|
+
date = closest_available_date(date)
|
142
|
+
validate(from, to, date)
|
143
|
+
|
144
|
+
BigDecimal('1.0') / data_for(from, date) * data_for(to, date)
|
145
|
+
end
|
81
146
|
|
82
|
-
|
147
|
+
# Silence warning messages that can arise from +closest_available_date+
|
148
|
+
def silence_warnings(silence = true)
|
149
|
+
@silence_warnings = silence
|
83
150
|
end
|
84
151
|
private
|
152
|
+
# Find the closest date with available data.
|
153
|
+
#
|
154
|
+
# Returns the requested date if data is available. If not, we search back
|
155
|
+
# up to five days to see if there is available data. This is primarily
|
156
|
+
# used to handle weekends and holidays when ECB does not publish data. We
|
157
|
+
# will print a warning message (see +silence_warnings+) if we are using
|
158
|
+
# a date other than the one specified.
|
159
|
+
def closest_available_date(date)
|
160
|
+
closest = date.to_date
|
161
|
+
5.times do
|
162
|
+
if @euro.key?(closest.to_s)
|
163
|
+
unless closest == date || @silence_warnings
|
164
|
+
warn "Foreign exchange reference rate for #{date.to_s} " \
|
165
|
+
"is not available, using rate for #{closest.to_s}."
|
166
|
+
end
|
167
|
+
return closest
|
168
|
+
end
|
169
|
+
closest = closest - 1
|
170
|
+
end
|
171
|
+
raise MissingDateError.new(date)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Grab the exchange rate for a currency on a particular date.
|
175
|
+
def data_for(currency, date)
|
176
|
+
@euro[date.to_s][currency] || BigDecimal('0')
|
177
|
+
end
|
178
|
+
|
179
|
+
# Load exchange rate data based on the date requested.
|
180
|
+
#--
|
181
|
+
# NOTE: should we just load the 90-day historical file? It includes the
|
182
|
+
# most recent date, but takes 2-3x the time to load (.6 or .9 seconds,
|
183
|
+
# compared to .3 for the daily feed in local tests).
|
184
|
+
# NOTE: this loads the 90-day historical file even if the requested date
|
185
|
+
# is outside the 90-day window.
|
186
|
+
#++
|
187
|
+
def load_data!(date)
|
188
|
+
if date.to_date == Date.today
|
189
|
+
@euro ||= load_daily
|
190
|
+
elsif !defined?(@euro) || @euro.size < 2
|
191
|
+
@euro = load_ninty
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
85
195
|
# Load data from European Central Bank's daily XML feed.
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
196
|
+
def load_daily
|
197
|
+
response = HTTParty.get(DAILY_URI, format: :xml)
|
198
|
+
load(response['Envelope']['Cube']['Cube'])
|
199
|
+
end
|
200
|
+
|
201
|
+
# Load data from European Central Bank's historical 90-day XML feed.
|
202
|
+
def load_ninty
|
203
|
+
data = {}
|
204
|
+
|
205
|
+
response = HTTParty.get(NINTY_URI, format: :xml)
|
206
|
+
response['Envelope']['Cube']['Cube'].each do |cube|
|
207
|
+
data.merge!(load(cube))
|
94
208
|
end
|
209
|
+
|
210
|
+
data
|
211
|
+
end
|
212
|
+
|
213
|
+
# Load one +Cube+ worth of data.
|
214
|
+
def load(hash)
|
215
|
+
date = hash['time']
|
216
|
+
data = hash['Cube']
|
217
|
+
data = data.reduce({}) do |hash, element|
|
218
|
+
hash[element['currency']] = BigDecimal.new(element['rate'])
|
219
|
+
hash
|
220
|
+
end
|
221
|
+
data['EUR'] = BigDecimal('1.0')
|
222
|
+
|
223
|
+
{ date => data }
|
95
224
|
end
|
96
225
|
|
97
226
|
# Raise errors for invalid currencies or missing data.
|
98
|
-
def validate(from, to)
|
227
|
+
def validate(from, to, date)
|
99
228
|
raise UnknownCurrencyError.new(from) unless CURRENCIES.include?(from)
|
100
229
|
raise UnknownCurrencyError.new(to) unless CURRENCIES.include?(to)
|
101
230
|
|
102
|
-
raise MissingExchangeRateError.new(from) if
|
103
|
-
raise MissingExchangeRateError.new(to) if
|
231
|
+
raise MissingExchangeRateError.new(from) if data_for(from, date).zero?
|
232
|
+
raise MissingExchangeRateError.new(to) if data_for(to, date).zero?
|
104
233
|
end
|
105
234
|
end
|
106
235
|
end
|
@@ -1,17 +1,21 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
2
|
|
3
|
-
def rate_comparison(from, to, actual)
|
4
|
-
ECB::CurrencyConverter.rate(from, to).should
|
3
|
+
def rate_comparison(from, to, date, actual)
|
4
|
+
ECB::CurrencyConverter.rate(from, to, date).should(
|
5
|
+
be_within(0.0001).of(actual)
|
6
|
+
)
|
5
7
|
end
|
6
8
|
|
7
|
-
def exchange_comparison(amount, from, to, actual)
|
8
|
-
ECB::CurrencyConverter.exchange(amount, from, to).should(
|
9
|
+
def exchange_comparison(amount, from, to, date, actual)
|
10
|
+
ECB::CurrencyConverter.exchange(amount, from, to, date).should(
|
9
11
|
be_within(0.0001).of(actual)
|
10
12
|
)
|
11
13
|
end
|
12
14
|
|
13
15
|
describe ECB::CurrencyConverter do
|
14
16
|
describe '#exchange' do
|
17
|
+
let(:date) { Date.today }
|
18
|
+
|
15
19
|
it 'should convert between currencies through the EUR' do
|
16
20
|
[ ['AUD', 'LTL', 244.34222], ['BGN', 'LVL', 35.87278],
|
17
21
|
['BRL', 'MXN', 591.82960], ['CAD', 'MYR', 309.46094],
|
@@ -23,12 +27,33 @@ describe ECB::CurrencyConverter do
|
|
23
27
|
['ILS', 'TRY', 52.38214], ['INR', 'USD', 1.70152],
|
24
28
|
['JPY', 'ZAR', 10.51728],
|
25
29
|
].each do |from, to, actual|
|
26
|
-
exchange_comparison(100, from, to, actual)
|
30
|
+
exchange_comparison(100, from, to, date, actual)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#exchange(historical)' do
|
36
|
+
let(:date) { Date.parse('2013-05-17') }
|
37
|
+
|
38
|
+
it 'should convert between currencies through the EUR' do
|
39
|
+
[ ['AUD', 'LTL', 261.25907], ['BGN', 'LVL', 35.75518],
|
40
|
+
['BRL', 'MXN', 606.90804], ['CAD', 'MYR', 294.17549],
|
41
|
+
['CHF', 'NOK', 605.61490], ['CNY', 'NZD', 20.13790],
|
42
|
+
['CZK', 'PHP', 203.79391], ['DKK', 'PLN', 55.96049],
|
43
|
+
['EUR', 'KRW', 143784.00], ['GBP', 'RON', 513.40633],
|
44
|
+
['HKD', 'RUB', 404.23576], ['HRK', 'SEK', 113.48831],
|
45
|
+
['HUF', 'SGD', 0.55602], ['IDR', 'THB', 0.30525],
|
46
|
+
['ILS', 'TRY', 50.16654], ['INR', 'USD', 1.82264],
|
47
|
+
['JPY', 'ZAR', 9.15780],
|
48
|
+
].each do |from, to, actual|
|
49
|
+
exchange_comparison(100, from, to, date, actual)
|
27
50
|
end
|
28
51
|
end
|
29
52
|
end
|
30
53
|
|
31
54
|
describe '#rate' do
|
55
|
+
let(:date) { Date.today }
|
56
|
+
|
32
57
|
it 'should match the provided rates when converting from EUR' do
|
33
58
|
[ ['AUD', 1.4131], ['BGN', 1.9558], ['BRL', 2.9203],
|
34
59
|
['CAD', 1.3635], ['CHF', 1.2315], ['CNY', 8.1963],
|
@@ -42,7 +67,7 @@ describe ECB::CurrencyConverter do
|
|
42
67
|
['SEK', 8.6753], ['SGD', 1.6855], ['THB', 41.259],
|
43
68
|
['TRY', 2.5167], ['USD', 1.3374], ['ZAR', 13.4148]
|
44
69
|
].each do |currency, value|
|
45
|
-
rate_comparison('EUR', currency, value)
|
70
|
+
rate_comparison('EUR', currency, date, value)
|
46
71
|
end
|
47
72
|
end
|
48
73
|
|
@@ -59,12 +84,12 @@ describe ECB::CurrencyConverter do
|
|
59
84
|
['SEK', 8.6753], ['SGD', 1.6855], ['THB', 41.259],
|
60
85
|
['TRY', 2.5167], ['USD', 1.3374], ['ZAR', 13.4148]
|
61
86
|
].each do |currency, value|
|
62
|
-
rate_comparison(currency, 'EUR', 1.0 / value)
|
87
|
+
rate_comparison(currency, 'EUR', date, 1.0 / value)
|
63
88
|
end
|
64
89
|
end
|
65
90
|
|
66
91
|
it 'should report a rate of 1.0 between the same currency' do
|
67
|
-
rate_comparison('KRW', 'KRW', 1.0)
|
92
|
+
rate_comparison('KRW', 'KRW', date, 1.0)
|
68
93
|
end
|
69
94
|
|
70
95
|
it 'should convert rates between currencies through the EUR' do
|
@@ -77,62 +102,149 @@ describe ECB::CurrencyConverter do
|
|
77
102
|
['IDR', 'THB', 0.00311], ['ILS', 'TRY', 0.52382],
|
78
103
|
['INR', 'USD', 0.01701], ['JPY', 'ZAR', 0.10517],
|
79
104
|
].each do |from, to, value|
|
80
|
-
rate_comparison(from, to, value)
|
105
|
+
rate_comparison(from, to, date, value)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#rate(historical)' do
|
111
|
+
let(:date) { Date.parse('2013-05-17') }
|
112
|
+
it 'should match the provided rates when converting from EUR' do
|
113
|
+
[ ['AUD', 1.3216], ['BGN', 1.9558], ['BRL', 2.61],
|
114
|
+
['CAD', 1.322], ['CHF', 1.2449], ['CNY', 7.904],
|
115
|
+
['CZK', 25.989], ['DKK', 7.4524], ['GBP', 0.84475],
|
116
|
+
['HKD', 9.9911], ['HRK', 7.571], ['HUF', 290.56],
|
117
|
+
['IDR', 12554.93], ['ILS', 4.7135], ['INR', 70.606],
|
118
|
+
['JPY', 131.87], ['KRW', 1437.84], ['LTL', 3.4528],
|
119
|
+
['LVL', 0.6993], ['MXN', 15.8403], ['MYR', 3.889],
|
120
|
+
['NOK', 7.5393], ['NZD', 1.5917], ['PHP', 52.964],
|
121
|
+
['PLN', 4.1704], ['RON', 4.337], ['RUB', 40.3876],
|
122
|
+
['SEK', 8.5922], ['SGD', 1.6156], ['THB', 38.324],
|
123
|
+
['TRY', 2.3646], ['USD', 1.2869], ['ZAR', 12.0764]
|
124
|
+
].each do |currency, value|
|
125
|
+
rate_comparison('EUR', currency, date, value)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should match the inverted rates when converting to EUR' do
|
130
|
+
[ ['AUD', 1.3216], ['BGN', 1.9558], ['BRL', 2.61],
|
131
|
+
['CAD', 1.322], ['CHF', 1.2449], ['CNY', 7.904],
|
132
|
+
['CZK', 25.989], ['DKK', 7.4524], ['GBP', 0.84475],
|
133
|
+
['HKD', 9.9911], ['HRK', 7.571], ['HUF', 290.56],
|
134
|
+
['IDR', 12554.93], ['ILS', 4.7135], ['INR', 70.606],
|
135
|
+
['JPY', 131.87], ['KRW', 1437.84], ['LTL', 3.4528],
|
136
|
+
['LVL', 0.6993], ['MXN', 15.8403], ['MYR', 3.889],
|
137
|
+
['NOK', 7.5393], ['NZD', 1.5917], ['PHP', 52.964],
|
138
|
+
['PLN', 4.1704], ['RON', 4.337], ['RUB', 40.3876],
|
139
|
+
['SEK', 8.5922], ['SGD', 1.6156], ['THB', 38.324],
|
140
|
+
['TRY', 2.3646], ['USD', 1.2869], ['ZAR', 12.0764]
|
141
|
+
].each do |currency, value|
|
142
|
+
rate_comparison(currency, 'EUR', date, 1.0 / value)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should report a rate of 1.0 between the same currency' do
|
147
|
+
rate_comparison('KRW', 'KRW', date, 1.0)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should convert rates between currencies through the EUR' do
|
151
|
+
[ ['AUD', 'LTL', 2.61259], ['BGN', 'LVL', 0.35755],
|
152
|
+
['BRL', 'MXN', 6.06908], ['CAD', 'MYR', 2.94175],
|
153
|
+
['CHF', 'NOK', 6.05614], ['CNY', 'NZD', 0.20137],
|
154
|
+
['CZK', 'PHP', 2.03793], ['DKK', 'PLN', 0.55960],
|
155
|
+
['GBP', 'RON', 5.13406], ['HKD', 'RUB', 4.04235],
|
156
|
+
['HRK', 'SEK', 1.13488], ['HUF', 'SGD', 0.00556],
|
157
|
+
['IDR', 'THB', 0.00305], ['ILS', 'TRY', 0.50166],
|
158
|
+
['INR', 'USD', 0.01822], ['JPY', 'ZAR', 0.09157],
|
159
|
+
].each do |from, to, value|
|
160
|
+
rate_comparison(from, to, date, value)
|
81
161
|
end
|
82
162
|
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#silence_warnings' do
|
166
|
+
let(:date) { Date.parse('2013-05-18') } # NOTE: weekend date
|
167
|
+
around { ECB::CurrencyConverter.silence_warnings(false) }
|
168
|
+
|
169
|
+
it 'should warn users when not using their requested date' do
|
170
|
+
ECB::CurrencyConverter.silence_warnings
|
171
|
+
Kernal.should_not_receive(:warn)
|
172
|
+
|
173
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD', date)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should not warn users when they silence warnings' do
|
177
|
+
Kernal.should_receive(:warn)
|
178
|
+
|
179
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD', date)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#validate' do
|
184
|
+
describe ECB::MissingDateError do
|
185
|
+
it 'should raise an error for a date more than 90 days ago' do
|
186
|
+
expect {
|
187
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD', Date.today - 90)
|
188
|
+
}.to raise_error(ECB::MissingDateError)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should not raise an error for a date less than 90 days ago' do
|
192
|
+
expect {
|
193
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD', Date.today - 89)
|
194
|
+
}.to_not raise_error(ECB::MissingDateError)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe ECB::MissingExchangeRateError do
|
199
|
+
before do
|
200
|
+
ECB::CurrencyConverter.send(:load_data!, Date.today)
|
201
|
+
|
202
|
+
euro = ECB::CurrencyConverter.instance_variable_get(:@euro)
|
203
|
+
euro[Date.today.to_s]['USD'] = nil
|
204
|
+
|
205
|
+
ECB::CurrencyConverter.instance_variable_set(:@euro, euro)
|
206
|
+
end
|
207
|
+
|
208
|
+
after do
|
209
|
+
ECB::CurrencyConverter.instance_variable_set(:@euro, nil)
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should raise an error if supported data is missing' do
|
213
|
+
expect {
|
214
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD')
|
215
|
+
}.to raise_error(ECB::MissingExchangeRateError)
|
216
|
+
|
217
|
+
expect {
|
218
|
+
ECB::CurrencyConverter.exchange(100, 'USD', 'EUR')
|
219
|
+
}.to raise_error(ECB::MissingExchangeRateError)
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'should not raise an error if we have data available' do
|
223
|
+
expect {
|
224
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'GBP')
|
225
|
+
}.to_not raise_error(ECB::MissingExchangeRateError)
|
226
|
+
|
227
|
+
expect {
|
228
|
+
ECB::CurrencyConverter.exchange(100, 'GBP', 'EUR')
|
229
|
+
}.to_not raise_error(ECB::MissingExchangeRateError)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe ECB::UnknownCurrencyError do
|
234
|
+
it 'should raise an error on an unsupported currency code' do
|
235
|
+
expect {
|
236
|
+
ECB::CurrencyConverter.exchange(100, 'USD', 'UNKNOWN')
|
237
|
+
}.to raise_error(ECB::UnknownCurrencyError)
|
238
|
+
|
239
|
+
expect {
|
240
|
+
ECB::CurrencyConverter.exchange(100, 'UNKNOWN', 'USD')
|
241
|
+
}.to raise_error(ECB::UnknownCurrencyError)
|
242
|
+
end
|
83
243
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
ECB::CurrencyConverter.exchange(100, 'USD', 'UNKNOWN')
|
89
|
-
}.to raise_error(ECB::UnknownCurrencyError)
|
90
|
-
|
91
|
-
expect {
|
92
|
-
ECB::CurrencyConverter.exchange(100, 'UNKNOWN', 'USD')
|
93
|
-
}.to raise_error(ECB::UnknownCurrencyError)
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'should not raise an error for supported currency codes' do
|
97
|
-
expect {
|
98
|
-
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD')
|
99
|
-
}.to_not raise_error(ECB::UnknownCurrencyError)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
describe ECB::MissingExchangeRateError do
|
104
|
-
before do
|
105
|
-
ECB::CurrencyConverter.send(:load_data!)
|
106
|
-
|
107
|
-
euro = ECB::CurrencyConverter.instance_variable_get(:@euro)
|
108
|
-
euro['USD'] = nil
|
109
|
-
|
110
|
-
ECB::CurrencyConverter.instance_variable_set(:@euro, euro)
|
111
|
-
end
|
112
|
-
|
113
|
-
after do
|
114
|
-
ECB::CurrencyConverter.instance_variable_set(:@euro, nil)
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'should raise an error if supported data is missing' do
|
118
|
-
expect {
|
119
|
-
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD')
|
120
|
-
}.to raise_error(ECB::MissingExchangeRateError)
|
121
|
-
|
122
|
-
expect {
|
123
|
-
ECB::CurrencyConverter.exchange(100, 'USD', 'EUR')
|
124
|
-
}.to raise_error(ECB::MissingExchangeRateError)
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'should not raise an error if we have data available' do
|
128
|
-
expect {
|
129
|
-
ECB::CurrencyConverter.exchange(100, 'EUR', 'GBP')
|
130
|
-
}.to_not raise_error(ECB::MissingExchangeRateError)
|
131
|
-
|
132
|
-
expect {
|
133
|
-
ECB::CurrencyConverter.exchange(100, 'GBP', 'EUR')
|
134
|
-
}.to_not raise_error(ECB::MissingExchangeRateError)
|
135
|
-
end
|
244
|
+
it 'should not raise an error for supported currency codes' do
|
245
|
+
expect {
|
246
|
+
ECB::CurrencyConverter.exchange(100, 'EUR', 'USD')
|
247
|
+
}.to_not raise_error(ECB::UnknownCurrencyError)
|
136
248
|
end
|
137
249
|
end
|
138
250
|
end
|