exchange 0.12.0 → 1.0.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 +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
|