exchange 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +17 -8
- data/Gemfile.lock +12 -6
- data/LICENSE.txt +1 -1
- data/README.rdoc +15 -12
- data/VERSION +1 -1
- data/exchange.gemspec +13 -23
- data/lib/core_extensions/conversability.rb +5 -4
- data/lib/exchange.rb +6 -11
- data/lib/exchange/base.rb +19 -0
- data/lib/exchange/cache/base.rb +57 -32
- data/lib/exchange/cache/file.rb +41 -45
- data/lib/exchange/cache/memcached.rb +28 -29
- data/lib/exchange/cache/no_cache.rb +4 -24
- data/lib/exchange/cache/rails.rb +26 -26
- data/lib/exchange/cache/redis.rb +30 -33
- data/lib/exchange/configuration.rb +167 -73
- data/lib/exchange/currency.rb +61 -30
- data/lib/exchange/external_api.rb +2 -0
- data/lib/exchange/external_api/base.rb +11 -5
- data/lib/exchange/external_api/call.rb +10 -7
- data/lib/exchange/external_api/currency_bot.rb +9 -5
- data/lib/exchange/external_api/ecb.rb +29 -13
- data/lib/exchange/external_api/json.rb +22 -0
- data/lib/exchange/external_api/xavier_media.rb +6 -5
- data/lib/exchange/external_api/xml.rb +22 -0
- data/lib/exchange/gem_loader.rb +35 -0
- data/lib/exchange/helper.rb +22 -15
- data/lib/exchange/iso_4217.rb +55 -44
- data/spec/core_extensions/conversability_spec.rb +2 -2
- data/spec/exchange/cache/base_spec.rb +7 -7
- data/spec/exchange/cache/file_spec.rb +19 -18
- data/spec/exchange/cache/memcached_spec.rb +29 -26
- data/spec/exchange/cache/no_cache_spec.rb +12 -6
- data/spec/exchange/cache/rails_spec.rb +13 -9
- data/spec/exchange/cache/redis_spec.rb +24 -24
- data/spec/exchange/configuration_spec.rb +38 -30
- data/spec/exchange/currency_spec.rb +33 -21
- data/spec/exchange/external_api/base_spec.rb +3 -1
- data/spec/exchange/external_api/call_spec.rb +5 -1
- data/spec/exchange/external_api/currency_bot_spec.rb +5 -1
- data/spec/exchange/external_api/ecb_spec.rb +9 -5
- data/spec/exchange/external_api/xavier_media_spec.rb +5 -1
- data/spec/exchange/gem_loader_spec.rb +29 -0
- data/spec/spec_helper.rb +1 -1
- metadata +12 -87
data/Gemfile
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
# Add dependencies required to use your gem here.
|
3
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"
|
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
8
|
|
9
9
|
# Add dependencies to develop your gem here.
|
10
10
|
# Include everything needed to run rake, tests, features, etc.
|
11
|
+
|
12
|
+
gemspec
|
13
|
+
|
11
14
|
group :development do
|
12
|
-
gem "
|
13
|
-
gem "
|
14
|
-
gem "
|
15
|
-
|
15
|
+
gem "yard", "~> 0.7.4"
|
16
|
+
gem "bundler", ">= 1.0.0"
|
17
|
+
gem "jeweler", "~> 1.8.3"
|
18
|
+
end
|
19
|
+
|
20
|
+
group :test do
|
21
|
+
gem "nokogiri", ">= 1.5.0", :require => false
|
22
|
+
gem "dalli", ">= 2.0.0", :require => false
|
23
|
+
gem "redis", ">= 2.2.0", :require => false
|
24
|
+
gem "shoulda", ">= 0"
|
16
25
|
gem "rspec"
|
17
26
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
exchange (0.6.0)
|
5
|
+
json (>= 1.0.0)
|
6
|
+
|
1
7
|
GEM
|
2
8
|
remote: http://rubygems.org/
|
3
9
|
specs:
|
10
|
+
dalli (2.2.1)
|
4
11
|
diff-lcs (1.1.3)
|
5
12
|
git (1.2.5)
|
6
13
|
jeweler (1.8.3)
|
@@ -9,12 +16,11 @@ GEM
|
|
9
16
|
rake
|
10
17
|
rdoc
|
11
18
|
json (1.6.6)
|
12
|
-
|
13
|
-
nokogiri (1.5.0)
|
19
|
+
nokogiri (1.5.5)
|
14
20
|
rake (0.9.2.2)
|
15
21
|
rdoc (3.12)
|
16
22
|
json (~> 1.4)
|
17
|
-
redis (
|
23
|
+
redis (3.0.1)
|
18
24
|
rspec (2.9.0)
|
19
25
|
rspec-core (~> 2.9.0)
|
20
26
|
rspec-expectations (~> 2.9.0)
|
@@ -34,10 +40,10 @@ PLATFORMS
|
|
34
40
|
ruby
|
35
41
|
|
36
42
|
DEPENDENCIES
|
37
|
-
bundler (
|
43
|
+
bundler (>= 1.0.0)
|
44
|
+
dalli (>= 2.0.0)
|
45
|
+
exchange!
|
38
46
|
jeweler (~> 1.8.3)
|
39
|
-
json (>= 1.6.5)
|
40
|
-
memcached (>= 1.3.0)
|
41
47
|
nokogiri (>= 1.5.0)
|
42
48
|
redis (>= 2.2.0)
|
43
49
|
rspec
|
data/LICENSE.txt
CHANGED
data/README.rdoc
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
= exchange {<img src="https://secure.travis-ci.org/beatrichartz/exchange.png" />}[http://travis-ci.org/beatrichartz/exchange]
|
2
2
|
|
3
|
-
The Exchange Gem gives you easy access to currency functions directly on your Numbers. It has been tested against ruby 1.8.7, ree, ruby 1.9.2 and 1.9.3. You can use it with just plain ruby projects, in Rails 2 and 3, Sinatra
|
3
|
+
The Exchange Gem gives you easy access to currency functions directly on your Numbers. It has been tested against ruby 1.8.7, ree, ruby 1.9.2 and 1.9.3. You can use it with just plain ruby projects, in Rails 2 and 3, Sinatra or whatever Framework you like.
|
4
4
|
|
5
|
-
===
|
5
|
+
=== Changelog
|
6
6
|
|
7
|
-
|
7
|
+
== 0.6
|
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
|
8
17
|
|
9
|
-
|
10
|
-
c.api_app_id = 'YOUR_APP_ID'
|
11
|
-
end
|
12
|
-
|
13
|
-
Also, since it is not my intention to support paid APIs with this gem, the default API has been moved to use the Xavier Media API at xaviermedia.org. However, since Open Exchange Rates (Currency Bot) still supports a free plan, I updated the gem to be able to use it.
|
14
|
-
|
15
|
-
As a sidenote: Please be reasonable when using the APIs provided, and be sure to enable caching and mock out any http calls in your specs.
|
18
|
+
==== Features
|
16
19
|
|
17
20
|
=== Easy Conversion
|
18
21
|
|
@@ -24,7 +27,7 @@ which gets you an exchange at the rates of yesterday.
|
|
24
27
|
|
25
28
|
=== Only one request per day to keep you up to date
|
26
29
|
|
27
|
-
|
30
|
+
You're hitting the internet only daily to get new rates with this gem (hourly updates are available if you're eager to have the absolutely newest ones)
|
28
31
|
|
29
32
|
=== ISO 4217 Currency formatting
|
30
33
|
On of the issues with currencies is: You never know the format they should be in. With Exchange, you can just use the currencies
|
@@ -40,9 +43,9 @@ a string with the currency code in front, or just the amount in the right format
|
|
40
43
|
|
41
44
|
Three open APIs are already included:
|
42
45
|
|
43
|
-
- Currency Bot (http://currencybot.github.com/)
|
44
46
|
- Xaviermedia (http://www.xavierforum.com/viewtopic.php?f=5&t=10979&sid=671a685edbfa5dbec219fbc6793d5057)
|
45
47
|
- European Central Bank (http://www.ecb.int/stats/exchange/eurofxref/html/index.en.html)
|
48
|
+
- Currency Bot (http://currencybot.github.com/)
|
46
49
|
|
47
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 with the Exchange gem. Just visit the documentation here: http://rubydoc.info/github/beatrichartz/exchange/Exchange/ExternalAPI to see an example of a custom API Extension
|
48
51
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/exchange.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "exchange"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Beat Richartz"]
|
12
|
-
s.date = "2012-10-
|
12
|
+
s.date = "2012-10-04"
|
13
13
|
s.description = "The Exchange Gem gives you easy access to currency functions directly on your Numbers. Imagine a conversion as easy as \n 1.eur.to_usd\n or even better \n 1.eur.to_usd(:at => Time.now - 84600)\n which gets you an exchange at the rates of yesterday."
|
14
14
|
s.email = "exchange_gem@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"iso4217.yml",
|
32
32
|
"lib/core_extensions/conversability.rb",
|
33
33
|
"lib/exchange.rb",
|
34
|
+
"lib/exchange/base.rb",
|
34
35
|
"lib/exchange/cache.rb",
|
35
36
|
"lib/exchange/cache/base.rb",
|
36
37
|
"lib/exchange/cache/file.rb",
|
@@ -45,7 +46,10 @@ Gem::Specification.new do |s|
|
|
45
46
|
"lib/exchange/external_api/call.rb",
|
46
47
|
"lib/exchange/external_api/currency_bot.rb",
|
47
48
|
"lib/exchange/external_api/ecb.rb",
|
49
|
+
"lib/exchange/external_api/json.rb",
|
48
50
|
"lib/exchange/external_api/xavier_media.rb",
|
51
|
+
"lib/exchange/external_api/xml.rb",
|
52
|
+
"lib/exchange/gem_loader.rb",
|
49
53
|
"lib/exchange/helper.rb",
|
50
54
|
"lib/exchange/iso_4217.rb",
|
51
55
|
"spec/core_extensions/conversability_spec.rb",
|
@@ -62,6 +66,7 @@ Gem::Specification.new do |s|
|
|
62
66
|
"spec/exchange/external_api/currency_bot_spec.rb",
|
63
67
|
"spec/exchange/external_api/ecb_spec.rb",
|
64
68
|
"spec/exchange/external_api/xavier_media_spec.rb",
|
69
|
+
"spec/exchange/gem_loader_spec.rb",
|
65
70
|
"spec/exchange/helper_spec.rb",
|
66
71
|
"spec/exchange/iso_4217_spec.rb",
|
67
72
|
"spec/spec_helper.rb",
|
@@ -82,36 +87,21 @@ Gem::Specification.new do |s|
|
|
82
87
|
s.specification_version = 3
|
83
88
|
|
84
89
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
85
|
-
s.add_runtime_dependency(%q<
|
86
|
-
s.add_runtime_dependency(%q<json>, [">= 1.6.5"])
|
87
|
-
s.add_runtime_dependency(%q<memcached>, [">= 1.3.0"])
|
88
|
-
s.add_runtime_dependency(%q<redis>, [">= 2.2.0"])
|
89
|
-
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
90
|
+
s.add_runtime_dependency(%q<exchange>, [">= 0"])
|
90
91
|
s.add_development_dependency(%q<yard>, ["~> 0.7.4"])
|
91
|
-
s.add_development_dependency(%q<bundler>, ["
|
92
|
+
s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
|
92
93
|
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
93
|
-
s.add_development_dependency(%q<rspec>, [">= 0"])
|
94
94
|
else
|
95
|
-
s.add_dependency(%q<
|
96
|
-
s.add_dependency(%q<json>, [">= 1.6.5"])
|
97
|
-
s.add_dependency(%q<memcached>, [">= 1.3.0"])
|
98
|
-
s.add_dependency(%q<redis>, [">= 2.2.0"])
|
99
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
95
|
+
s.add_dependency(%q<exchange>, [">= 0"])
|
100
96
|
s.add_dependency(%q<yard>, ["~> 0.7.4"])
|
101
|
-
s.add_dependency(%q<bundler>, ["
|
97
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
102
98
|
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
103
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
104
99
|
end
|
105
100
|
else
|
106
|
-
s.add_dependency(%q<
|
107
|
-
s.add_dependency(%q<json>, [">= 1.6.5"])
|
108
|
-
s.add_dependency(%q<memcached>, [">= 1.3.0"])
|
109
|
-
s.add_dependency(%q<redis>, [">= 2.2.0"])
|
110
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
101
|
+
s.add_dependency(%q<exchange>, [">= 0"])
|
111
102
|
s.add_dependency(%q<yard>, ["~> 0.7.4"])
|
112
|
-
s.add_dependency(%q<bundler>, ["
|
103
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
113
104
|
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
114
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
115
105
|
end
|
116
106
|
end
|
117
107
|
|
@@ -4,11 +4,12 @@ module Exchange
|
|
4
4
|
# @author Beat Richartz
|
5
5
|
# @version 0.2
|
6
6
|
# @since 0.1
|
7
|
-
|
7
|
+
#
|
8
8
|
module Conversability
|
9
|
+
|
9
10
|
# Dynamic method generation is used here to allow instantiation and immediate conversion of Currency objects from
|
10
|
-
# a common Fixnum or Float or BigDecimal. Since ruby 1.9
|
11
|
-
# via method missing, this is not handled via method missing because it would seriously break down performance.
|
11
|
+
# a common Fixnum or Float or BigDecimal. Since some builds of ruby 1.9 handle certain type conversion of Fixnum, Float
|
12
|
+
# and others via method missing, this is not handled via method missing because it would seriously break down performance.
|
12
13
|
#
|
13
14
|
# @example Instantiate from any type of number
|
14
15
|
# 40.usd => #<Exchange::Currency @value=40 @currency=:usd>
|
@@ -22,7 +23,7 @@ module Exchange
|
|
22
23
|
# 1.usd.to_eur(:at => Time.now - 86400) => #<Exchange::Currency @value=0.80 @currency=:eur>
|
23
24
|
# 1.nok.to_chf(:at => Time.now - 3600) => #<Exchange::Currency @value=6.57 @currency=:chf>
|
24
25
|
# -3.5.dkk.to_huf(:at => Time.now - 172800) => #<Exchange::Currency @value=-337.40 @currency=:huf>
|
25
|
-
|
26
|
+
#
|
26
27
|
ISO4217.definitions.keys.each do |c|
|
27
28
|
define_method c.downcase.to_sym do |*args|
|
28
29
|
Currency.new(self, c, *args)
|
data/lib/exchange.rb
CHANGED
@@ -1,19 +1,14 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'rubygems'
|
3
2
|
require 'bigdecimal'
|
4
3
|
require 'open-uri'
|
5
|
-
require '
|
4
|
+
require 'ostruct'
|
6
5
|
require 'json'
|
7
|
-
require '
|
8
|
-
require '
|
9
|
-
require 'memcached'
|
6
|
+
require 'exchange/base'
|
7
|
+
require 'exchange/gem_loader'
|
10
8
|
require 'exchange/helper'
|
11
|
-
require 'exchange/configuration'
|
12
9
|
require 'exchange/iso_4217'
|
13
10
|
require 'exchange/currency'
|
14
11
|
require 'exchange/external_api'
|
15
12
|
require 'exchange/cache'
|
16
|
-
require '
|
17
|
-
|
18
|
-
# The error that gets thrown if no conversion rate is available
|
19
|
-
NoRateError = Class.new(StandardError)
|
13
|
+
require 'exchange/configuration'
|
14
|
+
require 'core_extensions/conversability'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Exchange
|
2
|
+
|
3
|
+
# The current version of the exchange gem
|
4
|
+
#
|
5
|
+
VERSION = '0.6.0'
|
6
|
+
|
7
|
+
# The root installation path of the gem
|
8
|
+
# @version 0.5
|
9
|
+
# @since 0.1
|
10
|
+
#
|
11
|
+
ROOT_PATH = File.dirname(__FILE__).to_s.sub(/\/lib\/exchange\/?$/, '')
|
12
|
+
|
13
|
+
# The error that gets thrown if no conversion rate is available
|
14
|
+
# @version 0.1
|
15
|
+
# @since 0.1
|
16
|
+
#
|
17
|
+
NoRateError = Class.new StandardError
|
18
|
+
|
19
|
+
end
|
data/lib/exchange/cache/base.rb
CHANGED
@@ -1,55 +1,80 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'forwardable'
|
3
|
+
|
1
4
|
module Exchange
|
2
5
|
# The cache module. All Classes handling caching for this gem have to be placed here. Allows easy extension with own caching solutions
|
3
6
|
# as shown in the example below.
|
4
7
|
# @example Write your own caching module
|
5
8
|
# module Cache
|
6
9
|
# class MyCustomCache < Base
|
7
|
-
# class
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# # Your code goes here
|
12
|
-
# end
|
10
|
+
# # a cache class has to have the class method "cached"
|
11
|
+
# def cached api, opts={}, &block
|
12
|
+
# # generate the key with key(api, opts[:at]) and you will get a unique key to store in your cache
|
13
|
+
# # Your code goes here
|
13
14
|
# end
|
14
15
|
# end
|
15
16
|
# end
|
16
17
|
# # Now, you can configure your Caching solution in the configuration. The Symbol will get camelcased and constantized
|
17
|
-
#
|
18
|
+
# configuration.cache.subclass = :my_custom_cache
|
18
19
|
# # Have fun, and don't forget to write tests.
|
19
20
|
|
20
21
|
module Cache
|
21
22
|
|
22
23
|
# The base Class for all Caching operations. Essentially generates a helper function for all cache classes to generate a key
|
23
24
|
# @author Beat Richartz
|
24
|
-
# @version 0.
|
25
|
+
# @version 0.6
|
25
26
|
# @since 0.1
|
26
|
-
|
27
|
+
#
|
27
28
|
class Base
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
time.year.to_s,
|
45
|
-
time.yday.to_s,
|
46
|
-
Exchange::Configuration.update == :hourly ? time.hour.to_s : nil,
|
47
|
-
*(opts[:key_for] || [])
|
48
|
-
].compact.join('_')
|
49
|
-
end
|
50
|
-
|
29
|
+
include Singleton
|
30
|
+
extend SingleForwardable
|
31
|
+
|
32
|
+
# returns The result of the block called
|
33
|
+
# This method has to be the same in all the cache classes in order for the configuration binding to work
|
34
|
+
# @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
|
35
|
+
# @param [Hash] opts the options to cache with
|
36
|
+
# @option opts [Time] :at is ignored for filecache, other than that is the time of the cached rate
|
37
|
+
# @option opts [Symbol] :cache_period The period to cache for
|
38
|
+
# @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
|
39
|
+
# @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
|
40
|
+
#
|
41
|
+
def cached api, opts={}, &block
|
42
|
+
raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
|
43
|
+
|
44
|
+
block.call
|
51
45
|
end
|
46
|
+
|
47
|
+
# Forwards the cached method to the instance using singleforwardable
|
48
|
+
#
|
49
|
+
def_delegator :instance, :cached
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
# A Cache Key generator for the API Classes and the time
|
54
|
+
# Generates a key which can handle expiration by itself
|
55
|
+
# @param [Exchange::ExternalAPI::Subclass] api_class The API to store the data for
|
56
|
+
# @param [optional, Time] time The time for which the data is valid
|
57
|
+
# @return [String] A string that can be used as cache key
|
58
|
+
# @example
|
59
|
+
# Exchange::Cache::Base.key(Exchange::ExternalAPI::CurrencyBot, Time.gm(2012,1,1)) #=> "Exchange_ExternalAPI_CurrencyBot_2012_1"
|
60
|
+
#
|
61
|
+
def key api, opts={}
|
62
|
+
time = Exchange::Helper.assure_time(opts[:at], :default => :now)
|
63
|
+
[ 'exchange',
|
64
|
+
api.to_s,
|
65
|
+
time.year.to_s,
|
66
|
+
time.yday.to_s,
|
67
|
+
Exchange.configuration.cache.expire == :hourly ? time.hour.to_s : nil,
|
68
|
+
*(opts[:key_for] || [])
|
69
|
+
].compact.join('_')
|
70
|
+
end
|
71
|
+
|
52
72
|
end
|
73
|
+
|
74
|
+
# The error getting thrown if caching is attempted without a given block
|
75
|
+
# @since 0.1
|
76
|
+
# @version 0.1
|
77
|
+
#
|
53
78
|
CachingWithoutBlockError = Class.new(ArgumentError)
|
54
79
|
end
|
55
80
|
end
|
data/lib/exchange/cache/file.rb
CHANGED
@@ -5,61 +5,57 @@ module Exchange
|
|
5
5
|
# A class that allows to store api call results in files. THIS NOT A RECOMMENDED CACHING OPTION!
|
6
6
|
# It just may be necessary to cache large files somewhere, this class allows you to do that
|
7
7
|
#
|
8
|
-
# @version 0.
|
8
|
+
# @version 0.6
|
9
9
|
# @since 0.3
|
10
10
|
|
11
11
|
class File < Base
|
12
|
-
class << self
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
# returns either cached data from a stored file or stores a file.
|
14
|
+
# This method has to be the same in all the cache classes in order for the configuration binding to work
|
15
|
+
# @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
|
16
|
+
# @param [Hash] opts the options to cache with
|
17
|
+
# @option opts [Time] :at IS IGNORED FOR FILECACHE
|
18
|
+
# @option opts [Symbol] :cache_period The period to cache the file for
|
19
|
+
# @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
|
20
|
+
# @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
|
21
|
+
#
|
22
|
+
def cached api, opts={}, &block
|
23
|
+
today = Time.now
|
24
|
+
dir = Exchange.configuration.cache.path
|
25
|
+
path = ::File.join(dir, key(api, opts[:cache_period]))
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
else
|
33
|
-
result = block.call
|
34
|
-
if result && !result.to_s.empty?
|
35
|
-
FileUtils.mkdir_p(dir) unless Dir.respond_to?(:exists?) && Dir.exists?(dir)
|
36
|
-
keep_files = [key(api, :daily), key(api, :monthly)]
|
37
|
-
Dir.entries(dir).each do |e|
|
38
|
-
::File.delete(::File.join(dir, e)) unless keep_files.include?(e) || e.match(/\A\./)
|
39
|
-
end
|
40
|
-
::File.open(path, 'w') {|f| f.write(result) }
|
27
|
+
if ::File.exists?(path)
|
28
|
+
result = ::File.read(path)
|
29
|
+
else
|
30
|
+
result = super
|
31
|
+
if result && !result.to_s.empty?
|
32
|
+
FileUtils.mkdir_p(dir) unless Dir.respond_to?(:exists?) && Dir.exists?(dir)
|
33
|
+
keep_files = [key(api, :daily), key(api, :monthly)]
|
34
|
+
Dir.entries(dir).each do |e|
|
35
|
+
::File.delete(::File.join(dir, e)) unless keep_files.include?(e) || e.match(/\A\./)
|
41
36
|
end
|
37
|
+
::File.open(path, 'w') {|f| f.write(result) }
|
42
38
|
end
|
43
|
-
|
44
|
-
result
|
45
39
|
end
|
46
40
|
|
47
|
-
|
48
|
-
|
49
|
-
# A Cache Key generator for the file Cache Class and the time
|
50
|
-
# Generates a key which can handle expiration by itself
|
51
|
-
# @param [Exchange::ExternalAPI::Subclass] api_class The API to store the data for
|
52
|
-
# @param [optional, Symbol] cache_period The time for which the data is valid
|
53
|
-
# @return [String] A string that can be used as cache key
|
54
|
-
# @example
|
55
|
-
# Exchange::Cache::Base.key(Exchange::ExternalAPI::CurrencyBot, :monthly) #=> "Exchange_ExternalAPI_CurrencyBot_monthly_2012_1"
|
56
|
-
|
57
|
-
def key(api_class, cache_period=:daily)
|
58
|
-
time = Time.now
|
59
|
-
[api_class.to_s.gsub(/::/, '_'), cache_period, time.year, time.send(cache_period == :monthly ? :month : :yday)].join('_')
|
60
|
-
end
|
61
|
-
|
41
|
+
result
|
62
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# A Cache Key generator for the file Cache Class and the time
|
47
|
+
# Generates a key which can handle expiration by itself
|
48
|
+
# @param [Exchange::ExternalAPI::Subclass] api_class The API to store the data for
|
49
|
+
# @param [optional, Symbol] cache_period The time for which the data is valid
|
50
|
+
# @return [String] A string that can be used as cache key
|
51
|
+
# @example
|
52
|
+
# Exchange::Cache::Base.key(Exchange::ExternalAPI::CurrencyBot, :monthly) #=> "Exchange_ExternalAPI_CurrencyBot_monthly_2012_1"
|
53
|
+
#
|
54
|
+
def key(api_class, cache_period=:daily)
|
55
|
+
time = Time.now
|
56
|
+
[api_class.to_s.gsub(/::/, '_'), cache_period, time.year, time.send(cache_period == :monthly ? :month : :yday)].join('_')
|
57
|
+
end
|
58
|
+
|
63
59
|
end
|
64
60
|
end
|
65
61
|
end
|