exchange 0.2.6 → 0.3.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.
Files changed (43) hide show
  1. data/.travis.yml +1 -0
  2. data/README.rdoc +5 -3
  3. data/VERSION +1 -1
  4. data/exchange.gemspec +16 -2
  5. data/iso4217.yml +589 -0
  6. data/lib/core_extensions/conversability.rb +12 -7
  7. data/lib/exchange.rb +7 -1
  8. data/lib/exchange/cache.rb +2 -0
  9. data/lib/exchange/cache/base.rb +9 -5
  10. data/lib/exchange/cache/file.rb +65 -0
  11. data/lib/exchange/cache/memcached.rb +4 -4
  12. data/lib/exchange/cache/no_cache.rb +33 -0
  13. data/lib/exchange/cache/rails.rb +2 -2
  14. data/lib/exchange/cache/redis.rb +5 -5
  15. data/lib/exchange/configuration.rb +23 -7
  16. data/lib/exchange/currency.rb +94 -40
  17. data/lib/exchange/external_api.rb +1 -0
  18. data/lib/exchange/external_api/base.rb +11 -19
  19. data/lib/exchange/external_api/call.rb +10 -12
  20. data/lib/exchange/external_api/currency_bot.rb +2 -2
  21. data/lib/exchange/external_api/ecb.rb +68 -0
  22. data/lib/exchange/external_api/xavier_media.rb +4 -3
  23. data/lib/exchange/helper.rb +27 -0
  24. data/lib/exchange/iso_4217.rb +95 -0
  25. data/spec/core_extensions/conversability_spec.rb +40 -6
  26. data/spec/exchange/cache/base_spec.rb +4 -4
  27. data/spec/exchange/cache/file_spec.rb +70 -0
  28. data/spec/exchange/cache/memcached_spec.rb +5 -2
  29. data/spec/exchange/cache/no_cache_spec.rb +27 -0
  30. data/spec/exchange/cache/rails_spec.rb +6 -3
  31. data/spec/exchange/cache/redis_spec.rb +5 -2
  32. data/spec/exchange/currency_spec.rb +86 -23
  33. data/spec/exchange/external_api/base_spec.rb +8 -5
  34. data/spec/exchange/external_api/call_spec.rb +38 -29
  35. data/spec/exchange/external_api/currency_bot_spec.rb +8 -10
  36. data/spec/exchange/external_api/ecb_spec.rb +55 -0
  37. data/spec/exchange/external_api/xavier_media_spec.rb +8 -8
  38. data/spec/exchange/helper_spec.rb +30 -0
  39. data/spec/exchange/iso_4217_spec.rb +45 -0
  40. data/spec/support/api_responses/example_ecb_xml_90d.xml +64 -0
  41. data/spec/support/api_responses/example_ecb_xml_daily.xml +44 -0
  42. data/spec/support/api_responses/example_ecb_xml_history.xml +64 -0
  43. metadata +35 -21
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Exchange::Cache::File" do
4
+ subject { Exchange::Cache::File }
5
+ before(:each) do
6
+ Exchange::Configuration.define do |c|
7
+ c.cache = :file
8
+ end
9
+ end
10
+ after(:each) do
11
+ Exchange::Configuration.define do |c|
12
+ c.cache = :memcached
13
+ end
14
+ end
15
+ describe "cached" do
16
+ it "should raise an error if no block was given" do
17
+ lambda { subject.cached('API_CLASS') }.should raise_error(Exchange::Cache::CachingWithoutBlockError)
18
+ end
19
+ context "when a result is returned" do
20
+ context "with a daily cache" do
21
+ before(:each) do
22
+ subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
23
+ Exchange::Configuration.should_receive(:filestore_path).and_return('STORE')
24
+ ::File.should_receive(:exists?).with('STORE/KEY').and_return(true)
25
+ ::File.should_receive(:read).with('STORE/KEY').and_return 'CONTENT'
26
+ end
27
+ it "should return the file contents" do
28
+ subject.cached('API_CLASS') { 'something' }.should == 'CONTENT'
29
+ end
30
+ end
31
+ context "with an monthly cache" do
32
+ before(:each) do
33
+ subject.should_receive(:key).with('API_CLASS', an_instance_of(Symbol)).and_return('KEY')
34
+ Exchange::Configuration.should_receive(:filestore_path).and_return('STORE')
35
+ ::File.should_receive(:exists?).with('STORE/KEY').and_return(true)
36
+ ::File.should_receive(:read).with('STORE/KEY').and_return 'CONTENT'
37
+ end
38
+ it "should return the file contents" do
39
+ subject.cached('API_CLASS', :cache_period => :monthly) { 'something' }.should == 'CONTENT'
40
+ end
41
+ end
42
+ end
43
+ context "when no file is cached yet" do
44
+ before(:each) do
45
+ subject.should_receive(:key).with('API_CLASS', an_instance_of(Symbol)).at_most(3).times.and_return('KEY')
46
+ Exchange::Configuration.should_receive(:filestore_path).and_return('STORE')
47
+ ::File.should_receive(:exists?).with('STORE/KEY').and_return(false)
48
+ Dir.should_receive(:entries).with('STORE').once.and_return(%W(entry entry2))
49
+ ::File.should_receive(:delete).with(an_instance_of(String)).twice
50
+ FileUtils.should_receive(:mkdir_p).once
51
+ ::File.should_receive(:open).once
52
+ end
53
+ it "should return the file contents" do
54
+ subject.cached('API_CLASS', :cache_period => :monthly) { 'RESULT' }.should == 'RESULT'
55
+ end
56
+ end
57
+ context "when no result is returned" do
58
+ before(:each) do
59
+ subject.should_receive(:key).with('API_CLASS', an_instance_of(Symbol)).at_most(3).times.and_return('KEY')
60
+ Exchange::Configuration.should_receive(:filestore_path).and_return('STORE')
61
+ ::File.should_receive(:exists?).with('STORE/KEY').and_return(false)
62
+ FileUtils.should_receive(:mkdir_p).never
63
+ ::File.should_receive(:open).never
64
+ end
65
+ it "should return the file contents" do
66
+ subject.cached('API_CLASS', :cache_period => :monthly) { '' }.should == ''
67
+ end
68
+ end
69
+ end
70
+ end
@@ -25,10 +25,13 @@ describe "Exchange::Cache::Memcached" do
25
25
  end
26
26
  end
27
27
  describe "cached" do
28
+ it "should raise an error if no block was given" do
29
+ lambda { subject.cached('API_CLASS') }.should raise_error(Exchange::Cache::CachingWithoutBlockError)
30
+ end
28
31
  context "when a cached result exists" do
29
32
  let(:client) { mock('memcached') }
30
33
  before(:each) do
31
- subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
34
+ subject.should_receive(:key).with('API_CLASS', {}).and_return('KEY')
32
35
  ::Memcached.should_receive(:new).with("HOST:PORT").and_return(client)
33
36
  client.should_receive(:get).with('KEY').and_return "{\"RESULT\":\"YAY\"}"
34
37
  end
@@ -42,7 +45,7 @@ describe "Exchange::Cache::Memcached" do
42
45
  context "when no cached result exists" do
43
46
  let(:client) { mock('memcached') }
44
47
  before(:each) do
45
- subject.should_receive(:key).with('API_CLASS', nil).twice.and_return('KEY')
48
+ subject.should_receive(:key).with('API_CLASS', {}).twice.and_return('KEY')
46
49
  ::Memcached.should_receive(:new).with("HOST:PORT").and_return(client)
47
50
  client.should_receive(:get).with('KEY').and_raise(::Memcached::NotFound)
48
51
  end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Exchange::Cache::Rails" do
4
+ context "with rails defined" do
5
+ class ::Rails
6
+ end
7
+ end
8
+ subject { Exchange::Cache::NoCache }
9
+ before(:each) do
10
+ Exchange::Configuration.define do |c|
11
+ c.cache = false
12
+ end
13
+ end
14
+ after(:each) do
15
+ Exchange::Configuration.define do |c|
16
+ c.cache = :memcached
17
+ end
18
+ end
19
+ describe "cached" do
20
+ it "should directly call the block" do
21
+ subject.cached('API_CLASS') { 'something' }.should == 'something'
22
+ end
23
+ it "should raise an error if no block was given" do
24
+ lambda { subject.cached('API_CLASS') }.should raise_error(Exchange::Cache::CachingWithoutBlockError)
25
+ end
26
+ end
27
+ end
@@ -24,11 +24,14 @@ describe "Exchange::Cache::Rails" do
24
24
  end
25
25
  end
26
26
  describe "cached" do
27
+ it "should raise an error if no block was given" do
28
+ lambda { subject.cached('API_CLASS') }.should raise_error(Exchange::Cache::CachingWithoutBlockError)
29
+ end
27
30
  context "when a result is returned" do
28
31
  let(:client) { mock('rails_cache') }
29
32
  context "with a daily cache" do
30
33
  before(:each) do
31
- subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
34
+ subject.should_receive(:key).with('API_CLASS', {}).and_return('KEY')
32
35
  ::Rails.should_receive(:cache).and_return(client)
33
36
  client.should_receive(:fetch).with('KEY', :expires_in => 86400).and_return "{\"RESULT\":\"YAY\"}"
34
37
  end
@@ -39,7 +42,7 @@ describe "Exchange::Cache::Rails" do
39
42
  context "with an hourly cache" do
40
43
  before(:each) do
41
44
  Exchange::Configuration.update = :hourly
42
- subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
45
+ subject.should_receive(:key).with('API_CLASS', {}).and_return('KEY')
43
46
  ::Rails.should_receive(:cache).and_return(client)
44
47
  client.should_receive(:fetch).with('KEY', :expires_in => 3600).and_return "{\"RESULT\":\"YAY\"}"
45
48
  end
@@ -54,7 +57,7 @@ describe "Exchange::Cache::Rails" do
54
57
  context "when no result is returned" do
55
58
  let(:client) { mock('rails_cache') }
56
59
  before(:each) do
57
- subject.should_receive(:key).with('API_CLASS', nil).at_most(3).times.and_return('KEY')
60
+ subject.should_receive(:key).with('API_CLASS', {}).at_most(3).times.and_return('KEY')
58
61
  ::Rails.should_receive(:cache).twice.and_return(client)
59
62
  client.should_receive(:fetch).with('KEY', an_instance_of(Hash)).and_return nil
60
63
  client.should_receive(:delete).with('KEY').once
@@ -27,10 +27,13 @@ describe "Exchange::Cache::Redis" do
27
27
  end
28
28
  end
29
29
  describe "cached" do
30
+ it "should raise an error if no block was given" do
31
+ lambda { subject.cached('API_CLASS') }.should raise_error(Exchange::Cache::CachingWithoutBlockError)
32
+ end
30
33
  context "when a cached result exists" do
31
34
  let(:client) { mock('redis') }
32
35
  before(:each) do
33
- subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
36
+ subject.should_receive(:key).with('API_CLASS', {}).and_return('KEY')
34
37
  ::Redis.should_receive(:new).with(:host => 'HOST', :port => 'PORT').and_return(client)
35
38
  client.should_receive(:get).with('KEY').and_return "{\"RESULT\":\"YAY\"}"
36
39
  end
@@ -44,7 +47,7 @@ describe "Exchange::Cache::Redis" do
44
47
  context "when no cached result exists" do
45
48
  let(:client) { mock('redis') }
46
49
  before(:each) do
47
- subject.should_receive(:key).with('API_CLASS', nil).at_most(3).times.and_return('KEY')
50
+ subject.should_receive(:key).with('API_CLASS', {}).at_most(3).times.and_return('KEY')
48
51
  ::Redis.should_receive(:new).with(:host => 'HOST', :port => 'PORT').and_return(client)
49
52
  client.should_receive(:get).with('KEY').and_return nil
50
53
  end
@@ -18,7 +18,7 @@ describe "Exchange::Currency" do
18
18
  describe "convert_to" do
19
19
  it "should be able to convert itself to other currencies" do
20
20
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 3)
21
- subject.convert_to(:chf).value.should == 36.5
21
+ subject.convert_to(:chf).value.round(2).should == 36.5
22
22
  subject.convert_to(:chf).currency.should == :chf
23
23
  subject.convert_to(:chf).should be_kind_of Exchange::Currency
24
24
  end
@@ -33,7 +33,7 @@ describe "Exchange::Currency" do
33
33
  end
34
34
  it "should be able to add another currency value" do
35
35
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
36
- (subject + Exchange::Currency.new(30, :chf)).value.should == 72.87
36
+ (subject + Exchange::Currency.new(30, :chf)).value.round(2).should == 72.87
37
37
  (subject + Exchange::Currency.new(30, :sek)).currency.should == :usd
38
38
  end
39
39
  it "should raise when currencies get mixed and the configuration does not allow it" do
@@ -57,7 +57,7 @@ describe "Exchange::Currency" do
57
57
  end
58
58
  it "should be able to subtract another currency value" do
59
59
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
60
- (subject + Exchange::Currency.new(10, :chf)).value.should == 50.96
60
+ (subject + Exchange::Currency.new(10, :chf)).value.round(2).should == 50.96
61
61
  (subject + Exchange::Currency.new(23.3, :eur)).currency.should == :usd
62
62
  end
63
63
  it "should raise when currencies get mixed and the configuration does not allow it" do
@@ -81,7 +81,7 @@ describe "Exchange::Currency" do
81
81
  end
82
82
  it "should be able to multiply by another currency value" do
83
83
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
84
- (((subject * Exchange::Currency.new(10, :chf)).value * 10000.0).round.to_f / 10000.0).should == 438.4
84
+ (subject * Exchange::Currency.new(10, :chf)).value.round(1).should == 438.3
85
85
  (subject * Exchange::Currency.new(23.3, :eur)).currency.should == :usd
86
86
  end
87
87
  it "should raise when currencies get mixed and the configuration does not allow it" do
@@ -101,11 +101,11 @@ describe "Exchange::Currency" do
101
101
  (subject / 40).value.should == 1
102
102
  end
103
103
  it "should be able to multiply a float" do
104
- (((subject / 40.5).value * 10000.0).round.to_f / 10000.0).should == 0.9877
104
+ BigDecimal.new((subject / 40.5).value.to_s).round(4).should == 0.9877
105
105
  end
106
106
  it "should be able to multiply by another currency value" do
107
107
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
108
- (((subject / Exchange::Currency.new(10, :chf)).value * 10000.0).round.to_f / 10000.0).should == 3.6496
108
+ (subject / Exchange::Currency.new(10, :chf)).value.round(2).should == BigDecimal.new("3.65")
109
109
  (subject / Exchange::Currency.new(23.3, :eur)).currency.should == :usd
110
110
  end
111
111
  it "should raise when currencies get mixed and the configuration does not allow it" do
@@ -126,7 +126,7 @@ describe "Exchange::Currency" do
126
126
  let(:comp2) { Exchange::Currency.new(40, :usd) }
127
127
  let(:comp3) { Exchange::Currency.new(50, :eur) }
128
128
  let(:comp4) { Exchange::Currency.new(45, :eur) }
129
- let(:comp5) { Exchange::Currency.new(66.1, :usd) }
129
+ let(:comp5) { Exchange::Currency.new(50, :eur).to_usd }
130
130
  let(:comp6) { Exchange::Currency.new(66.1, :usd, :at => Time.gm(2011,1,1)) }
131
131
  before(:each) do
132
132
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
@@ -167,35 +167,95 @@ describe "Exchange::Currency" do
167
167
  end
168
168
  describe "round" do
169
169
  subject { Exchange::Currency.new(40.123, :usd) }
170
- it "should apply it to its number" do
171
- subject.round.value.should == 40
172
- subject.round.currency.should == :usd
173
- subject.round.should be_kind_of Exchange::Currency
170
+ context "without arguments" do
171
+ it "should apply it to its number in the iso certified format" do
172
+ subject.round.value.should == 40.12
173
+ subject.round.currency.should == :usd
174
+ subject.round.should be_kind_of Exchange::Currency
175
+ end
176
+ end
177
+ context "with arguments" do
178
+ it "should apply it to its number" do
179
+ subject.round(0).value.should == 40
180
+ subject.round(0).currency.should == :usd
181
+ subject.round(0).should be_kind_of Exchange::Currency
182
+ end
183
+ it "should allow to round to whatever number of decimals" do
184
+ subject.round(2).value.should == 40.12
185
+ subject.round(2).currency.should == :usd
186
+ subject.round(2).should be_kind_of Exchange::Currency
187
+ end
174
188
  end
175
189
  end
176
190
  describe "ceil" do
177
- subject { Exchange::Currency.new(40.123, :usd) }
178
- it "should apply it to its number" do
179
- subject.ceil.value.should == 41
180
- subject.ceil.currency.should == :usd
181
- subject.ceil.should be_kind_of Exchange::Currency
191
+ subject { Exchange::Currency.new(40.1236, :omr) }
192
+ context "without arguments" do
193
+ it "should apply it to its number in the iso certified format" do
194
+ subject.ceil.value.should == 40.124
195
+ subject.ceil.currency.should == :omr
196
+ subject.ceil.should be_kind_of Exchange::Currency
197
+ end
198
+ end
199
+ context "with arguments" do
200
+ it "should apply it to its number" do
201
+ subject.ceil(0).value.should == 41
202
+ subject.ceil(0).currency.should == :omr
203
+ subject.ceil(0).should be_kind_of Exchange::Currency
204
+ end
205
+ it "should allow to round to whatever number of decimals" do
206
+ subject.ceil(2).value.should == 40.13
207
+ subject.ceil(2).currency.should == :omr
208
+ subject.ceil(2).should be_kind_of Exchange::Currency
209
+ end
182
210
  end
183
211
  end
184
212
  describe "floor" do
185
- subject { Exchange::Currency.new(40.723, :usd) }
186
- it "should apply it to its number" do
187
- subject.floor.value.should == 40
188
- subject.floor.currency.should == :usd
189
- subject.floor.should be_kind_of Exchange::Currency
213
+ subject { Exchange::Currency.new(40.723, :jpy) }
214
+ context "without arguments" do
215
+ it "should apply it to its number in the iso certified format" do
216
+ subject.floor.value.should == 40
217
+ subject.floor.currency.should == :jpy
218
+ subject.floor.should be_kind_of Exchange::Currency
219
+ end
220
+ end
221
+ context "with arguments" do
222
+ it "should apply it to its number" do
223
+ subject.floor(1).value.should == 40.7
224
+ subject.floor(1).currency.should == :jpy
225
+ subject.floor(1).should be_kind_of Exchange::Currency
226
+ end
227
+ it "should allow to round to whatever number of decimals" do
228
+ subject.floor(2).value.should == 40.72
229
+ subject.floor(2).currency.should == :jpy
230
+ subject.floor(2).should be_kind_of Exchange::Currency
231
+ end
190
232
  end
191
233
  end
192
234
  end
235
+ describe "to_s" do
236
+ it "should render the currency according to ISO 4217 Definitions" do
237
+ Exchange::Currency.new(23.232524, 'TND').to_s.should == "TND 23.233"
238
+ Exchange::Currency.new(23.23252423, 'SAR').to_s.should == "SAR 23.23"
239
+ Exchange::Currency.new(23.23252423, 'CLP').to_s.should == "CLP 23"
240
+ Exchange::Currency.new(23.2, 'TND').to_s.should == "TND 23.200"
241
+ Exchange::Currency.new(23.4, 'SAR').to_s.should == "SAR 23.40"
242
+ Exchange::Currency.new(23.0, 'CLP').to_s.should == "CLP 23"
243
+ end
244
+ it "should render only the currency amount if the argument amount is passed" do
245
+ Exchange::Currency.new(23.232524, 'TND').to_s(:amount).should == "23.233"
246
+ Exchange::Currency.new(23.23252423, 'SAR').to_s(:amount).should == "23.23"
247
+ Exchange::Currency.new(23.23252423, 'CLP').to_s(:amount).should == "23"
248
+ Exchange::Currency.new(23.2, 'TND').to_s(:amount).should == "23.200"
249
+ Exchange::Currency.new(23.4, 'SAR').to_s(:amount).should == "23.40"
250
+ Exchange::Currency.new(23.0, 'CLP').to_s(:amount).should == "23"
251
+ end
252
+ end
193
253
  describe "methods via method missing" do
194
254
  it "should be able to convert via to_currency to other currencies" do
195
255
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 6)
196
256
  {'chf' => 36.5, 'usd' => 40.0, 'dkk' => 225.12, 'sek' => 269.85, 'nok' => 232.06, 'rub' => 1205.24}.each do |currency, value|
197
257
  c = subject.send(:"to_#{currency}")
198
- c.value.should == value
258
+ c.value.round(2).should == value
199
259
  c.currency.should == currency
200
260
  end
201
261
  end
@@ -203,7 +263,7 @@ describe "Exchange::Currency" do
203
263
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/historical/2011-10-09.json", fixture('api_responses/example_json_api.json'), 6)
204
264
  {'chf' => 36.5, 'usd' => 40.0, 'dkk' => 225.12, 'sek' => 269.85, 'nok' => 232.06, 'rub' => 1205.24}.each do |currency, value|
205
265
  c = subject.send(:"to_#{currency}", :at => Time.gm(2011,10,9))
206
- c.value.should == value
266
+ c.value.round(2).should == value
207
267
  c.currency.should == currency
208
268
  end
209
269
  end
@@ -211,6 +271,9 @@ describe "Exchange::Currency" do
211
271
  mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/historical/2011-01-01.json", fixture('api_responses/example_json_api.json'), 2)
212
272
  5.eur(:at => Time.gm(2011,1,1)).to_usd.value.should == 5.eur.to_usd(:at => Time.gm(2011,1,1)).value
213
273
  end
274
+ it "should raise errors for currency conversions it does not have rates for" do
275
+ lambda { subject.to_ssp }.should raise_error(NoRateError)
276
+ end
214
277
  it "should pass on methods it does not understand to its number" do
215
278
  subject.to_f.should == 40
216
279
  lambda { subject.to_hell }.should raise_error(NoMethodError)
@@ -3,29 +3,32 @@ require 'spec_helper'
3
3
  describe "Exchange::ExternalAPI::Base" do
4
4
  subject { Exchange::ExternalAPI::Base.new }
5
5
  before(:each) do
6
- subject.instance_variable_set("@rates", {'EUR' => 3.45, 'CHF' => 5.565})
6
+ Exchange::Configuration.cache = false
7
+ end
8
+ before(:each) do
9
+ subject.instance_variable_set("@rates", {'EUR' => BigDecimal.new("3.45"), 'CHF' => BigDecimal.new("5.565")})
7
10
  subject.instance_variable_set("@base", 'usd')
8
11
  end
9
12
  describe "rate" do
10
13
  it "should put out an exchange rate for the two currencies" do
11
14
  subject.should_receive(:update).once
12
- ((subject.rate('eur', 'chf') * 10000).round.to_f / 10000).should == 1.613
15
+ subject.rate('eur', 'chf').round(3).should == 1.613
13
16
  end
14
17
  it "should put out an exchange rate for the two currencies and pass on opts" do
15
18
  time = Time.now
16
19
  subject.should_receive(:update).with(:at => time).once
17
- ((subject.rate('eur', 'chf', :at => time) * 10000).round.to_f / 10000).should == 1.613
20
+ subject.rate('eur', 'chf', :at => time).round(3).should == 1.613
18
21
  end
19
22
  end
20
23
  describe "convert" do
21
24
  it "should convert according to the given rates" do
22
25
  subject.should_receive(:update).once
23
- subject.convert(80,'chf','eur').should == 49.6
26
+ subject.convert(80,'chf','eur').round(2).should == 49.6
24
27
  end
25
28
  it "should convert according to the given rates and pass opts" do
26
29
  time = Time.now
27
30
  subject.should_receive(:update).with(:at => time).once
28
- subject.convert(80,'chf','eur', :at => time).should == 49.6
31
+ subject.convert(80,'chf','eur', :at => time).round(2).should == 49.6
29
32
  end
30
33
  end
31
34
  end
@@ -7,27 +7,31 @@ describe "Exchange::ExternalAPI::Call" do
7
7
  describe "initialization" do
8
8
  context "with a json api" do
9
9
  before(:each) do
10
- mock_api('JSON_API', fixture('api_responses/example_json_api.json'))
10
+ mock_api('JSON_API', fixture('api_responses/example_json_api.json'), 5)
11
11
  end
12
12
  it "should call the api and yield a block with the result" do
13
13
  Exchange::ExternalAPI::Call.new('JSON_API') do |result|
14
14
  result.should == JSON.load(fixture('api_responses/example_json_api.json'))
15
15
  end
16
16
  end
17
- #context "with http errors" do
18
- #let(:opened_uri) { mock('uri', :read => fixture('api_responses/example_xml_api.xml'))}
19
- # it "should recall as many times as specified in the options" do
20
- # URI.should_receive(:parse).with('JSON_API').and_return(uri_mock)
21
- # Exchange::ExternalAPI::Call.new('JSON_API') do |result|
22
- # result.should == JSON.load(fixture('api_responses/example_json_api.json'))
23
- # end
24
- # end
25
- # it "should raise errors if the maximum call repetition is reached" do
26
- # URI.stub_chain :parse, :open, :read => OpenURI::HTTPError.new
27
- # uri_mock.should_receive(:open).at_most(5).times.and_raise(OpenURI::HTTPError)
28
- # lambda { Exchange::ExternalAPI::Call.new('JSON_API') }.should raise_error(Exchange::ExternalAPI::APIError)
29
- # end
30
- #end
17
+ context "with http errors" do
18
+ it "should recall and deliver the result if possible" do
19
+ @count = 0
20
+ @uri_mock.should_receive(:open).at_most(3).times.and_return do
21
+ @count += 1
22
+ @count == 3 ? mock('opened', :read => fixture('api_responses/example_json_api.json')) : raise(OpenURI::HTTPError.new('404', 'URI'))
23
+ end
24
+ Exchange::ExternalAPI::Call.new('JSON_API') do |result|
25
+ result.should == JSON.load(fixture('api_responses/example_json_api.json'))
26
+ end
27
+ end
28
+ it "should raise if the maximum recall size is reached" do
29
+ @uri_mock.should_receive(:open).at_most(5).times.and_return do
30
+ raise OpenURI::HTTPError.new('404', 'URI')
31
+ end
32
+ lambda { Exchange::ExternalAPI::Call.new('JSON_API') }.should raise_error(Exchange::ExternalAPI::APIError)
33
+ end
34
+ end
31
35
  context "with socket errors" do
32
36
  it "should raise an error immediately" do
33
37
  @uri_mock.should_receive(:open).at_most(5).times.and_raise(SocketError)
@@ -38,26 +42,31 @@ describe "Exchange::ExternalAPI::Call" do
38
42
  end
39
43
  context "with an xml api" do
40
44
  before(:each) do
41
- mock_api('XML_API', fixture('api_responses/example_xml_api.xml'))
45
+ mock_api('XML_API', fixture('api_responses/example_xml_api.xml'), 5)
42
46
  end
43
47
  it "should call the api and yield a block with the result" do
44
48
  Exchange::ExternalAPI::Call.new('XML_API', :format => :xml) do |result|
45
49
  result.to_s.should == Nokogiri.parse(fixture('api_responses/example_xml_api.xml')).to_s
46
50
  end
47
51
  end
48
- # context "with http errors" do
49
- # let(:error_mock) { mock('opened_uri') }
50
- # it "should recall as many times as specified in the options" do
51
- # URI.stub! :parse => OpenURI::HTTPError.new
52
- # Exchange::ExternalAPI::Call.new('XML_API', :format => :xml) do |result|
53
- # result.to_s.should == Nokogiri.parse(fixture('api_responses/example_xml_api.xml')).to_s
54
- # end
55
- # end
56
- # it "should raise errors if the maximum call repetition is reached" do
57
- # URI.stub! :parse => OpenURI::HTTPError.new
58
- # lambda { Exchange::ExternalAPI::Call.new('XML_API', :format => :xml) }.should raise_error(Exchange::ExternalAPI::APIError)
59
- # end
60
- # end
52
+ context "with http errors" do
53
+ it "should recall and deliver the result if possible" do
54
+ @count = 0
55
+ @uri_mock.should_receive(:open).at_most(3).times.and_return do
56
+ @count += 1
57
+ @count == 3 ? mock('opened', :read => fixture('api_responses/example_xml_api.xml')) : raise(OpenURI::HTTPError.new('404', 'URI'))
58
+ end
59
+ Exchange::ExternalAPI::Call.new('XML_API', :format => :xml) do |result|
60
+ result.to_s.should == Nokogiri.parse(fixture('api_responses/example_xml_api.xml')).to_s
61
+ end
62
+ end
63
+ it "should raise if the maximum recall size is reached" do
64
+ @uri_mock.should_receive(:open).at_most(5).times.and_return do
65
+ raise OpenURI::HTTPError.new('404', 'URI')
66
+ end
67
+ lambda { Exchange::ExternalAPI::Call.new('XML_API') }.should raise_error(Exchange::ExternalAPI::APIError)
68
+ end
69
+ end
61
70
  context "with socket errors" do
62
71
  it "should raise an error immediately" do
63
72
  @uri_mock.should_receive(:open).once.and_raise(SocketError)