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.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'rubygems'
|
2
3
|
require 'bigdecimal'
|
3
4
|
require 'open-uri'
|
@@ -11,4 +12,4 @@ require 'exchange/external_api'
|
|
11
12
|
require 'exchange/cache'
|
12
13
|
require 'exchange/configuration'
|
13
14
|
require 'exchange/core_extensions'
|
14
|
-
require 'exchange/typecasting'
|
15
|
+
require 'exchange/typecasting'
|
data/lib/exchange/base.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Exchange
|
2
3
|
|
3
4
|
# The current version of the exchange gem
|
4
5
|
#
|
5
|
-
VERSION = '0.
|
6
|
+
VERSION = '1.0.0'
|
6
7
|
|
7
8
|
# The root installation path of the gem
|
8
9
|
# @version 0.5
|
@@ -22,4 +23,4 @@ module Exchange
|
|
22
23
|
#
|
23
24
|
NoCurrencyError = Class.new(ArgumentError)
|
24
25
|
|
25
|
-
end
|
26
|
+
end
|
data/lib/exchange/cache.rb
CHANGED
data/lib/exchange/cache/base.rb
CHANGED
data/lib/exchange/cache/file.rb
CHANGED
data/lib/exchange/cache/rails.rb
CHANGED
data/lib/exchange/cache/redis.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'singleton'
|
2
3
|
require 'forwardable'
|
3
4
|
|
@@ -10,7 +11,7 @@ module Exchange
|
|
10
11
|
attr_accessor :subclass
|
11
12
|
|
12
13
|
def_delegators :instance, :subclass, :subclass=, :set
|
13
|
-
|
14
|
+
|
14
15
|
def subclass_with_constantize
|
15
16
|
self.subclass = parent_module.const_get camelize(self.subclass_without_constantize) unless !self.subclass_without_constantize || self.subclass_without_constantize.is_a?(Class)
|
16
17
|
subclass_without_constantize
|
@@ -55,4 +56,4 @@ module Exchange
|
|
55
56
|
|
56
57
|
end
|
57
58
|
|
58
|
-
end
|
59
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Exchange
|
2
3
|
|
3
4
|
class << self
|
@@ -67,7 +68,7 @@ module Exchange
|
|
67
68
|
end
|
68
69
|
|
69
70
|
# The configuration defaults
|
70
|
-
# @version 0
|
71
|
+
# @version 1.0
|
71
72
|
# @since 0.6
|
72
73
|
#
|
73
74
|
DEFAULTS = {
|
@@ -75,7 +76,8 @@ module Exchange
|
|
75
76
|
:subclass => ExternalAPI::XavierMedia,
|
76
77
|
:retries => 5,
|
77
78
|
:protocol => :http,
|
78
|
-
:app_id => nil
|
79
|
+
:app_id => nil,
|
80
|
+
:fallback => ExternalAPI::Ecb
|
79
81
|
},
|
80
82
|
:cache => {
|
81
83
|
:subclass => Cache::Memory,
|
@@ -143,8 +145,9 @@ module Exchange
|
|
143
145
|
# @since 0.6
|
144
146
|
# @version 0.6
|
145
147
|
# @param [Hash] The hash to set the configuration to
|
146
|
-
# @option [Symbol] :subclass The API subclass to use as a underscored symbol (will be camelized and constantized)
|
148
|
+
# @option [Symbol] :subclass The API subclass to use as a underscored symbol (will be camelized and constantized). Options available are :open_exchange_rates, :xavier_media, :ecb and :random (Random Rates for development use). You can build your own api subclass by following instructions under external_api/base.rb
|
147
149
|
# @option [Integer] :retries The amount of retries on connection failure
|
150
|
+
# @option [Array,Symbol,Class] :fallback An array of fallbacks to be tried in the event when the standard API lookup fails or the standard API does not support the given currency
|
148
151
|
# @example set the api to be ecb with a maximum of 8 retries on connection failure
|
149
152
|
# configuration.api = { :subclass => :ecb, :retries => 8 }
|
150
153
|
#
|
@@ -154,7 +157,7 @@ module Exchange
|
|
154
157
|
# @since 0.6
|
155
158
|
# @version 0.6
|
156
159
|
# @param [Hash] The hash to set the configuration to
|
157
|
-
# @option [Symbol] :subclass The Cache subclass to use as a underscored symbol (will be camelized and constantized)
|
160
|
+
# @option [Symbol] :subclass The Cache subclass to use as a underscored symbol (will be camelized and constantized). Options available are :memcached (via Dalli), :redis, :rails and :memory (default). You can build your own cache subclass by following instructions under cache/base.rb
|
158
161
|
# @option [String] :host The cache connection host
|
159
162
|
# @option [Integer] :port The cache connection port
|
160
163
|
# @option [Symbol] :expire The expiration period for the cache, can be :daily or :hourly, defaults to daily
|
@@ -176,4 +179,4 @@ module Exchange
|
|
176
179
|
install_getter :cache
|
177
180
|
|
178
181
|
end
|
179
|
-
end
|
182
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Exchange
|
2
3
|
module Cachify
|
3
4
|
|
@@ -22,4 +23,4 @@ Symbol.send :include, Exchange::Cachify
|
|
22
23
|
String.send :include, Exchange::Decachify
|
23
24
|
Hash.send :include, Exchange::Cachify
|
24
25
|
Array.send :include, Exchange::Cachify
|
25
|
-
NilClass.send :include, Exchange::Cachify
|
26
|
+
NilClass.send :include, Exchange::Cachify
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Exchange
|
2
3
|
|
3
4
|
# Make Floating Points forget about their incapabilities when dealing with money
|
@@ -41,4 +42,4 @@ module Exchange
|
|
41
42
|
|
42
43
|
end
|
43
44
|
|
44
|
-
Float.send(:include, Exchange::ErrorSafe)
|
45
|
+
Float.send(:include, Exchange::ErrorSafe)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Exchange
|
2
3
|
|
3
4
|
# The conversability module which will get included in Fixnum and Float, giving them the in currency instantiate methods
|
@@ -35,4 +36,4 @@ module Exchange
|
|
35
36
|
end
|
36
37
|
|
37
38
|
# include the Conversability methods in all number operations. Stack traces will indicate the module if something goes wrong.
|
38
|
-
Numeric.send :include, Exchange::Conversability
|
39
|
+
Numeric.send :include, Exchange::Conversability
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'exchange/external_api/configuration'
|
2
3
|
require 'exchange/external_api/base'
|
3
4
|
require 'exchange/external_api/xml'
|
@@ -5,4 +6,5 @@ require 'exchange/external_api/json'
|
|
5
6
|
require 'exchange/external_api/call'
|
6
7
|
require 'exchange/external_api/open_exchange_rates'
|
7
8
|
require 'exchange/external_api/xavier_media'
|
8
|
-
require 'exchange/external_api/ecb'
|
9
|
+
require 'exchange/external_api/ecb'
|
10
|
+
require 'exchange/external_api/random'
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Exchange
|
2
3
|
module ExternalAPI
|
3
4
|
# @author Beat Richartz
|
@@ -8,9 +9,23 @@ module Exchange
|
|
8
9
|
#
|
9
10
|
class Configuration < Exchange::Configurable
|
10
11
|
|
11
|
-
attr_accessor :retries, :app_id, :protocol
|
12
|
+
attr_accessor :retries, :app_id, :protocol, :fallback
|
12
13
|
|
13
|
-
def_delegators :instance, :retries, :retries=, :app_id, :app_id=, :protocol, :protocol=
|
14
|
+
def_delegators :instance, :retries, :retries=, :app_id, :app_id=, :protocol, :protocol=, :fallback, :fallback=
|
15
|
+
|
16
|
+
def fallback_with_constantize
|
17
|
+
self.fallback = Array(fallback_without_constantize).map do |fb|
|
18
|
+
unless !fb || fb.is_a?(Class)
|
19
|
+
parent_module.const_get camelize(fb)
|
20
|
+
else
|
21
|
+
fb
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
fallback_without_constantize
|
26
|
+
end
|
27
|
+
alias_method :fallback_without_constantize, :fallback
|
28
|
+
alias_method :fallback, :fallback_with_constantize
|
14
29
|
|
15
30
|
def parent_module
|
16
31
|
ExternalAPI
|
@@ -22,4 +37,4 @@ module Exchange
|
|
22
37
|
|
23
38
|
end
|
24
39
|
end
|
25
|
-
end
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Exchange
|
3
|
+
module ExternalAPI
|
4
|
+
|
5
|
+
# The Random API class, which is only intended for use in development mode. Returns random exchange rates.
|
6
|
+
# @author Beat Richartz
|
7
|
+
# @version 1.0
|
8
|
+
# @since 1.0
|
9
|
+
#
|
10
|
+
class Random < Base
|
11
|
+
|
12
|
+
CURRENCIES = Exchange::ISO.currencies
|
13
|
+
RANDOM_RATES = lambda { Hash[*CURRENCIES.zip(CURRENCIES.size.times.map{|i| rand}).flatten] }
|
14
|
+
|
15
|
+
# Updates the rates with new random ones
|
16
|
+
# The call gets cached for a maximum of 24 hours.
|
17
|
+
# @version 0.7
|
18
|
+
# @param [Hash] opts Options to define for the API Call
|
19
|
+
# @option opts [Time, String] :at a historical date to get the exchange rates for
|
20
|
+
# @example Update the currency bot API to use the file of March 2, 2010
|
21
|
+
# Exchange::ExternalAPI::XavierMedia.new.update(:at => Time.gm(3,2,2010))
|
22
|
+
#
|
23
|
+
def update(opts={})
|
24
|
+
@base = :usd
|
25
|
+
@rates = RANDOM_RATES.call
|
26
|
+
@timestamp = Time.now.to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/exchange/gem_loader.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Exchange
|
2
3
|
|
3
4
|
# The gem loader takes care of loading gems without adding too many unnecessary dependencies to the gem
|
@@ -32,4 +33,4 @@ module Exchange
|
|
32
33
|
raise GemNotFoundError.new("You specified #{@gem} to be used with Exchange, yet it is not loadable. Please install #{@gem} to be able to use it with Exchange")
|
33
34
|
end
|
34
35
|
end
|
35
|
-
end
|
36
|
+
end
|
data/lib/exchange/helper.rb
CHANGED
data/lib/exchange/iso.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'singleton'
|
2
3
|
require 'forwardable'
|
3
4
|
require 'yaml'
|
@@ -22,9 +23,16 @@ module Exchange
|
|
22
23
|
|
23
24
|
def install_operation op
|
24
25
|
self.class_eval <<-EOV
|
25
|
-
def #{op}(amount, currency, precision=nil)
|
26
|
+
def #{op}(amount, currency, precision=nil, opts={})
|
26
27
|
minor = definitions[currency][:minor_unit]
|
27
|
-
|
28
|
+
money = amount.is_a?(BigDecimal) ? amount : BigDecimal.new(amount.to_s, precision_for(amount, currency))
|
29
|
+
if opts[:psych] && minor > 0
|
30
|
+
money.#{op}(0) - BigDecimal.new((1.0/(10**minor)).to_s)
|
31
|
+
elsif opts[:psych]
|
32
|
+
(((money.#{op}(0) / BigDecimal.new("10.0")).#{op}(0)) - BigDecimal.new("0.1")) * BigDecimal.new("10")
|
33
|
+
else
|
34
|
+
money.#{op}(precision || minor)
|
35
|
+
end
|
28
36
|
end
|
29
37
|
EOV
|
30
38
|
end
|
@@ -92,7 +100,7 @@ module Exchange
|
|
92
100
|
# @param [BigDecimal, Fixed, Float] amount The amount of currency you want to stringify
|
93
101
|
# @param [String, Symbol] currency The currency you want to stringify
|
94
102
|
# @param [Hash] opts The options for formatting
|
95
|
-
# @option opts [Boolean] :
|
103
|
+
# @option opts [Boolean] :format The format to put the string out in: :amount for only the amount, :symbol for a string with a currency symbol
|
96
104
|
# @return [String] The formatted string
|
97
105
|
# @example Convert a currency to a string
|
98
106
|
# Exchange::ISO.stringify(49.567, :usd) #=> "USD 49.57"
|
@@ -104,9 +112,22 @@ module Exchange
|
|
104
112
|
# Exchange::ISO.stringif(34.34, :omr, :amount_only => true) #=> "34.340"
|
105
113
|
#
|
106
114
|
def stringify(amount, currency, opts={})
|
107
|
-
|
108
|
-
|
109
|
-
"
|
115
|
+
definition = definitions[currency]
|
116
|
+
separators = definition[:separators] || {}
|
117
|
+
format = "%.#{definition[:minor_unit]}f"
|
118
|
+
string = format % amount
|
119
|
+
major, minor = string.split('.')
|
120
|
+
|
121
|
+
if separators[:major]
|
122
|
+
major.reverse!
|
123
|
+
major.gsub!(/(\d{3})(?=.)/) { $1 + separators[:major] }
|
124
|
+
major.reverse!
|
125
|
+
end
|
126
|
+
|
127
|
+
string = minor ? major + (separators[:minor] || '.') + minor : major
|
128
|
+
pre = [opts[:format] == :amount && '', opts[:format] == :symbol && definition[:symbol], currency.to_s.upcase + ' '].detect{|a| a.is_a?(String)}
|
129
|
+
|
130
|
+
"#{pre}#{string}"
|
110
131
|
end
|
111
132
|
|
112
133
|
# Use this to round a currency amount. This allows us to round exactly to the number of minors the currency has in the
|
@@ -148,12 +169,7 @@ module Exchange
|
|
148
169
|
new_hsh = Hash.new
|
149
170
|
|
150
171
|
hsh.each_pair do |k,v|
|
151
|
-
if v.is_a?(Hash)
|
152
|
-
v.keys.each do |key|
|
153
|
-
v[key.to_sym] = v.delete(key)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
172
|
+
v = symbolize_keys v if v.is_a?(Hash)
|
157
173
|
new_hsh[k.downcase.to_sym] = v
|
158
174
|
end
|
159
175
|
|
@@ -172,4 +188,4 @@ module Exchange
|
|
172
188
|
end
|
173
189
|
|
174
190
|
end
|
175
|
-
end
|
191
|
+
end
|