ecb-currency_converter 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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