exchange 0.2.6
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/.document +5 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +262 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/exchange.gemspec +102 -0
- data/lib/core_extensions/conversability.rb +32 -0
- data/lib/exchange.rb +11 -0
- data/lib/exchange/cache.rb +4 -0
- data/lib/exchange/cache/base.rb +51 -0
- data/lib/exchange/cache/memcached.rb +52 -0
- data/lib/exchange/cache/rails.rb +45 -0
- data/lib/exchange/cache/redis.rb +54 -0
- data/lib/exchange/configuration.rb +72 -0
- data/lib/exchange/currency.rb +237 -0
- data/lib/exchange/external_api.rb +4 -0
- data/lib/exchange/external_api/base.rb +114 -0
- data/lib/exchange/external_api/call.rb +76 -0
- data/lib/exchange/external_api/currency_bot.rb +47 -0
- data/lib/exchange/external_api/xavier_media.rb +47 -0
- data/spec/core_extensions/conversability_spec.rb +64 -0
- data/spec/exchange/cache/base_spec.rb +29 -0
- data/spec/exchange/cache/memcached_spec.rb +72 -0
- data/spec/exchange/cache/rails_spec.rb +67 -0
- data/spec/exchange/cache/redis_spec.rb +76 -0
- data/spec/exchange/configuration_spec.rb +47 -0
- data/spec/exchange/currency_spec.rb +219 -0
- data/spec/exchange/external_api/base_spec.rb +31 -0
- data/spec/exchange/external_api/call_spec.rb +68 -0
- data/spec/exchange/external_api/currency_bot_spec.rb +61 -0
- data/spec/exchange/external_api/xavier_media_spec.rb +59 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/api_responses/example_historic_json.json +167 -0
- data/spec/support/api_responses/example_json_api.json +167 -0
- data/spec/support/api_responses/example_xml_api.xml +156 -0
- metadata +191 -0
@@ -0,0 +1,67 @@
|
|
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::Rails }
|
9
|
+
before(:each) do
|
10
|
+
Exchange::Configuration.define do |c|
|
11
|
+
c.cache = :rails
|
12
|
+
end
|
13
|
+
end
|
14
|
+
after(:each) do
|
15
|
+
Exchange::Configuration.define do |c|
|
16
|
+
c.cache = :memcached
|
17
|
+
end
|
18
|
+
end
|
19
|
+
describe "client" do
|
20
|
+
let(:client) { mock('rails_cache') }
|
21
|
+
it "should set up a client on the specified host and port for the cache" do
|
22
|
+
::Rails.should_receive(:cache).and_return(client)
|
23
|
+
subject.client.should == client
|
24
|
+
end
|
25
|
+
end
|
26
|
+
describe "cached" do
|
27
|
+
context "when a result is returned" do
|
28
|
+
let(:client) { mock('rails_cache') }
|
29
|
+
context "with a daily cache" do
|
30
|
+
before(:each) do
|
31
|
+
subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
|
32
|
+
::Rails.should_receive(:cache).and_return(client)
|
33
|
+
client.should_receive(:fetch).with('KEY', :expires_in => 86400).and_return "{\"RESULT\":\"YAY\"}"
|
34
|
+
end
|
35
|
+
it "should return the JSON loaded result" do
|
36
|
+
subject.cached('API_CLASS') { 'something' }.should == "{\"RESULT\":\"YAY\"}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
context "with an hourly cache" do
|
40
|
+
before(:each) do
|
41
|
+
Exchange::Configuration.update = :hourly
|
42
|
+
subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
|
43
|
+
::Rails.should_receive(:cache).and_return(client)
|
44
|
+
client.should_receive(:fetch).with('KEY', :expires_in => 3600).and_return "{\"RESULT\":\"YAY\"}"
|
45
|
+
end
|
46
|
+
after(:each) do
|
47
|
+
Exchange::Configuration.update = :daily
|
48
|
+
end
|
49
|
+
it "should return the JSON loaded result" do
|
50
|
+
subject.cached('API_CLASS') { 'something' }.should == "{\"RESULT\":\"YAY\"}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
context "when no result is returned" do
|
55
|
+
let(:client) { mock('rails_cache') }
|
56
|
+
before(:each) do
|
57
|
+
subject.should_receive(:key).with('API_CLASS', nil).at_most(3).times.and_return('KEY')
|
58
|
+
::Rails.should_receive(:cache).twice.and_return(client)
|
59
|
+
client.should_receive(:fetch).with('KEY', an_instance_of(Hash)).and_return nil
|
60
|
+
client.should_receive(:delete).with('KEY').once
|
61
|
+
end
|
62
|
+
it "should call the block and delete the key to avoid empty caches" do
|
63
|
+
subject.cached('API_CLASS') { {'RESULT' => 'YAY'} }.should be_nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Exchange::Cache::Redis" do
|
4
|
+
subject { Exchange::Cache::Redis }
|
5
|
+
before(:each) do
|
6
|
+
Exchange::Configuration.define do |c|
|
7
|
+
c.cache = :redis
|
8
|
+
c.cache_host = 'HOST'
|
9
|
+
c.cache_port = 'PORT'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
after(:each) do
|
13
|
+
Exchange::Configuration.define do |c|
|
14
|
+
c.cache = :memcached
|
15
|
+
c.cache_host = 'localhost'
|
16
|
+
c.cache_port = 11211
|
17
|
+
end
|
18
|
+
end
|
19
|
+
describe "client" do
|
20
|
+
let(:client) { mock('redis') }
|
21
|
+
after(:each) do
|
22
|
+
subject.send(:remove_class_variable, "@@client")
|
23
|
+
end
|
24
|
+
it "should set up a client on the specified host and port for the cache" do
|
25
|
+
::Redis.should_receive(:new).with(:host => 'HOST', :port => 'PORT').and_return(client)
|
26
|
+
subject.client.should == client
|
27
|
+
end
|
28
|
+
end
|
29
|
+
describe "cached" do
|
30
|
+
context "when a cached result exists" do
|
31
|
+
let(:client) { mock('redis') }
|
32
|
+
before(:each) do
|
33
|
+
subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
|
34
|
+
::Redis.should_receive(:new).with(:host => 'HOST', :port => 'PORT').and_return(client)
|
35
|
+
client.should_receive(:get).with('KEY').and_return "{\"RESULT\":\"YAY\"}"
|
36
|
+
end
|
37
|
+
after(:each) do
|
38
|
+
subject.send(:remove_class_variable, "@@client")
|
39
|
+
end
|
40
|
+
it "should return the JSON loaded result" do
|
41
|
+
subject.cached('API_CLASS') { 'something' }.should == {'RESULT' => 'YAY'}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
context "when no cached result exists" do
|
45
|
+
let(:client) { mock('redis') }
|
46
|
+
before(:each) do
|
47
|
+
subject.should_receive(:key).with('API_CLASS', nil).at_most(3).times.and_return('KEY')
|
48
|
+
::Redis.should_receive(:new).with(:host => 'HOST', :port => 'PORT').and_return(client)
|
49
|
+
client.should_receive(:get).with('KEY').and_return nil
|
50
|
+
end
|
51
|
+
after(:each) do
|
52
|
+
subject.send(:remove_class_variable, "@@client")
|
53
|
+
end
|
54
|
+
context "with daily cache" do
|
55
|
+
it "should call the block and set and return the result" do
|
56
|
+
client.should_receive(:set).with('KEY', "{\"RESULT\":\"YAY\"}").once
|
57
|
+
client.should_receive(:expire).with('KEY', 86400).once
|
58
|
+
subject.cached('API_CLASS') { {'RESULT' => 'YAY'} }.should == {'RESULT' => 'YAY'}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
context "with hourly cache" do
|
62
|
+
before(:each) do
|
63
|
+
Exchange::Configuration.update = :hourly
|
64
|
+
end
|
65
|
+
after(:each) do
|
66
|
+
Exchange::Configuration.update = :daily
|
67
|
+
end
|
68
|
+
it "should call the block and set and return the result" do
|
69
|
+
client.should_receive(:set).with('KEY', "{\"RESULT\":\"YAY\"}").once
|
70
|
+
client.should_receive(:expire).with('KEY', 3600).once
|
71
|
+
subject.cached('API_CLASS') { {'RESULT' => 'YAY'} }.should == {'RESULT' => 'YAY'}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Exchange::Configuration" do
|
4
|
+
let(:subject) { Exchange::Configuration }
|
5
|
+
it "should have a standard configuration" do
|
6
|
+
subject.api.should == :currency_bot
|
7
|
+
subject.api_class.should == Exchange::ExternalAPI::CurrencyBot
|
8
|
+
subject.cache.should == :memcached
|
9
|
+
subject.cache_class.should == Exchange::Cache::Memcached
|
10
|
+
subject.cache_host.should == 'localhost'
|
11
|
+
subject.cache_port.should == 11211
|
12
|
+
subject.retries.should == 5
|
13
|
+
end
|
14
|
+
it "should respond to all configuration getters and setters" do
|
15
|
+
[:api, :retries, :allow_mixed_operations, :cache, :cache_host, :cache_port, :update].each do |k|
|
16
|
+
subject.should be_respond_to(k)
|
17
|
+
subject.should be_respond_to(:"#{k}=")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
it "should allow to be defined with a block" do
|
21
|
+
subject.define do |c|
|
22
|
+
c.api = :xavier_media
|
23
|
+
c.cache = :redis
|
24
|
+
c.retries = 60
|
25
|
+
end
|
26
|
+
subject.api.should == :xavier_media
|
27
|
+
subject.api_class.should == Exchange::ExternalAPI::XavierMedia
|
28
|
+
subject.cache.should == :redis
|
29
|
+
subject.cache_class.should == Exchange::Cache::Redis
|
30
|
+
subject.retries.should == 60
|
31
|
+
end
|
32
|
+
it "should allow to be set directly" do
|
33
|
+
subject.api = :paypal
|
34
|
+
subject.cache = :yml
|
35
|
+
subject.retries = 1
|
36
|
+
subject.api.should == :paypal
|
37
|
+
subject.cache.should == :yml
|
38
|
+
subject.retries.should == 1
|
39
|
+
end
|
40
|
+
after(:all) do
|
41
|
+
subject.api = :currency_bot
|
42
|
+
subject.cache = :memcached
|
43
|
+
subject.cache_host = 'localhost'
|
44
|
+
subject.cache_port = 11211
|
45
|
+
subject.retries = 5
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Exchange::Currency" do
|
4
|
+
subject { Exchange::Currency.new(40, :usd) }
|
5
|
+
before(:all) do
|
6
|
+
Exchange::Configuration.define do |c|
|
7
|
+
c.api = :currency_bot
|
8
|
+
c.cache = false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
after(:all) do
|
12
|
+
Exchange::Configuration.cache = :memcached
|
13
|
+
end
|
14
|
+
it "should initialize with a number and a currency" do
|
15
|
+
subject.value.should == 40
|
16
|
+
subject.currency.should == :usd
|
17
|
+
end
|
18
|
+
describe "convert_to" do
|
19
|
+
it "should be able to convert itself to other currencies" do
|
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
|
22
|
+
subject.convert_to(:chf).currency.should == :chf
|
23
|
+
subject.convert_to(:chf).should be_kind_of Exchange::Currency
|
24
|
+
end
|
25
|
+
end
|
26
|
+
describe "operations" do
|
27
|
+
describe "+ other" do
|
28
|
+
it "should be able to add an integer" do
|
29
|
+
(subject + 40).value.should == 80
|
30
|
+
end
|
31
|
+
it "should be able to add a float" do
|
32
|
+
(subject + 40.5).value.should == 80.5
|
33
|
+
end
|
34
|
+
it "should be able to add another currency value" do
|
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
|
37
|
+
(subject + Exchange::Currency.new(30, :sek)).currency.should == :usd
|
38
|
+
end
|
39
|
+
it "should raise when currencies get mixed and the configuration does not allow it" do
|
40
|
+
Exchange::Configuration.allow_mixed_operations = false
|
41
|
+
lambda { subject + Exchange::Currency.new(30, :chf) }.should raise_error(Exchange::CurrencyMixError)
|
42
|
+
Exchange::Configuration.allow_mixed_operations = true
|
43
|
+
end
|
44
|
+
it "should not raise when currencies get mixed and the configuration does not allow if the other currency is the same" do
|
45
|
+
Exchange::Configuration.allow_mixed_operations = false
|
46
|
+
mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
|
47
|
+
lambda { subject + Exchange::Currency.new(30, :usd) }.should_not raise_error
|
48
|
+
Exchange::Configuration.allow_mixed_operations = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
describe "- other" do
|
52
|
+
it "should be able to subtract an integer" do
|
53
|
+
(subject + 40).value.should == 80
|
54
|
+
end
|
55
|
+
it "should be able to subtract a float" do
|
56
|
+
(subject + 40.5).value.should == 80.5
|
57
|
+
end
|
58
|
+
it "should be able to subtract another currency value" do
|
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
|
61
|
+
(subject + Exchange::Currency.new(23.3, :eur)).currency.should == :usd
|
62
|
+
end
|
63
|
+
it "should raise when currencies get mixed and the configuration does not allow it" do
|
64
|
+
Exchange::Configuration.allow_mixed_operations = false
|
65
|
+
lambda { subject - Exchange::Currency.new(30, :chf) }.should raise_error(Exchange::CurrencyMixError)
|
66
|
+
Exchange::Configuration.allow_mixed_operations = true
|
67
|
+
end
|
68
|
+
it "should not raise when currencies get mixed and the configuration does not allow if the other currency is the same" do
|
69
|
+
Exchange::Configuration.allow_mixed_operations = false
|
70
|
+
mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
|
71
|
+
lambda { subject - Exchange::Currency.new(30, :usd) }.should_not raise_error
|
72
|
+
Exchange::Configuration.allow_mixed_operations = true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
describe "* other" do
|
76
|
+
it "should be able to multiply by an integer" do
|
77
|
+
(subject * 40).value.should == 1600
|
78
|
+
end
|
79
|
+
it "should be able to multiply a float" do
|
80
|
+
(subject * 40.5).value.should == 1620
|
81
|
+
end
|
82
|
+
it "should be able to multiply by another currency value" do
|
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
|
85
|
+
(subject * Exchange::Currency.new(23.3, :eur)).currency.should == :usd
|
86
|
+
end
|
87
|
+
it "should raise when currencies get mixed and the configuration does not allow it" do
|
88
|
+
Exchange::Configuration.allow_mixed_operations = false
|
89
|
+
lambda { subject * Exchange::Currency.new(30, :chf) }.should raise_error(Exchange::CurrencyMixError)
|
90
|
+
Exchange::Configuration.allow_mixed_operations = true
|
91
|
+
end
|
92
|
+
it "should not raise when currencies get mixed and the configuration does not allow if the other currency is the same" do
|
93
|
+
Exchange::Configuration.allow_mixed_operations = false
|
94
|
+
mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
|
95
|
+
lambda { subject * Exchange::Currency.new(30, :usd) }.should_not raise_error
|
96
|
+
Exchange::Configuration.allow_mixed_operations = true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
describe "/ other" do
|
100
|
+
it "should be able to multiply by an integer" do
|
101
|
+
(subject / 40).value.should == 1
|
102
|
+
end
|
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
|
105
|
+
end
|
106
|
+
it "should be able to multiply by another currency value" do
|
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
|
109
|
+
(subject / Exchange::Currency.new(23.3, :eur)).currency.should == :usd
|
110
|
+
end
|
111
|
+
it "should raise when currencies get mixed and the configuration does not allow it" do
|
112
|
+
Exchange::Configuration.allow_mixed_operations = false
|
113
|
+
lambda { subject / Exchange::Currency.new(30, :chf) }.should raise_error(Exchange::CurrencyMixError)
|
114
|
+
Exchange::Configuration.allow_mixed_operations = true
|
115
|
+
end
|
116
|
+
it "should not raise when currencies get mixed and the configuration does not allow if the other currency is the same" do
|
117
|
+
Exchange::Configuration.allow_mixed_operations = false
|
118
|
+
mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
|
119
|
+
lambda { subject / Exchange::Currency.new(30, :usd) }.should_not raise_error
|
120
|
+
Exchange::Configuration.allow_mixed_operations = true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
describe "comparison" do
|
124
|
+
subject { Exchange::Currency.new(40.123, :usd) }
|
125
|
+
let(:comp1) { Exchange::Currency.new(40.123, :usd) }
|
126
|
+
let(:comp2) { Exchange::Currency.new(40, :usd) }
|
127
|
+
let(:comp3) { Exchange::Currency.new(50, :eur) }
|
128
|
+
let(:comp4) { Exchange::Currency.new(45, :eur) }
|
129
|
+
let(:comp5) { Exchange::Currency.new(66.1, :usd) }
|
130
|
+
let(:comp6) { Exchange::Currency.new(66.1, :usd, :at => Time.gm(2011,1,1)) }
|
131
|
+
before(:each) do
|
132
|
+
mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 2)
|
133
|
+
end
|
134
|
+
context "with identical currencies" do
|
135
|
+
it "should be true if the currency and the value is the same" do
|
136
|
+
(subject == comp1).should be_true
|
137
|
+
end
|
138
|
+
it "should be false if the value is different" do
|
139
|
+
(subject == comp2).should be_false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
context "with different currencies" do
|
143
|
+
it "should be true if the converted value is the same" do
|
144
|
+
(comp3 == comp5).should be_true
|
145
|
+
end
|
146
|
+
it "should be false if the converted value is different" do
|
147
|
+
(subject == comp4).should be_false
|
148
|
+
end
|
149
|
+
it "should be false if the currency is defined historic and the converted value is different" do
|
150
|
+
mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/historical/2011-01-01.json", fixture('api_responses/example_historic_json.json'), 2)
|
151
|
+
(comp3 == comp6).should be_false
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
describe "sorting" do
|
156
|
+
subject { Exchange::Currency.new(40.123, :usd) }
|
157
|
+
let(:comp1) { Exchange::Currency.new(40.123, :usd) }
|
158
|
+
let(:comp2) { Exchange::Currency.new(40, :usd) }
|
159
|
+
let(:comp3) { Exchange::Currency.new(50, :eur) }
|
160
|
+
let(:comp4) { Exchange::Currency.new(45, :eur) }
|
161
|
+
before(:each) do
|
162
|
+
mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 6)
|
163
|
+
end
|
164
|
+
it "should sort and by doing conversions" do
|
165
|
+
[subject, comp1, comp2, comp3, comp4].sort.should == [comp2, subject, comp1, comp4, comp3]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
describe "round" do
|
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
|
174
|
+
end
|
175
|
+
end
|
176
|
+
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
|
182
|
+
end
|
183
|
+
end
|
184
|
+
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
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
describe "methods via method missing" do
|
194
|
+
it "should be able to convert via to_currency to other currencies" do
|
195
|
+
mock_api("https://raw.github.com/currencybot/open-exchange-rates/master/latest.json", fixture('api_responses/example_json_api.json'), 6)
|
196
|
+
{'chf' => 36.5, 'usd' => 40.0, 'dkk' => 225.12, 'sek' => 269.85, 'nok' => 232.06, 'rub' => 1205.24}.each do |currency, value|
|
197
|
+
c = subject.send(:"to_#{currency}")
|
198
|
+
c.value.should == value
|
199
|
+
c.currency.should == currency
|
200
|
+
end
|
201
|
+
end
|
202
|
+
it "should be able to convert via to_currency to other currencies and use historic data" do
|
203
|
+
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
|
+
{'chf' => 36.5, 'usd' => 40.0, 'dkk' => 225.12, 'sek' => 269.85, 'nok' => 232.06, 'rub' => 1205.24}.each do |currency, value|
|
205
|
+
c = subject.send(:"to_#{currency}", :at => Time.gm(2011,10,9))
|
206
|
+
c.value.should == value
|
207
|
+
c.currency.should == currency
|
208
|
+
end
|
209
|
+
end
|
210
|
+
it "should use the own time if defined as historic to convert" do
|
211
|
+
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
|
+
5.eur(:at => Time.gm(2011,1,1)).to_usd.value.should == 5.eur.to_usd(:at => Time.gm(2011,1,1)).value
|
213
|
+
end
|
214
|
+
it "should pass on methods it does not understand to its number" do
|
215
|
+
subject.to_f.should == 40
|
216
|
+
lambda { subject.to_hell }.should raise_error(NoMethodError)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Exchange::ExternalAPI::Base" do
|
4
|
+
subject { Exchange::ExternalAPI::Base.new }
|
5
|
+
before(:each) do
|
6
|
+
subject.instance_variable_set("@rates", {'EUR' => 3.45, 'CHF' => 5.565})
|
7
|
+
subject.instance_variable_set("@base", 'usd')
|
8
|
+
end
|
9
|
+
describe "rate" do
|
10
|
+
it "should put out an exchange rate for the two currencies" do
|
11
|
+
subject.should_receive(:update).once
|
12
|
+
((subject.rate('eur', 'chf') * 10000).round.to_f / 10000).should == 1.613
|
13
|
+
end
|
14
|
+
it "should put out an exchange rate for the two currencies and pass on opts" do
|
15
|
+
time = Time.now
|
16
|
+
subject.should_receive(:update).with(:at => time).once
|
17
|
+
((subject.rate('eur', 'chf', :at => time) * 10000).round.to_f / 10000).should == 1.613
|
18
|
+
end
|
19
|
+
end
|
20
|
+
describe "convert" do
|
21
|
+
it "should convert according to the given rates" do
|
22
|
+
subject.should_receive(:update).once
|
23
|
+
subject.convert(80,'chf','eur').should == 49.6
|
24
|
+
end
|
25
|
+
it "should convert according to the given rates and pass opts" do
|
26
|
+
time = Time.now
|
27
|
+
subject.should_receive(:update).with(:at => time).once
|
28
|
+
subject.convert(80,'chf','eur', :at => time).should == 49.6
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Exchange::ExternalAPI::Call" do
|
4
|
+
before(:all) do
|
5
|
+
Exchange::Configuration.cache = false
|
6
|
+
end
|
7
|
+
describe "initialization" do
|
8
|
+
context "with a json api" do
|
9
|
+
before(:each) do
|
10
|
+
mock_api('JSON_API', fixture('api_responses/example_json_api.json'))
|
11
|
+
end
|
12
|
+
it "should call the api and yield a block with the result" do
|
13
|
+
Exchange::ExternalAPI::Call.new('JSON_API') do |result|
|
14
|
+
result.should == JSON.load(fixture('api_responses/example_json_api.json'))
|
15
|
+
end
|
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
|
31
|
+
context "with socket errors" do
|
32
|
+
it "should raise an error immediately" do
|
33
|
+
@uri_mock.should_receive(:open).at_most(5).times.and_raise(SocketError)
|
34
|
+
lambda { Exchange::ExternalAPI::Call.new('JSON_API') }.should raise_error(Exchange::ExternalAPI::APIError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
context "with an xml api" do
|
40
|
+
before(:each) do
|
41
|
+
mock_api('XML_API', fixture('api_responses/example_xml_api.xml'))
|
42
|
+
end
|
43
|
+
it "should call the api and yield a block with the result" do
|
44
|
+
Exchange::ExternalAPI::Call.new('XML_API', :format => :xml) do |result|
|
45
|
+
result.to_s.should == Nokogiri.parse(fixture('api_responses/example_xml_api.xml')).to_s
|
46
|
+
end
|
47
|
+
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
|
61
|
+
context "with socket errors" do
|
62
|
+
it "should raise an error immediately" do
|
63
|
+
@uri_mock.should_receive(:open).once.and_raise(SocketError)
|
64
|
+
lambda { Exchange::ExternalAPI::Call.new('XML_API', :format => :xml) }.should raise_error(Exchange::ExternalAPI::APIError)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|