exchange 0.12.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -0
- data/README.rdoc +34 -6
- data/Rakefile +3 -30
- data/changelog.rdoc +6 -0
- data/exchange.gemspec +3 -6
- data/iso4217.yml +238 -81
- data/lib/exchange.rb +2 -1
- data/lib/exchange/base.rb +3 -2
- data/lib/exchange/cache.rb +1 -0
- data/lib/exchange/cache/base.rb +2 -1
- data/lib/exchange/cache/configuration.rb +2 -1
- data/lib/exchange/cache/file.rb +2 -1
- data/lib/exchange/cache/memcached.rb +2 -1
- data/lib/exchange/cache/memory.rb +2 -1
- data/lib/exchange/cache/no_cache.rb +2 -1
- data/lib/exchange/cache/rails.rb +2 -1
- data/lib/exchange/cache/redis.rb +2 -1
- data/lib/exchange/configurable.rb +3 -2
- data/lib/exchange/configuration.rb +8 -5
- data/lib/exchange/core_extensions.rb +2 -1
- data/lib/exchange/core_extensions/cachify.rb +2 -1
- data/lib/exchange/core_extensions/float/error_safe.rb +2 -1
- data/lib/exchange/core_extensions/numeric/conversability.rb +2 -1
- data/lib/exchange/external_api.rb +3 -1
- data/lib/exchange/external_api/base.rb +2 -1
- data/lib/exchange/external_api/call.rb +2 -1
- data/lib/exchange/external_api/configuration.rb +18 -3
- data/lib/exchange/external_api/ecb.rb +2 -1
- data/lib/exchange/external_api/json.rb +2 -1
- data/lib/exchange/external_api/open_exchange_rates.rb +2 -1
- data/lib/exchange/external_api/random.rb +31 -0
- data/lib/exchange/external_api/xavier_media.rb +2 -1
- data/lib/exchange/external_api/xml.rb +2 -1
- data/lib/exchange/gem_loader.rb +2 -1
- data/lib/exchange/helper.rb +2 -1
- data/lib/exchange/iso.rb +29 -13
- data/lib/exchange/money.rb +35 -9
- data/lib/exchange/typecasting.rb +2 -1
- data/spec/exchange/cache/base_spec.rb +2 -1
- data/spec/exchange/cache/configuration_spec.rb +4 -1
- data/spec/exchange/cache/file_spec.rb +2 -1
- data/spec/exchange/cache/memcached_spec.rb +2 -1
- data/spec/exchange/cache/memory_spec.rb +4 -2
- data/spec/exchange/cache/no_cache_spec.rb +2 -1
- data/spec/exchange/cache/rails_spec.rb +2 -1
- data/spec/exchange/cache/redis_spec.rb +2 -1
- data/spec/exchange/configuration_spec.rb +6 -2
- data/spec/exchange/core_extensions/array/cachify_spec.rb +2 -1
- data/spec/exchange/core_extensions/float/error_safe_spec.rb +2 -1
- data/spec/exchange/core_extensions/hash/cachify_spec.rb +2 -1
- data/spec/exchange/core_extensions/numeric/cachify_spec.rb +2 -1
- data/spec/exchange/core_extensions/numeric/conversability_spec.rb +2 -1
- data/spec/exchange/core_extensions/string/cachify_spec.rb +2 -1
- data/spec/exchange/core_extensions/symbol/cachify_spec.rb +2 -1
- data/spec/exchange/external_api/base_spec.rb +2 -1
- data/spec/exchange/external_api/call_spec.rb +2 -1
- data/spec/exchange/external_api/configuration_spec.rb +18 -2
- data/spec/exchange/external_api/ecb_spec.rb +2 -1
- data/spec/exchange/external_api/open_exchange_rates_spec.rb +2 -1
- data/spec/exchange/external_api/random_spec.rb +40 -0
- data/spec/exchange/external_api/xavier_media_spec.rb +2 -1
- data/spec/exchange/gem_loader_spec.rb +2 -1
- data/spec/exchange/helper_spec.rb +2 -1
- data/spec/exchange/iso_spec.rb +214 -189
- data/spec/exchange/money_spec.rb +65 -2
- data/spec/exchange/typecasting_spec.rb +2 -1
- data/spec/spec_helper.rb +3 -1
- metadata +10 -10
- data/benchmark/benchmark.rb +0 -50
data/lib/exchange/money.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
# Top Level Module of the the gem.
|
2
3
|
# @author Beat Richartz
|
3
4
|
# @version 0.9
|
@@ -88,9 +89,17 @@ module Exchange
|
|
88
89
|
if api_supports_currency?(other)
|
89
90
|
opts = { :at => time, :from => self }.merge(options)
|
90
91
|
Money.new(api.new.convert(value, currency, other, opts), other, opts)
|
92
|
+
elsif fallback!
|
93
|
+
to other, options
|
91
94
|
else
|
92
95
|
raise_no_rate_error(other)
|
93
96
|
end
|
97
|
+
rescue ExternalAPI::APIError
|
98
|
+
if fallback!
|
99
|
+
to other, options
|
100
|
+
else
|
101
|
+
raise
|
102
|
+
end
|
94
103
|
end
|
95
104
|
alias :in :to
|
96
105
|
|
@@ -103,7 +112,8 @@ module Exchange
|
|
103
112
|
#
|
104
113
|
def install_operation op
|
105
114
|
define_method op do |*precision|
|
106
|
-
|
115
|
+
psych = precision.first == :psych
|
116
|
+
Exchange::Money.new(ISO.send(op, self.value, self.currency, psych ? nil : precision.first, {:psych => psych}), currency, :at => time, :from => self)
|
107
117
|
end
|
108
118
|
end
|
109
119
|
|
@@ -296,18 +306,34 @@ module Exchange
|
|
296
306
|
# Exchange::Money.new(45, :jpy).to_s #=> "JPY 45"
|
297
307
|
# @example Convert a currency with a three decimal minor to a string
|
298
308
|
# Exchange::Money.new(34.34, :omr).to_s #=> "OMR 34.340"
|
299
|
-
# @example Convert a currency to a string
|
300
|
-
# Exchange::
|
309
|
+
# @example Convert a currency with a three decimal minor to a string with a currency symbol
|
310
|
+
# Exchange::Money.new(34.34, :usd).to_s(:symbol) #=> "$34.34"
|
311
|
+
# @example Convert a currency with a three decimal minor to a string with just the amount
|
312
|
+
# Exchange::Money.new(34.34, :omr).to_s(:amount) #=> "34.340"
|
301
313
|
#
|
302
314
|
def to_s format=:currency
|
303
|
-
|
304
|
-
format == :currency && ISO.stringify(value, currency),
|
305
|
-
format == :amount && ISO.stringify(value, currency, :amount_only => true)
|
306
|
-
].detect{|l| l.is_a?(String) }
|
315
|
+
ISO.stringify(value, currency, :format => format)
|
307
316
|
end
|
308
317
|
|
309
318
|
private
|
310
319
|
|
320
|
+
# Fallback to the next api defined in the api fallbacks. Changes the api for the given instance
|
321
|
+
# @return [Boolean] true if the fallback was successful, false if not
|
322
|
+
# @since 1.0
|
323
|
+
# @version 1.0
|
324
|
+
#
|
325
|
+
def fallback!
|
326
|
+
fallback = Exchange.configuration.api.fallback
|
327
|
+
new_api = fallback.index(api) ? fallback[fallback.index(api) + 1] : fallback.first
|
328
|
+
|
329
|
+
if new_api
|
330
|
+
@api = new_api
|
331
|
+
return true
|
332
|
+
end
|
333
|
+
|
334
|
+
return false
|
335
|
+
end
|
336
|
+
|
311
337
|
# determine if another given object is an instance of Exchange::Money
|
312
338
|
# @param [Object] other The object to be tested against
|
313
339
|
# @return [Boolean] true if the other is an instance of Exchange::Money, false if not
|
@@ -363,7 +389,7 @@ module Exchange
|
|
363
389
|
# @version 0.7.2
|
364
390
|
#
|
365
391
|
def raise_no_rate_error other
|
366
|
-
raise NoRateError.new("Cannot convert to #{other} because the defined api
|
392
|
+
raise NoRateError.new("Cannot convert to #{other} because the defined api nor the fallbacks provide a rate")
|
367
393
|
end
|
368
394
|
|
369
395
|
end
|
@@ -372,4 +398,4 @@ module Exchange
|
|
372
398
|
#
|
373
399
|
ImplicitConversionError = Class.new(StandardError)
|
374
400
|
|
375
|
-
end
|
401
|
+
end
|
data/lib/exchange/typecasting.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "Exchange::Cache::Configuration" do
|
@@ -51,12 +52,14 @@ describe "Exchange::Cache::Configuration" do
|
|
51
52
|
end
|
52
53
|
it "should do so for the host" do
|
53
54
|
subject.subclass.client.should_not be_nil
|
55
|
+
subject.subclass.client
|
54
56
|
subject.subclass.instance.instance_variable_get("@client").should_not be_nil
|
55
57
|
subject.host = 'new'
|
56
58
|
subject.subclass.instance.instance_variable_get("@client").should be_nil
|
57
59
|
end
|
58
60
|
it "should do so for the port" do
|
59
61
|
subject.subclass.client.should_not be_nil
|
62
|
+
subject.subclass.client
|
60
63
|
subject.subclass.instance.instance_variable_get("@client").should_not be_nil
|
61
64
|
subject.port = 112
|
62
65
|
subject.subclass.instance.instance_variable_get("@client").should be_nil
|
@@ -91,4 +94,4 @@ describe "Exchange::Cache::Configuration" do
|
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
94
|
-
end
|
97
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "Exchange::Cache::Memory" do
|
@@ -37,7 +38,8 @@ describe "Exchange::Cache::Memory" do
|
|
37
38
|
end
|
38
39
|
it "should unset all expired instance variables" do
|
39
40
|
subject.send(:clean!)
|
40
|
-
subject.instance_variables.reject{|i| i.to_s == "@helper" }.map{|i| subject.instance_variable_get(i) }.compact.sort_by(&:to_s)
|
41
|
+
ivars = subject.instance_variables.reject{|i| i.to_s == "@helper" }.map{|i| subject.instance_variable_get(i) }.compact.sort_by(&:to_s)
|
42
|
+
ivars.select{|i| i.is_a?(String) }.should be_eql(%W(Valid1 Valid2 Valid3 Valid4))
|
41
43
|
end
|
42
44
|
end
|
43
45
|
describe "cached" do
|
@@ -119,4 +121,4 @@ describe "Exchange::Cache::Memory" do
|
|
119
121
|
end
|
120
122
|
end
|
121
123
|
end
|
122
|
-
end
|
124
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "Exchange::Cache::NoCache" do
|
@@ -20,4 +21,4 @@ describe "Exchange::Cache::NoCache" do
|
|
20
21
|
lambda { subject.cached('API_CLASS') }.should raise_error(Exchange::Cache::CachingWithoutBlockError)
|
21
22
|
end
|
22
23
|
end
|
23
|
-
end
|
24
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "Exchange::Configuration" do
|
@@ -28,7 +29,8 @@ describe "Exchange::Configuration" do
|
|
28
29
|
Exchange.configuration = Exchange::Configuration.new {|c|
|
29
30
|
c.api = {
|
30
31
|
:subclass => :xavier_media,
|
31
|
-
:retries => 60
|
32
|
+
:retries => 60,
|
33
|
+
:fallback => :open_exchange_rates
|
32
34
|
}
|
33
35
|
c.cache = {
|
34
36
|
:subclass => :redis
|
@@ -36,6 +38,7 @@ describe "Exchange::Configuration" do
|
|
36
38
|
}
|
37
39
|
Exchange.configuration.api.subclass.should == Exchange::ExternalAPI::XavierMedia
|
38
40
|
Exchange.configuration.api.retries.should == 60
|
41
|
+
Exchange.configuration.api.fallback.should == [Exchange::ExternalAPI::OpenExchangeRates]
|
39
42
|
Exchange.configuration.cache.subclass.should == Exchange::Cache::Redis
|
40
43
|
end
|
41
44
|
it "should allow to be set directly" do
|
@@ -66,6 +69,7 @@ describe "Exchange::Configuration" do
|
|
66
69
|
subject.api.subclass.should == Exchange::ExternalAPI::XavierMedia
|
67
70
|
subject.api.retries.should == 5
|
68
71
|
subject.api.app_id.should be_nil
|
72
|
+
subject.api.fallback.should == [Exchange::ExternalAPI::Ecb]
|
69
73
|
subject.cache.subclass.should == Exchange::Cache::Memory
|
70
74
|
subject.cache.host.should be_nil
|
71
75
|
subject.cache.port.should be_nil
|
@@ -76,4 +80,4 @@ describe "Exchange::Configuration" do
|
|
76
80
|
after(:all) do
|
77
81
|
Exchange.configuration.reset
|
78
82
|
end
|
79
|
-
end
|
83
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "Exchange::Conversability" do
|
@@ -101,4 +102,4 @@ describe "Exchange::Conversability" do
|
|
101
102
|
lambda { BigDecimal.new("3.25").in(:zzz) }.should raise_error(Exchange::NoCurrencyError, "zzz is not a currency nor a country code matchable to a currency")
|
102
103
|
end
|
103
104
|
end
|
104
|
-
end
|
105
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "Exchange::ExternalAPI::Configuration" do
|
@@ -23,14 +24,28 @@ describe "Exchange::ExternalAPI::Configuration" do
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
describe "fallback constantize" do
|
28
|
+
it "should automatically constantize a single fallback and wrap it in an array" do
|
29
|
+
subject.fallback = :xavier_media
|
30
|
+
|
31
|
+
subject.fallback.should == [Exchange::ExternalAPI::XavierMedia]
|
32
|
+
end
|
33
|
+
it "should automatically constantize the fallbacks" do
|
34
|
+
subject.fallback = [:xavier_media, :random]
|
35
|
+
|
36
|
+
subject.fallback.should == [Exchange::ExternalAPI::XavierMedia, Exchange::ExternalAPI::Random]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
26
40
|
describe "set" do
|
27
41
|
before(:each) do
|
28
|
-
@return = subject.set :subclass => :xavier_media, :retries => 55, :app_id => "KEY"
|
42
|
+
@return = subject.set :subclass => :xavier_media, :retries => 55, :app_id => "KEY", :fallback => [:random, :ecb]
|
29
43
|
end
|
30
44
|
it "should set the options given" do
|
31
45
|
subject.subclass.should == Exchange::ExternalAPI::XavierMedia
|
32
46
|
subject.retries.should == 55
|
33
47
|
subject.app_id.should == 'KEY'
|
48
|
+
subject.fallback.should == [Exchange::ExternalAPI::Random, Exchange::ExternalAPI::Ecb]
|
34
49
|
end
|
35
50
|
it "should return self" do
|
36
51
|
@return.should == subject
|
@@ -46,7 +61,8 @@ describe "Exchange::ExternalAPI::Configuration" do
|
|
46
61
|
subject.subclass.should == Exchange::ExternalAPI::XavierMedia
|
47
62
|
subject.retries.should == 5
|
48
63
|
subject.app_id.should be_nil
|
64
|
+
subject.fallback.should == [Exchange::ExternalAPI::Ecb]
|
49
65
|
end
|
50
66
|
end
|
51
67
|
|
52
|
-
end
|
68
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "Exchange::ExternalAPI::Ecb" do
|
@@ -59,4 +60,4 @@ describe "Exchange::ExternalAPI::Ecb" do
|
|
59
60
|
subject.convert(70, :sek, :usd, :at => Time.gm(2011,9,9)).round(2).should == 10.35
|
60
61
|
end
|
61
62
|
end
|
62
|
-
end
|
63
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "Exchange::ExternalAPI::OpenExchangeRates" do
|
@@ -67,4 +68,4 @@ describe "Exchange::ExternalAPI::OpenExchangeRates" do
|
|
67
68
|
subject.convert(70, :sek, :usd, :at => Time.gm(Time.now.year - 1,3,1)).round(2).should == 10.38
|
68
69
|
end
|
69
70
|
end
|
70
|
-
end
|
71
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe "Exchange::ExternalAPI::OpenExchangeRates" do
|
5
|
+
before(:all) do
|
6
|
+
Exchange.configuration = Exchange::Configuration.new{|c|
|
7
|
+
c.cache = {
|
8
|
+
:subclass => :no_cache
|
9
|
+
}
|
10
|
+
c.api = {
|
11
|
+
:subclass => :random
|
12
|
+
}
|
13
|
+
}
|
14
|
+
end
|
15
|
+
after(:all) do
|
16
|
+
Exchange.configuration.reset
|
17
|
+
end
|
18
|
+
describe "updating rates" do
|
19
|
+
subject { Exchange::ExternalAPI::Random.new }
|
20
|
+
it "should have usd as base currency" do
|
21
|
+
subject.update
|
22
|
+
subject.base.should == :usd
|
23
|
+
end
|
24
|
+
it "should set the timestamp from time.now" do
|
25
|
+
time = Time.now
|
26
|
+
time.stub! :now => time
|
27
|
+
subject.update
|
28
|
+
subject.timestamp.should == time.to_i
|
29
|
+
end
|
30
|
+
end
|
31
|
+
describe "rates" do
|
32
|
+
subject { Exchange::ExternalAPI::Random.new }
|
33
|
+
it "should provide a rate for every ISO currency" do
|
34
|
+
subject.update
|
35
|
+
Exchange::ISO.currencies.each do |c|
|
36
|
+
subject.rates[c].should_not be_nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|