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
@@ -11,42 +11,41 @@ module Exchange
11
11
  # c.cache_host = 'Your memcached host'
12
12
  # c.cache_port = 'Your memcached port'
13
13
  # end
14
+ #
14
15
  class Memcached < Base
15
- class << self
16
16
 
17
- # instantiates a memcached client and memoizes it in a class variable.
18
- # Use this client to access memcached data. For further explanation of use visit the memcached gem documentation
19
- # @example
20
- # Exchange::Cache::Memcached.client.set('FOO', 'BAR')
21
- # @return [::Memcached] an instance of the memcached client gem class
22
-
23
- def client
24
- @@client ||= ::Memcached.new("#{Configuration.cache_host}:#{Configuration.cache_port}")
25
- end
17
+ # instantiates a memcached client and memoizes it in a class variable.
18
+ # Use this client to access memcached data. For further explanation of use visit the memcached gem documentation
19
+ # @example
20
+ # Exchange::Cache::Memcached.client.set('FOO', 'BAR')
21
+ # @return [Dalli::Client] an instance of the dalli gem client class
22
+ #
23
+ def client
24
+ Exchange::GemLoader.new('dalli').try_load unless defined?(::Dalli)
25
+ @client ||= Dalli::Client.new("#{Exchange.configuration.cache.host}:#{Exchange.configuration.cache.port}")
26
+ end
27
+
28
+ # returns either cached data from the memcached client or calls the block and caches it in memcached.
29
+ # This method has to be the same in all the cache classes in order for the configuration binding to work
30
+ # @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
31
+ # @param [Hash] opts the options to cache with
32
+ # @option opts [Time] :at the historic time of the exchange rates to be cached
33
+ # @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
34
+ # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
35
+
36
+ def cached api, opts={}, &block
37
+ result = opts[:plain] ? client.get(key(api, opts)).to_s.gsub(/["\s+]/, '') : JSON.load(client.get(key(api, opts)))
26
38
 
27
- # returns either cached data from the memcached client or calls the block and caches it in memcached.
28
- # This method has to be the same in all the cache classes in order for the configuration binding to work
29
- # @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
30
- # @param [Hash] opts the options to cache with
31
- # @option opts [Time] :at the historic time of the exchange rates to be cached
32
- # @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
33
- # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
34
-
35
- def cached api, opts={}, &block
36
- raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
37
- begin
38
- result = opts[:plain] ? client.get(key(api, opts)).gsub(/["\s+]/, '') : JSON.load(client.get(key(api, opts)))
39
- rescue ::Memcached::NotFound
40
- result = block.call
41
- if result && !result.to_s.empty?
42
- client.set key(api, opts), result.to_json, Configuration.update == :daily ? 86400 : 3600
43
- end
39
+ unless result
40
+ result = super
41
+ if result && !result.to_s.empty?
42
+ client.set key(api, opts), result.to_json, Exchange.configuration.cache.expire == :daily ? 86400 : 3600
44
43
  end
45
-
46
- result
47
44
  end
48
45
 
46
+ result
49
47
  end
48
+
50
49
  end
51
50
  end
52
51
  end
@@ -2,32 +2,12 @@ module Exchange
2
2
  module Cache
3
3
 
4
4
  # @author Beat Richartz
5
- # A class that allows to store api call results in files. THIS NOT A RECOMMENDED CACHING OPTION!
6
- # It just may be necessary to cache large files somewhere, this class allows you to do that
5
+ # A class to set caching to false
7
6
  #
8
- # @version 0.3
7
+ # @version 0.6
9
8
  # @since 0.3
9
+ #
10
+ NoCache = Class.new Base
10
11
 
11
- class NoCache < Base
12
- class << self
13
-
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
22
-
23
- def cached api, opts={}, &block
24
- raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
25
-
26
- block.call
27
- end
28
-
29
-
30
- end
31
- end
32
12
  end
33
13
  end
@@ -9,37 +9,37 @@ module Exchange
9
9
  # Exchange::Configuration.define do |c|
10
10
  # c.cache = :rails
11
11
  # end
12
+ #
12
13
  class Rails < Base
13
- class << self
14
14
 
15
- # returns a Rails cache client. This has not to be stored since rails already memoizes it.
16
- # Use this client to access rails cache data. For further explanation of use visit the rails documentation
17
- # @example
18
- # Exchange::Cache::Rails.client.set('FOO', 'BAR')
19
- # @return [ActiveSupport::Cache::Subclass] an instance of the rails cache class (presumably a subclass of ActiveSupport::Cache)
20
-
21
- def client
22
- ::Rails.cache if defined?(::Rails)
23
- end
24
-
25
- # returns either cached data from the memcached client or calls the block and caches it in rails cache.
26
- # This method has to be the same in all the cache classes in order for the configuration binding to work
27
- # @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
28
- # @param [Hash] opts the options to cache with
29
- # @option opts [Time] :at the historic time of the exchange rates to be cached
30
- # @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
31
- # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
15
+ # returns a Rails cache client. This has not to be stored since rails already memoizes it.
16
+ # Use this client to access rails cache data. For further explanation of use visit the rails documentation
17
+ # @example
18
+ # Exchange::Cache::Rails.client.set('FOO', 'BAR')
19
+ # @return [ActiveSupport::Cache::Subclass] an instance of the rails cache class (presumably a subclass of ActiveSupport::Cache)
20
+ #
21
+ def client
22
+ Exchange::GemLoader.new('rails').try_load unless defined?(::Rails)
23
+ ::Rails.cache
24
+ end
25
+
26
+ # returns either cached data from the memcached client or calls the block and caches it in rails cache.
27
+ # This method has to be the same in all the cache classes in order for the configuration binding to work
28
+ # @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
29
+ # @param [Hash] opts the options to cache with
30
+ # @option opts [Time] :at the historic time of the exchange rates to be cached
31
+ # @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
32
+ # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
33
+ #
34
+ def cached api, opts={}, &block
35
+ raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
32
36
 
33
- def cached api, opts={}, &block
34
- raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
35
-
36
- result = client.fetch key(api, opts), :expires_in => Configuration.update == :daily ? 86400 : 3600, &block
37
- client.delete(key(api, opts)) unless result && !result.to_s.empty?
38
-
39
- result
40
- end
37
+ result = client.fetch key(api, opts), :expires_in => Exchange.configuration.cache.expire == :daily ? 86400 : 3600, &block
38
+ client.delete(key(api, opts)) unless result && !result.to_s.empty?
41
39
 
40
+ result
42
41
  end
42
+
43
43
  end
44
44
  end
45
45
  end
@@ -12,43 +12,40 @@ module Exchange
12
12
  # c.cache_port = 'Your redis port (an Integer)'
13
13
  # end
14
14
  class Redis < Base
15
- class << self
16
-
17
- # instantiates a redis client and memoizes it in a class variable.
18
- # Use this client to access redis data. For further explanation of use visit the redis gem documentation
19
- # @example
20
- # Exchange::Cache::Redis.client.set('FOO', 'BAR')
21
- # @return [::Redis] an instance of the redis client gem class
22
-
23
- def client
24
- @@client ||= ::Redis.new(:host => Configuration.cache_host, :port => Configuration.cache_port)
25
- end
26
-
27
- # returns either cached data from the redis client or calls the block and caches it in redis.
28
- # This method has to be the same in all the cache classes in order for the configuration binding to work
29
- # @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
30
- # @param [Hash] opts the options to cache with
31
- # @option opts [Time] :at the historic time of the exchange rates to be cached
32
- # @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
33
- # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
34
-
35
- def cached api, opts={}, &block
36
- raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
37
-
38
- if result = client.get(key(api, opts))
39
- result = opts[:plain] ? result.gsub(/["\s+]/, '') : JSON.load(result)
40
- else
41
- result = block.call
42
- if result && !result.to_s.empty?
43
- client.set key(api, opts), result.to_json
44
- client.expire key(api, opts), Configuration.update == :daily ? 86400 : 3600
45
- end
15
+
16
+ # instantiates a redis client and memoizes it in a class variable.
17
+ # Use this client to access redis data. For further explanation of use visit the redis gem documentation
18
+ # @example
19
+ # Exchange::Cache::Redis.client.set('FOO', 'BAR')
20
+ # @return [::Redis] an instance of the redis client gem class
21
+ #
22
+ def client
23
+ Exchange::GemLoader.new('redis').try_load unless defined?(::Redis)
24
+ @client ||= ::Redis.new(:host => Exchange.configuration.cache.host, :port => Exchange.configuration.cache.port)
25
+ end
26
+
27
+ # returns either cached data from the redis client or calls the block and caches it in redis.
28
+ # This method has to be the same in all the cache classes in order for the configuration binding to work
29
+ # @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
30
+ # @param [Hash] opts the options to cache with
31
+ # @option opts [Time] :at the historic time of the exchange rates to be cached
32
+ # @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
33
+ # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
34
+ #
35
+ def cached api, opts={}, &block
36
+ if result = client.get(key(api, opts))
37
+ result = opts[:plain] ? result.gsub(/["\s+]/, '') : JSON.load(result)
38
+ else
39
+ result = super
40
+ if result && !result.to_s.empty?
41
+ client.set key(api, opts), result.to_json
42
+ client.expire key(api, opts), Exchange.configuration.cache.expire == :daily ? 86400 : 3600
46
43
  end
47
-
48
- result
49
44
  end
50
45
 
46
+ result
51
47
  end
48
+
52
49
  end
53
50
  end
54
51
  end
@@ -1,88 +1,182 @@
1
1
  module Exchange
2
+
3
+ class << self
4
+
5
+ # A configuration setter for the Exchange gem. This comes in handy if you want to have backup or test setting
6
+ # @param [Exchange::Configuration] configuration the configuration to install
7
+ # @raise [ArgumentError] if fed with anything else than an Exchange::Configuration
8
+ # @return [Exchange::Configuration] The configuration installed
9
+ #
10
+ def configuration= configuration
11
+ if configuration.is_a? Exchange::Configuration
12
+ @configuration = configuration
13
+ else
14
+ raise ArgumentError.new("The configuration needs to be an instance of Exchange::Configuration")
15
+ end
16
+ end
17
+
18
+ # A getter for the configuration, returns the currently installed configuration or a default configuration
19
+ # if no configuration is installed
20
+ # @return [Exchange::Configuration] The currently installed / the default configuration
21
+ #
22
+ def configuration
23
+ @configuration ||= Configuration.new
24
+ end
25
+ end
26
+
2
27
  # @author Beat Richartz
3
28
  # A configuration class that stores the configuration of the gem. It allows to set the api from which the data gets retrieved,
4
29
  # the cache in which the data gets cached, the regularity of updates for the currency rates, how many times the api calls should be
5
30
  # retried on failure, and wether operations mixing currencies should raise errors or not
6
- # @version 0.1
31
+ # @version 0.6
7
32
  # @since 0.1
8
- class Configuration
33
+ #
34
+ class Configuration
35
+
9
36
  class << self
10
- @@config ||= {:api => :xavier_media, :retries => 5, :filestore_path => File.expand_path('exchange_filestore'), :allow_mixed_operations => true, :cache => :memcached, :cache_host => 'localhost', :cache_port => 11211, :update => :daily}
11
-
12
- # A configuration method that stores the configuration of the gem. It allows to set the api from which the data gets retrieved,
13
- # the cache in which the data gets cached, the regularity of updates for the currency rates, how many times the api calls should be
14
- # retried on failure, and wether operations mixing currencies should raise errors or not
15
- # @version 0.1
16
- # @since 0.1
17
- # @example Set a configuration by passing a block to the configuration
18
- # Exchange::Configuration.define do |c|
19
- # c.cache = :redis
20
- # c.cache_host = '127.0.0.1'
21
- # c.cache_port = 6547
22
- # c.api = :currency_bot
23
- # c.retries = 1
24
- # c.allow_mixed_operations = false
25
- # c.update = :hourly
26
- # end
27
- # @yield [Exchange::Configuration] yields a the configuration class
28
- # @yieldparam [optional, Symbol] cache The cache type to use. Possible Values: :redis, :memcached or :rails or false to disable caching. Defaults to :memcached
29
- # @yieldparam [optional, String] cache_host A string with the hostname or IP to set the cache host to. Does not have to be set for Rails cache
30
- # @yieldparam [optional, Integer] cache_port An integer for the cache port. Does not have to be set for Rails cache
31
- # @yieldparam [optional, Symbol] api The API to use. Possible Values: :currency_bot (Open Source currency bot API) or :xavier_media (Xavier Media API). Defaults to :currency_bot
32
- # @yieldparam [optional, Integer] retries The number of times the gem should retry to connect to the api host. Defaults to 5.
33
- # @yieldparam [optional, Boolean] If set to false, Operations with with different currencies raise errors. Defaults to true.
34
- # @yieldparam [optional, Symbol] The regularity of updates for the API. Possible values: :daily, :hourly. Defaults to :daily.
35
- # @yieldparam [optional, String] The path where files can be stored for the gem (used for large files from ECB). Make sure ruby has write access.
36
- # @example Set configuration values directly to the class
37
- # Exchange::Configuration.cache = :redis
38
- # Exchange::Configuration.api = :xavier_media
39
37
 
40
- def define &blk
41
- self.instance_eval(&blk)
42
- end
43
-
44
- [:api, :api_app_id, :retries, :cache, :cache_host, :cache_port, :filestore_path, :update, :allow_mixed_operations].each do |m|
45
- define_method m do
46
- @@config[m]
47
- end
48
- define_method :"#{m}=" do |data|
49
- @@config.merge! m => data
38
+ private
39
+
40
+ # @private
41
+ # @macro [setter] install_setter
42
+ #
43
+ def install_setter key
44
+ define_method :"#{key}=" do |data|
45
+ @config[key] = DEFAULTS[key].merge(data)
46
+ end
50
47
  end
51
- end
52
-
53
- # The instantiated api class according to the configuration
54
- # @example
55
- # Exchange::Configuration.api = :currency_bot
56
- # Exchange::Configuration.api_class #=> Exchange::ExternalAPI::CurrencyBot
57
- # @param [Hash] options A hash of Options
58
- # @option options [Class] :api A api to return instead of the api class (use for fallback)
59
- # @return [Exchange::ExternalAPI::Subclass] A subclass of Exchange::ExternalAPI
60
-
61
- def api_class(options={})
62
- @api_class ||= {}
63
- return @api_class[options[:api] || self.api] if @api_class[options[:api] || self.api]
64
48
 
65
- @api_class[options[:api] || self.api] = ExternalAPI.const_get((options[:api] || self.api).to_s.gsub(/(?:^|_)(.)/) { $1.upcase })
66
- end
67
-
68
- # The instantiated cache class according to the configuration
69
- # @example
70
- # Exchange::Configuration.cache = :redis
71
- # Exchange::Configuration.cache_class #=> Exchange::ExternalAPI::Redis
72
- # @param [Hash] options A hash of Options
73
- # @option options [Class] :api A api to return instead of the api class (use for fallback)
74
- # @return [Exchange::Cache::Subclass] A subclass of Exchange::Cache (or nil if caching has been set to false)
75
-
76
- def cache_class(options={})
77
- @cache_class ||= {}
78
- return @cache_class[options[:cache] || self.cache] if @cache_class[options[:cache] || self.cache]
79
49
 
80
- @cache_class[options[:cache] || self.cache] = if self.cache
81
- Cache.const_get((options[:cache] || self.cache).to_s.gsub(/(?:^|_)(.)/) { $1.upcase })
82
- else
83
- Cache::NoCache
50
+ # @private
51
+ # @macro [getter] install_getter
52
+ #
53
+ def install_getter key, parent_module
54
+ define_method key do
55
+ @config[key] = OpenStruct.new(@config[key]) unless @config[key].is_a?(OpenStruct)
56
+ @config[key].subclass = parent_module.const_get camelize(@config[key].subclass) unless @config[key].subclass.is_a?(Class)
57
+ @config[key]
58
+ end
84
59
  end
85
- end
60
+
61
+ end
62
+
63
+ # The configuration defaults
64
+ # @version 0.6
65
+ # @since 0.6
66
+ #
67
+ DEFAULTS = {
68
+ :api => {
69
+ :subclass => ExternalAPI::XavierMedia,
70
+ :retries => 5
71
+ },
72
+ :cache => {
73
+ :subclass => Cache::Memcached,
74
+ :host => 'localhost',
75
+ :port => 11211,
76
+ :expire => :daily
77
+ },
78
+ :allow_mixed_operations => true
79
+ }
80
+
81
+ # Initialize a new configuration. Takes a hash and/or a block. Lets you easily set the configuration the way you want it to be
82
+ # @version 0.6
83
+ # @since 0.6
84
+ # @param [Hash] configuration The configuration as a hash
85
+ # @param [Proc] block A block to yield the configuration with
86
+ # @example Define the configuration with a hash
87
+ # Exchange::Configuration.new(:allow_mixed_operations => false, :api => {:subclass => :currency_bot, :retries => 2})
88
+ # @example Define the configuration with a block
89
+ # Exchange::Configuration.new do |c|
90
+ # c.allow_mixed_operations = false
91
+ # c.cache = {
92
+ # :subclass => Cache::Redis,
93
+ # :expire => :hourly
94
+ # }
95
+ #
96
+ # @yield [Exchange::Configuration] yields an instance of the configuration class
97
+ # @yieldparam [optional, Symbol] cache The cache type to use. Possible Values: :redis, :memcached or :rails or false to disable caching. Defaults to :memcached
98
+ # @yieldparam [optional, String] cache_host A string with the hostname or IP to set the cache host to. Does not have to be set for Rails cache
99
+ # @yieldparam [optional, Integer] cache_port An integer for the cache port. Does not have to be set for Rails cache
100
+ # @yieldparam [optional, Symbol] api The API to use. Possible Values: :currency_bot (Open Source currency bot API) or :xavier_media (Xavier Media API). Defaults to :currency_bot
101
+ # @yieldparam [optional, Integer] retries The number of times the gem should retry to connect to the api host. Defaults to 5.
102
+ # @yieldparam [optional, Boolean] If set to false, Operations with with different currencies raise errors. Defaults to true.
103
+ # @yieldparam [optional, Symbol] The regularity of updates for the API. Possible values: :daily, :hourly. Defaults to :daily.
104
+ # @yieldparam [optional, String] The path where files can be stored for the gem (used for large files from ECB). Make sure ruby has write access.
105
+ #
106
+ def initialize configuration={}, &block
107
+ @config = DEFAULTS.merge(configuration)
108
+ self.instance_eval(&block) if block_given?
109
+ super()
86
110
  end
111
+
112
+ # Getter for the mixed operations configuration. If set to true, operations with mixed currencies will not raise errors
113
+ # If set to false, mixed operations will raise errors
114
+ # @since 0.6
115
+ # @version 0.6
116
+ # @return [Boolean] True if mixed operations are allowed, false if not
117
+ #
118
+ def allow_mixed_operations
119
+ @config[:allow_mixed_operations]
120
+ end
121
+
122
+ # Setter for the mixed operations configuration. If set to true, operations with mixed currencies will not raise errors
123
+ # If set to false, mixed operations will raise errors
124
+ # @since 0.6
125
+ # @version 0.6
126
+ # @param [Boolean] data The configuration to set
127
+ # @return [Boolean] The configuration set
128
+ #
129
+ def allow_mixed_operations= data
130
+ @config[:allow_mixed_operations] = data
131
+ end
132
+
133
+ # Setter for the api configuration.
134
+ # @since 0.6
135
+ # @version 0.6
136
+ # @param [Hash] The hash to set the configuration to
137
+ # @option [Symbol] :subclass The API subclass to use as a underscored symbol (will be camelized and constantized)
138
+ # @option [Integer] :retries The amount of retries on connection failure
139
+ # @example set the api to be ecb with a maximum of 8 retries on connection failure
140
+ # configuration.api = { :subclass => :ecb, :retries => 8 }
141
+ #
142
+ install_setter :api
143
+
144
+ # Setter for the cache configuration.
145
+ # @since 0.6
146
+ # @version 0.6
147
+ # @param [Hash] The hash to set the configuration to
148
+ # @option [Symbol] :subclass The Cache subclass to use as a underscored symbol (will be camelized and constantized)
149
+ # @option [String] :host The cache connection host
150
+ # @option [Integer] :port The cache connection port
151
+ # @option [Symbol] :expire The expiration period for the cache, can be :daily or :hourly, defaults to daily
152
+ # @example set the cache to be redis on localhost:6578 with hourly expiration
153
+ # configuration.cache = { :subclass => :redis, :host => 'localhost', :port => 6578, :expire => :hourly }
154
+ #
155
+ install_setter :cache
156
+
157
+ # Getter for the api configuration. Instantiates the configuration as an open struct, if called for the first time.
158
+ # Also camelizes and constantizes the api subclass, if used for the first time.
159
+ # @return [OpenStruct] an openstruct with the complete api configuration
160
+ #
161
+ install_getter :api, ExternalAPI
162
+
163
+ # Getter for the cache configuration. Instantiates the configuration as an open struct, if called for the first time.
164
+ # Also camelizes and constantizes the cache subclass, if used for the first time.
165
+ # @return [OpenStruct] an openstruct with the complete cache configuration
166
+ #
167
+ install_getter :cache, Cache
168
+
169
+ private
170
+
171
+ # Camelize a string or a symbol
172
+ # @param [String, Symbol] s The string to camelize
173
+ # @return [String] a camelized string
174
+ # @example Camelize an underscored symbol
175
+ # camelize(:some_thing) #=> "SomeThing"
176
+ #
177
+ def camelize s
178
+ s = s.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }
179
+ end
180
+
87
181
  end
88
182
  end