exchange 0.6.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +54 -0
- data/.travis.yml +4 -0
- data/Gemfile +1 -12
- data/Gemfile.lock +14 -28
- data/README.rdoc +91 -68
- data/changelog.rdoc +20 -1
- data/exchange-0.7.5.gem +0 -0
- data/exchange-0.7.6.gem +0 -0
- data/exchange.gemspec +22 -102
- data/lib/core_extensions/conversability.rb +5 -3
- data/lib/exchange/base.rb +1 -1
- data/lib/exchange/cache/base.rb +11 -3
- data/lib/exchange/cache/memcached.rb +4 -3
- data/lib/exchange/configuration.rb +5 -14
- data/lib/exchange/currency.rb +69 -19
- data/lib/exchange/external_api/base.rb +75 -15
- data/lib/exchange/external_api/call.rb +7 -4
- data/lib/exchange/external_api/currency_bot.rb +12 -2
- data/lib/exchange/external_api/ecb.rb +62 -8
- data/lib/exchange/external_api/json.rb +3 -13
- data/lib/exchange/external_api/xavier_media.rb +51 -8
- data/lib/exchange/helper.rb +1 -1
- data/lib/exchange/iso_4217.rb +1 -0
- data/spec/exchange/cache/memcached_spec.rb +41 -15
- data/spec/exchange/currency_spec.rb +185 -7
- data/spec/exchange/external_api/call_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- metadata +37 -31
- data/VERSION +0 -1
data/.gitignore
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
.rvmrc
|
2
|
+
|
3
|
+
# rcov generated
|
4
|
+
coverage
|
5
|
+
coverage.data
|
6
|
+
|
7
|
+
# rdoc generated
|
8
|
+
rdoc
|
9
|
+
|
10
|
+
# yard generated
|
11
|
+
doc
|
12
|
+
.yardoc
|
13
|
+
|
14
|
+
# bundler
|
15
|
+
.bundle
|
16
|
+
|
17
|
+
# filestore
|
18
|
+
exchange_filestore
|
19
|
+
|
20
|
+
# jeweler generated
|
21
|
+
pkg
|
22
|
+
|
23
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
24
|
+
#
|
25
|
+
# * Create a file at ~/.gitignore
|
26
|
+
# * Include files you want ignored
|
27
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
28
|
+
#
|
29
|
+
# After doing this, these files will be ignored in all your git projects,
|
30
|
+
# saving you from having to 'pollute' every project you touch with them
|
31
|
+
#
|
32
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
33
|
+
#
|
34
|
+
# For MacOS:
|
35
|
+
#
|
36
|
+
#.DS_Store
|
37
|
+
|
38
|
+
# For TextMate
|
39
|
+
#*.tmproj
|
40
|
+
#tmtags
|
41
|
+
|
42
|
+
# For emacs:
|
43
|
+
#*~
|
44
|
+
#\#*
|
45
|
+
#.\#*
|
46
|
+
|
47
|
+
# For vim:
|
48
|
+
#*.swp
|
49
|
+
|
50
|
+
# For redcar:
|
51
|
+
#.redcar
|
52
|
+
|
53
|
+
# For rubinius:
|
54
|
+
#*.rbc
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,26 +1,15 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
#gem "nokogiri", ">= 1.5.0"
|
5
|
-
#gem "json", ">= 1.6.5"
|
6
|
-
#gem "memcached", ">= 1.3.0"
|
7
|
-
#gem "redis", ">= 2.2.0"
|
8
|
-
|
9
|
-
# Add dependencies to develop your gem here.
|
10
|
-
# Include everything needed to run rake, tests, features, etc.
|
11
2
|
|
12
3
|
gemspec
|
13
4
|
|
14
5
|
group :development do
|
15
|
-
gem "yard", "
|
6
|
+
gem "yard", ">= 0.7.4"
|
16
7
|
gem "bundler", ">= 1.0.0"
|
17
|
-
gem "jeweler", "~> 1.8.3"
|
18
8
|
end
|
19
9
|
|
20
10
|
group :test do
|
21
11
|
gem "nokogiri", ">= 1.5.0", :require => false
|
22
12
|
gem "dalli", ">= 2.0.0", :require => false
|
23
13
|
gem "redis", ">= 2.2.0", :require => false
|
24
|
-
gem "shoulda", ">= 0"
|
25
14
|
gem "rspec"
|
26
15
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
exchange (0.6
|
4
|
+
exchange (0.7.6)
|
5
5
|
json (>= 1.0.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -9,43 +9,29 @@ GEM
|
|
9
9
|
specs:
|
10
10
|
dalli (2.2.1)
|
11
11
|
diff-lcs (1.1.3)
|
12
|
-
|
13
|
-
jeweler (1.8.3)
|
14
|
-
bundler (~> 1.0)
|
15
|
-
git (>= 1.2.5)
|
16
|
-
rake
|
17
|
-
rdoc
|
18
|
-
json (1.6.6)
|
12
|
+
json (1.7.5)
|
19
13
|
nokogiri (1.5.5)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
rspec-
|
26
|
-
|
27
|
-
|
28
|
-
rspec-core (2.9.0)
|
29
|
-
rspec-expectations (2.9.1)
|
14
|
+
nokogiri (1.5.5-java)
|
15
|
+
redis (3.0.2)
|
16
|
+
rspec (2.11.0)
|
17
|
+
rspec-core (~> 2.11.0)
|
18
|
+
rspec-expectations (~> 2.11.0)
|
19
|
+
rspec-mocks (~> 2.11.0)
|
20
|
+
rspec-core (2.11.1)
|
21
|
+
rspec-expectations (2.11.3)
|
30
22
|
diff-lcs (~> 1.1.3)
|
31
|
-
rspec-mocks (2.
|
32
|
-
|
33
|
-
shoulda-context (~> 1.0.0)
|
34
|
-
shoulda-matchers (~> 1.0.0)
|
35
|
-
shoulda-context (1.0.0)
|
36
|
-
shoulda-matchers (1.0.0)
|
37
|
-
yard (0.7.5)
|
23
|
+
rspec-mocks (2.11.3)
|
24
|
+
yard (0.8.2.1)
|
38
25
|
|
39
26
|
PLATFORMS
|
27
|
+
java
|
40
28
|
ruby
|
41
29
|
|
42
30
|
DEPENDENCIES
|
43
31
|
bundler (>= 1.0.0)
|
44
32
|
dalli (>= 2.0.0)
|
45
33
|
exchange!
|
46
|
-
jeweler (~> 1.8.3)
|
47
34
|
nokogiri (>= 1.5.0)
|
48
35
|
redis (>= 2.2.0)
|
49
36
|
rspec
|
50
|
-
|
51
|
-
yard (~> 0.7.4)
|
37
|
+
yard (>= 0.7.4)
|
data/README.rdoc
CHANGED
@@ -1,68 +1,58 @@
|
|
1
|
-
= exchange {<img src="https://secure.travis-ci.org/beatrichartz/exchange.png" />}[http://travis-ci.org/beatrichartz/exchange]
|
1
|
+
= exchange {<img src="https://secure.travis-ci.org/beatrichartz/exchange.png" />}[http://travis-ci.org/beatrichartz/exchange] {<img src="https://gemnasium.com/beatrichartz/exchange.png" alt="Dependency Status" />}[https://gemnasium.com/beatrichartz/exchange] {<img src="https://codeclimate.com/badge.png" />}[https://codeclimate.com/github/beatrichartz/exchange]
|
2
2
|
|
3
|
-
The Exchange Gem gives you easy access to currency functions directly on your Numbers. It
|
3
|
+
The Exchange Gem gives you easy access to currency functions directly on your Numbers. {It is tested against}[http://travis-ci.org/beatrichartz/exchange]: ruby 1.9, ruby 1.8.7, ree, rubinius (1.8 and 1.9 mode) and jruby
|
4
4
|
|
5
|
-
|
5
|
+
You can use it with just plain ruby projects, in Rails 2 and 3, Sinatra or whatever Framework you like.
|
6
6
|
|
7
|
-
==
|
8
|
-
- Memcached, Redis, JSON and Nokogiri have been removed as explicit dependencies of the gem. The gem will now try to load the gems
|
9
|
-
you want to use with it during runtime, and it will throw an exception if a gem required according to your configuration is not present.
|
10
|
-
- The cache class is now a singleton and no class singleton anymore. This makes it easier to write a seamlessly integrating custom cache class for it.
|
11
|
-
== 0.5
|
12
|
-
- Changed the currency bot api to openexchangerates.org and changed the default api to xaviermedia. Since the openexchangerates.org api still supports a free plan with reasonable conditions, it will still be supported by the exchange gem, although it may be moved to a separate gem in the future
|
13
|
-
== 0.4
|
14
|
-
- Some potential 0 values on conversions when caching with memcached have been fixed.
|
15
|
-
== 0.3
|
16
|
-
- The ECB API has been added to the standard apis
|
17
|
-
|
18
|
-
==== Features
|
7
|
+
== Features
|
19
8
|
|
20
9
|
=== Easy Conversion
|
21
10
|
|
22
|
-
|
11
|
+
Conversion of currencies does not get any easier
|
23
12
|
1.eur.to_usd
|
24
|
-
or
|
13
|
+
or better for historic dates
|
25
14
|
1.eur.to_usd(:at => Time.now - 84600)
|
26
|
-
which gets you an exchange at the rates of yesterday.
|
27
15
|
|
28
16
|
=== Only one request per day to keep you up to date
|
29
17
|
|
30
|
-
You're hitting the internet only daily to get new rates
|
18
|
+
You're hitting the internet only daily to get new rates (hourly updates are available if you're eager to have the absolutely newest ones)
|
31
19
|
|
32
20
|
=== ISO 4217 Currency formatting
|
33
21
|
On of the issues with currencies is: You never know the format they should be in. With Exchange, you can just use the currencies
|
34
|
-
to_s method, which takes care of the right format for you.
|
35
|
-
a string with the currency code in front, or just the amount in the right format
|
22
|
+
to_s method, which takes care of the right format for you. You can either have a string with the currency code in front, or just the amount in the right format
|
36
23
|
|
37
24
|
Exchange::Currency.new(49.567, :usd).to_s #=> "USD 49.57"
|
38
25
|
Exchange::Currency.new(45, :jpy).to_s #=> "JPY 45"
|
39
26
|
Exchange::Currency.new(34.34, :omr).to_s #=> "OMR 34.340"
|
40
27
|
Exchange::ISO4217.stringif(34.34, :omr).to_s(:iso) #=> "34.340"
|
41
28
|
|
42
|
-
|
29
|
+
|
30
|
+
=== Use three great APIs or your own
|
43
31
|
|
44
32
|
Three open APIs are already included:
|
45
33
|
|
46
|
-
- Xaviermedia
|
47
|
-
- European Central Bank
|
48
|
-
- Currency Bot
|
34
|
+
- {Xaviermedia}[http://www.xavierforum.com/viewtopic.php?f=5&t=10979&sid=671a685edbfa5dbec219fbc6793d5057]
|
35
|
+
- {European Central Bank}[http://www.ecb.int/stats/exchange/eurofxref/html/index.en.html]
|
36
|
+
- {Currency Bot}[http://currencybot.github.com/]
|
49
37
|
|
50
|
-
but if you have another API you like to use, it becomes as easy as writing one Class and two methods to use it
|
38
|
+
but if you have another API you like to use, it becomes as easy as writing one Class and two methods to use it.
|
39
|
+
{Example of a custom API Extension}[http://rubydoc.info/github/beatrichartz/exchange/Exchange/ExternalAPI]
|
51
40
|
|
52
41
|
=== Use great caches or your own great cache
|
53
42
|
|
54
|
-
|
43
|
+
Use one of three available caching solutions:
|
55
44
|
|
56
|
-
- Memcached via the
|
45
|
+
- Memcached via the Dalli gem
|
57
46
|
- Redis via the redis gem
|
58
47
|
- Rails cache (This gem does however not depend on rails)
|
59
48
|
|
60
|
-
But, same here, if you don't like any of these or want to use your own caching solution, it is as easy as writing one Class and two methods to use it
|
49
|
+
But, same here, if you don't like any of these or want to use your own caching solution, it is as easy as writing one Class and two methods to use it.
|
50
|
+
{Example of a custom cache extension}[http://rubydoc.info/github/beatrichartz/exchange/Exchange/Cache]
|
61
51
|
|
62
52
|
== Installation
|
63
53
|
=== Bundler / Rails
|
64
54
|
Add it to your Gemfile
|
65
|
-
gem "exchange", ">=0.
|
55
|
+
gem "exchange", ">=0.7.3"
|
66
56
|
=== Manually
|
67
57
|
Just install it as a gem
|
68
58
|
gem install exchange
|
@@ -157,7 +147,7 @@ Access the original currency and its value after conversion, even over multiple
|
|
157
147
|
|
158
148
|
You can configure the exchange gem to a variety of options, allowing you to control restrictions on operations, caching and which API the gem uses. Just set the configuration with
|
159
149
|
|
160
|
-
Exchange::Configuration.
|
150
|
+
Exchange.configuration = Exchange::Configuration.new do |c|
|
161
151
|
# your configuration goes here
|
162
152
|
end
|
163
153
|
|
@@ -166,49 +156,64 @@ You can configure the exchange gem to a variety of options, allowing you to cont
|
|
166
156
|
|
167
157
|
The options available are
|
168
158
|
|
169
|
-
:cache
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
159
|
+
:cache Takes the cache options as a hash, the options are:
|
160
|
+
:subclass (default :memcached) The cache subclass to use for caching. Available: Rails cache, Redis, Memcached, Filecache
|
161
|
+
:host (default '127.0.0.1') A string with the hostname or IP to set the cache host to. Does not have to be set for Rails cache
|
162
|
+
:port (default 11211) An integer for the cache port. Does not have to be set for Rails cache
|
163
|
+
:expire (default :daily) Which period is used to expire the cache, :daily or :hourly are available
|
164
|
+
|
165
|
+
:api Takes the conversion api as a hash, the options are:
|
166
|
+
:subclass (default :xavier_media) The api to use. Available: Xavier Media, ECB, Currency Bot
|
167
|
+
:retries (default 5) The number of times the gem should retry to connect to the api host
|
168
|
+
:app_id (default nil) The app id to use with your api request
|
169
|
+
|
174
170
|
:allow_mixed_opterations (default true) If set to false, Operations with with different currencies raise errors.
|
175
|
-
:update (default :daily) The regularity of updates for the API. Possible values: :daily, :hourly.
|
176
171
|
|
177
|
-
If
|
178
|
-
Exchange
|
172
|
+
If you're afraid of mixed currency operations, just don't allow them
|
173
|
+
Exchange.configuration.allow_mixed_operations = false
|
179
174
|
1.usd + 1.eur #=> MixedCurrencyError
|
180
175
|
|
181
176
|
=== Caching Options
|
182
177
|
|
183
178
|
Use Memcached to cache the result (default). Exchange will cache the API files with a key starting with 'exchange_'
|
184
|
-
Exchange::Configuration.
|
185
|
-
c.cache =
|
186
|
-
|
187
|
-
|
179
|
+
Exchange.configuration = Exchange::Configuration.new do |c|
|
180
|
+
c.cache = {
|
181
|
+
:subclass => :memcached,
|
182
|
+
:host => 'yourhost',
|
183
|
+
:port => 2434, #yourport
|
184
|
+
}
|
188
185
|
end
|
189
186
|
|
190
187
|
Use Redis to cache the result. Exchange will cache the API files with a key starting with 'exchange_'
|
191
|
-
Exchange::Configuration.
|
192
|
-
c.cache =
|
193
|
-
|
194
|
-
|
188
|
+
Exchange.configuration = Exchange::Configuration.new do |c|
|
189
|
+
c.cache = {
|
190
|
+
:subclass => :redis,
|
191
|
+
:host => 'yourhost',
|
192
|
+
:port => 2434, #yourport
|
193
|
+
}
|
195
194
|
end
|
196
195
|
|
197
196
|
Use Rails to cache the result. Exchange will cache the API files with a key starting with 'exchange_'
|
198
|
-
Exchange::Configuration.
|
199
|
-
c.cache =
|
197
|
+
Exchange.configuration = Exchange::Configuration.new do |c|
|
198
|
+
c.cache = {
|
199
|
+
:subclass => :rails
|
200
|
+
}
|
200
201
|
end
|
201
202
|
|
202
203
|
=== API Options
|
203
204
|
|
204
|
-
Use the
|
205
|
-
Exchange::Configuration.
|
206
|
-
c.api =
|
205
|
+
Use the Xaviermedia API as the source of your conversion rates
|
206
|
+
Exchange.configuration = Exchange::Configuration.new do |c|
|
207
|
+
c.api = {
|
208
|
+
:subclass => :xavier_media
|
209
|
+
}
|
207
210
|
end
|
208
211
|
|
209
|
-
Use the
|
210
|
-
Exchange::Configuration.
|
211
|
-
c.api =
|
212
|
+
Use the currencybot Open Source API as a source of your conversion rates
|
213
|
+
Exchange.configuration = Exchange::Configuration.new do |c|
|
214
|
+
c.api = {
|
215
|
+
:subclass => :currency_bot
|
216
|
+
}
|
212
217
|
end
|
213
218
|
|
214
219
|
== Connect your own API and Cache
|
@@ -222,21 +227,36 @@ Easily connect to your custom API by writing an ExternalAPI Class, or use your o
|
|
222
227
|
# Define here which currencies your API can handle
|
223
228
|
CURRENCIES = %W(usd chf)
|
224
229
|
|
225
|
-
# Every instance of ExternalAPI Class has to have an update function which gets
|
230
|
+
# Every instance of ExternalAPI Class has to have an update function which gets
|
231
|
+
# the rates from the API
|
232
|
+
#
|
226
233
|
def update(opts={})
|
227
234
|
# assure that you will get a Time object for the historical dates
|
235
|
+
#
|
228
236
|
time = assure_time(opts[:at])
|
229
237
|
|
230
|
-
#
|
238
|
+
# Call your API (shown here with a helper function that builds your API URL).
|
239
|
+
# Like this, your calls will get cached.
|
240
|
+
#
|
231
241
|
Call.new(api_url(time), :at => time) do |result|
|
232
242
|
|
233
|
-
#
|
243
|
+
# Assign the currency conversion base.
|
244
|
+
# Attention, this is readonly, self.base= won't work
|
245
|
+
#
|
234
246
|
@base = result['base']
|
235
247
|
|
236
|
-
# assign the rates, this has to be a hash with the following format:
|
248
|
+
# assign the rates, this has to be a hash with the following format:
|
249
|
+
# {'USD' => 1.23242, 'CHF' => 1.34323}.
|
250
|
+
#
|
251
|
+
# Attention, this is readonly, self.rates= won't work
|
252
|
+
#
|
237
253
|
@rates = result['rates']
|
238
254
|
|
239
|
-
#
|
255
|
+
# Timestamp the api call result. This may come in handy to assure you have
|
256
|
+
# the right result.
|
257
|
+
#
|
258
|
+
# Attention, this is readonly, self.timestamp= won't work
|
259
|
+
#
|
240
260
|
@timestamp = result['timestamp'].to_i
|
241
261
|
end
|
242
262
|
end
|
@@ -253,7 +273,7 @@ Easily connect to your custom API by writing an ExternalAPI Class, or use your o
|
|
253
273
|
|
254
274
|
Now, you can configure your API in the configuration. The Symbol will get camelcased and constantized
|
255
275
|
|
256
|
-
Exchange::Configuration.api = :my_custom
|
276
|
+
Exchange::Configuration.api.subclass = :my_custom
|
257
277
|
|
258
278
|
Have fun, and don't forget to write tests.
|
259
279
|
|
@@ -262,19 +282,22 @@ Have fun, and don't forget to write tests.
|
|
262
282
|
Write your own caching module to use the gem with your own custom caching solution.
|
263
283
|
module Cache
|
264
284
|
class MyCustomCache < Base
|
265
|
-
class
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
285
|
+
# A cache class has to have the method "cached".
|
286
|
+
# The cache Base is a singleton and forwards the method "cached"
|
287
|
+
# to the instance
|
288
|
+
#
|
289
|
+
def cached api, opts={}, &block
|
290
|
+
# generate the storage with key(api, opts[:at]) and you will get a
|
291
|
+
# unique key to store in your cache
|
292
|
+
#
|
293
|
+
# Your code goes here
|
271
294
|
end
|
272
295
|
end
|
273
296
|
end
|
274
297
|
|
275
298
|
Now, you can configure your Caching solution in the configuration. The Symbol will get camelcased and constantized
|
276
299
|
|
277
|
-
Exchange
|
300
|
+
Exchange.configuration.cache.subclass = :my_custom_cache
|
278
301
|
|
279
302
|
Have fun, and don't forget to write tests.
|
280
303
|
|
data/changelog.rdoc
CHANGED
@@ -1,6 +1,25 @@
|
|
1
1
|
= Changes to Exchange
|
2
2
|
|
3
|
-
== 0.3
|
3
|
+
== 0.7.3
|
4
|
+
- jruby compatibility, horray!
|
5
|
+
|
6
|
+
== 0.7.1
|
7
|
+
- The DSL is now included into Numeric, which makes currency operations available for all subclasses (including your own) of Numeric
|
8
|
+
- Some structurally unsound behaviour with ruby base operators (+, -, *, /, round, floor, ceil) has been corrected. It is recommended to update to this latest version of exchange if you are using a version less than 0.7.1.
|
9
|
+
- the api classes have been refactored to less complex, more readable code,
|
10
|
+
- general performance has improved quite a bit.
|
11
|
+
|
12
|
+
== 0.6
|
13
|
+
- Memcached, Redis, JSON and Nokogiri have been removed as explicit dependencies of the gem. The gem will now try to load the gems you want to use with it during runtime, and it will throw an exception if a gem required according to your configuration is not present.
|
14
|
+
- The cache class is now a singleton and no class singleton anymore. This makes it easier to write a seamlessly integrating custom cache class for it.
|
15
|
+
|
16
|
+
== 0.5
|
17
|
+
- Changed the currency bot api to openexchangerates.org and changed the default api to xaviermedia. Since the openexchangerates.org api still supports a free plan with reasonable conditions, it will still be supported by the exchange gem, although it may be moved to a separate gem in the future
|
18
|
+
|
19
|
+
== 0.4
|
20
|
+
- Some potential 0 values on conversions when caching with memcached have been fixed.
|
21
|
+
|
22
|
+
== 0.3
|
4
23
|
- All Currencies & Rates get handled in BigDecimal now, preventing floating point errors
|
5
24
|
- Major Performance inprovements on the DSL methods
|
6
25
|
- New ECB API configuration possibility
|