exchange 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +17 -8
  2. data/Gemfile.lock +12 -6
  3. data/LICENSE.txt +1 -1
  4. data/README.rdoc +15 -12
  5. data/VERSION +1 -1
  6. data/exchange.gemspec +13 -23
  7. data/lib/core_extensions/conversability.rb +5 -4
  8. data/lib/exchange.rb +6 -11
  9. data/lib/exchange/base.rb +19 -0
  10. data/lib/exchange/cache/base.rb +57 -32
  11. data/lib/exchange/cache/file.rb +41 -45
  12. data/lib/exchange/cache/memcached.rb +28 -29
  13. data/lib/exchange/cache/no_cache.rb +4 -24
  14. data/lib/exchange/cache/rails.rb +26 -26
  15. data/lib/exchange/cache/redis.rb +30 -33
  16. data/lib/exchange/configuration.rb +167 -73
  17. data/lib/exchange/currency.rb +61 -30
  18. data/lib/exchange/external_api.rb +2 -0
  19. data/lib/exchange/external_api/base.rb +11 -5
  20. data/lib/exchange/external_api/call.rb +10 -7
  21. data/lib/exchange/external_api/currency_bot.rb +9 -5
  22. data/lib/exchange/external_api/ecb.rb +29 -13
  23. data/lib/exchange/external_api/json.rb +22 -0
  24. data/lib/exchange/external_api/xavier_media.rb +6 -5
  25. data/lib/exchange/external_api/xml.rb +22 -0
  26. data/lib/exchange/gem_loader.rb +35 -0
  27. data/lib/exchange/helper.rb +22 -15
  28. data/lib/exchange/iso_4217.rb +55 -44
  29. data/spec/core_extensions/conversability_spec.rb +2 -2
  30. data/spec/exchange/cache/base_spec.rb +7 -7
  31. data/spec/exchange/cache/file_spec.rb +19 -18
  32. data/spec/exchange/cache/memcached_spec.rb +29 -26
  33. data/spec/exchange/cache/no_cache_spec.rb +12 -6
  34. data/spec/exchange/cache/rails_spec.rb +13 -9
  35. data/spec/exchange/cache/redis_spec.rb +24 -24
  36. data/spec/exchange/configuration_spec.rb +38 -30
  37. data/spec/exchange/currency_spec.rb +33 -21
  38. data/spec/exchange/external_api/base_spec.rb +3 -1
  39. data/spec/exchange/external_api/call_spec.rb +5 -1
  40. data/spec/exchange/external_api/currency_bot_spec.rb +5 -1
  41. data/spec/exchange/external_api/ecb_spec.rb +9 -5
  42. data/spec/exchange/external_api/xavier_media_spec.rb +5 -1
  43. data/spec/exchange/gem_loader_spec.rb +29 -0
  44. data/spec/spec_helper.rb +1 -1
  45. 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 "shoulda", ">= 0"
13
- gem "yard", "~> 0.7.4"
14
- gem "bundler", "> 1.0.0"
15
- gem "jeweler", "~> 1.8.3"
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
@@ -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
- memcached (1.4.1)
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 (2.2.2)
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 (> 1.0.0)
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 beatrichartz
1
+ Copyright (c) 2012 Beat Richartz
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -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, Padrino or whatever Framework you like.
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
- === Important Note
5
+ === Changelog
6
6
 
7
- Since The Currency Bot API, now renamed to Open Exchange Rates API at openexchangerates.org (http://openexchangerates.org), requires an APP ID now, you should configure it using the Exchange::Configuration
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
- Exchange::Configuration.define do |c|
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
- Imagine the exchange rates getting cached, and you hitting the internet only daily to get new rates (hourly if you're eager to have the absolutely newest ones)
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.5.1
1
+ 0.6.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "exchange"
8
- s.version = "0.5.1"
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-02"
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<nokogiri>, [">= 1.5.0"])
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>, ["> 1.0.0"])
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<nokogiri>, [">= 1.5.0"])
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>, ["> 1.0.0"])
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<nokogiri>, [">= 1.5.0"])
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>, ["> 1.0.0"])
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 handles certain type conversion of Fixnum, Float and others
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)
@@ -1,19 +1,14 @@
1
- EXCHANGE_GEM_ROOT_PATH = File.dirname(__FILE__).sub(/\/lib$/, '') if __FILE__.is_a?(String)
2
-
1
+ require 'rubygems'
3
2
  require 'bigdecimal'
4
3
  require 'open-uri'
5
- require 'bundler'
4
+ require 'ostruct'
6
5
  require 'json'
7
- require 'nokogiri'
8
- require 'redis'
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 'core_extensions/conversability'
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
@@ -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 << self
8
- # # a cache class has to have the class method "cached"
9
- # def cached api, opts={}, &block
10
- # # generate the key with key(api, opts[:at]) and you will get a unique key to store in your cache
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
- # Exchange::Configuration.cache = :my_custom_cache
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.1
25
+ # @version 0.6
25
26
  # @since 0.1
26
-
27
+ #
27
28
  class Base
28
- class << self
29
-
30
- protected
31
-
32
- # A Cache Key generator for the API Classes and the time
33
- # Generates a key which can handle expiration by itself
34
- # @param [Exchange::ExternalAPI::Subclass] api_class The API to store the data for
35
- # @param [optional, Time] time The time for which the data is valid
36
- # @return [String] A string that can be used as cache key
37
- # @example
38
- # Exchange::Cache::Base.key(Exchange::ExternalAPI::CurrencyBot, Time.gm(2012,1,1)) #=> "Exchange_ExternalAPI_CurrencyBot_2012_1"
39
-
40
- def key(api, opts={})
41
- time = Exchange::Helper.assure_time(opts[:at], :default => :now)
42
- [ 'exchange',
43
- api.to_s,
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
@@ -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.3
8
+ # @version 0.6
9
9
  # @since 0.3
10
10
 
11
11
  class File < Base
12
- class << self
13
12
 
14
- # returns either cached data from a stored file or stores a file.
15
- # This method has to be the same in all the cache classes in order for the configuration binding to work
16
- # @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
17
- # @param [Hash] opts the options to cache with
18
- # @option opts [Time] :at IS IGNORED FOR FILECACHE
19
- # @option opts [Symbol] :cache_period The period to cache the file for
20
- # @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
21
- # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
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
- def cached api, opts={}, &block
24
- raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
25
-
26
- today = Time.now
27
- dir = Exchange::Configuration.filestore_path
28
- path = ::File.join(dir, key(api, opts[:cache_period]))
29
-
30
- if ::File.exists?(path)
31
- result = ::File.read(path)
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
- private
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