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.
- data/.travis.yml +1 -0
- data/README.rdoc +5 -3
- data/VERSION +1 -1
- data/exchange.gemspec +16 -2
- data/iso4217.yml +589 -0
- data/lib/core_extensions/conversability.rb +12 -7
- data/lib/exchange.rb +7 -1
- data/lib/exchange/cache.rb +2 -0
- data/lib/exchange/cache/base.rb +9 -5
- data/lib/exchange/cache/file.rb +65 -0
- data/lib/exchange/cache/memcached.rb +4 -4
- data/lib/exchange/cache/no_cache.rb +33 -0
- data/lib/exchange/cache/rails.rb +2 -2
- data/lib/exchange/cache/redis.rb +5 -5
- data/lib/exchange/configuration.rb +23 -7
- data/lib/exchange/currency.rb +94 -40
- data/lib/exchange/external_api.rb +1 -0
- data/lib/exchange/external_api/base.rb +11 -19
- data/lib/exchange/external_api/call.rb +10 -12
- data/lib/exchange/external_api/currency_bot.rb +2 -2
- data/lib/exchange/external_api/ecb.rb +68 -0
- data/lib/exchange/external_api/xavier_media.rb +4 -3
- data/lib/exchange/helper.rb +27 -0
- data/lib/exchange/iso_4217.rb +95 -0
- data/spec/core_extensions/conversability_spec.rb +40 -6
- data/spec/exchange/cache/base_spec.rb +4 -4
- data/spec/exchange/cache/file_spec.rb +70 -0
- data/spec/exchange/cache/memcached_spec.rb +5 -2
- data/spec/exchange/cache/no_cache_spec.rb +27 -0
- data/spec/exchange/cache/rails_spec.rb +6 -3
- data/spec/exchange/cache/redis_spec.rb +5 -2
- data/spec/exchange/currency_spec.rb +86 -23
- data/spec/exchange/external_api/base_spec.rb +8 -5
- data/spec/exchange/external_api/call_spec.rb +38 -29
- data/spec/exchange/external_api/currency_bot_spec.rb +8 -10
- data/spec/exchange/external_api/ecb_spec.rb +55 -0
- data/spec/exchange/external_api/xavier_media_spec.rb +8 -8
- data/spec/exchange/helper_spec.rb +30 -0
- data/spec/exchange/iso_4217_spec.rb +45 -0
- data/spec/support/api_responses/example_ecb_xml_90d.xml +64 -0
- data/spec/support/api_responses/example_ecb_xml_daily.xml +44 -0
- data/spec/support/api_responses/example_ecb_xml_history.xml +64 -0
- 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',
|
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',
|
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',
|
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',
|
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',
|
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',
|
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',
|
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
|
-
(
|
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
|
-
((
|
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
|
-
(
|
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(
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
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.
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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, :
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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)
|