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 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 reference rates.
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.73999999999998
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.7477194556602363
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 Unknown Currency Error
2
+ # = ECB Missing Date Error
3
3
  #
4
- # Raised when we try to grab data for an unsupported currency code.
4
+ # Raised when the data for a date is +nil+.
5
5
  #
6
- # * +currency_code+ - the unsupported ISO 4217 Currency Code.
7
- class UnknownCurrencyError < StandardError
8
- def initialize(currency_code)
9
- super("#{currency_code} is not supported.")
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,5 +1,5 @@
1
1
  module ECB # :nodoc:
2
2
  module CurrencyConverter # :nodoc:
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  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
- module ECB # :nodoc:
6
- # = European Central Bank - Currency Converter
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
- DAILY_URI = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
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.73999999999998
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
- def exchange(value, from, to)
66
- value * rate(from, to)
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.7477194556602363
78
- def rate(from, to)
79
- load_data!
80
- validate(from, to)
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
- 1.0 / @euro[from] * @euro[to]
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 load_data!
87
- @euro ||= begin
88
- response = HTTParty.get(DAILY_URI, format: :xml)
89
-
90
- data = response['Envelope']['Cube']['Cube']['Cube']
91
- data = data.reduce({}) { |a,e| a[e['currency']] = e['rate'].to_f; a }
92
- data['EUR'] = 1.0
93
- data
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 @euro[from].to_f.zero?
103
- raise MissingExchangeRateError.new(to) if @euro[to].to_f.zero?
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 be_within(0.0001).of(actual)
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
- describe '#validate' do
85
- describe ECB::UnknownCurrencyError do
86
- it 'should raise an error on an unsupported currency code' do
87
- expect {
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