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
@@ -0,0 +1,22 @@
1
+ module Exchange
2
+ module ExternalAPI
3
+
4
+ # The json base class takes care of JSON apis. It assumes you would want to use json as a parser and preloads the gem
5
+ # Also, this may serve as a base for some operations which might be common to the json apis
6
+ # @author Beat Richartz
7
+ # @version 0.6
8
+ # @since 0.6
9
+ #
10
+ class JSON < Base
11
+
12
+ # Initializer, essentially takes the arguments passed to initialization, loads the json gem on the way
13
+ # and passes the arguments to the api base
14
+ #
15
+ def initialize *args
16
+ Exchange::GemLoader.new('json').try_load unless defined?(::JSON)
17
+ super *args
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -6,8 +6,9 @@ module Exchange
6
6
  # @author Beat Richartz
7
7
  # @version 0.1
8
8
  # @since 0.1
9
-
10
- class XavierMedia < Base
9
+ #
10
+ class XavierMedia < XML
11
+
11
12
  # The base of the Xaviermedia API URL
12
13
  API_URL = "http://api.finance.xaviermedia.com/api"
13
14
  # The currencies the Xaviermedia API URL can handle
@@ -20,11 +21,11 @@ module Exchange
20
21
  # @option opts [Time, String] :at a historical date to get the exchange rates for
21
22
  # @example Update the currency bot API to use the file of March 2, 2010
22
23
  # Exchange::ExternalAPI::XavierMedia.new.update(:at => Time.gm(3,2,2010))
23
-
24
+ #
24
25
  def update(opts={})
25
26
  time = Exchange::Helper.assure_time(opts[:at], :default => :now)
26
27
  api_url = api_url(time)
27
- retry_urls = Exchange::Configuration.retries.times.map{ |i| api_url(time - 86400 * (i+1)) }
28
+ retry_urls = Exchange.configuration.api.retries.times.map{ |i| api_url(time - 86400 * (i+1)) }
28
29
 
29
30
  Call.new(api_url, :format => :xml, :at => time, :retry_with => retry_urls) do |result|
30
31
  @base = result.css('basecurrency').children[0].to_s
@@ -38,7 +39,7 @@ module Exchange
38
39
  # A helper function which build a valid api url for the specified time
39
40
  # @param [Time] time The exchange rate date for which the URL should be built
40
41
  # @return [String] An Xaviermedia API URL for the specified time
41
-
42
+ #
42
43
  def api_url(time)
43
44
  [API_URL, "#{time.strftime("%Y/%m/%d")}.xml"].join('/')
44
45
  end
@@ -0,0 +1,22 @@
1
+ module Exchange
2
+ module ExternalAPI
3
+
4
+ # The xml base class takes care of XML apis. It assumes you would want to use nokogiri as a parser and preloads the gem
5
+ # Also, this may serve as a base for some operations which might be common to the xml apis
6
+ # @author Beat Richartz
7
+ # @version 0.6
8
+ # @since 0.6
9
+ #
10
+ class XML < Base
11
+
12
+ # Initializer, essentially takes the arguments passed to initialization, loads nokogiri on the way
13
+ # and passes the arguments to the api base
14
+ #
15
+ def initialize *args
16
+ Exchange::GemLoader.new('nokogiri').try_load unless defined?(Nokogiri)
17
+ super *args
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ module Exchange
2
+
3
+ # The gem loader takes care of loading gems without adding too many unnecessary dependencies to the gem
4
+ # @author Beat Richartz
5
+ # @version 0.6
6
+ # @since 0.6
7
+ #
8
+ class GemLoader
9
+
10
+ # The error that gets thrown if a needed gem is not available or loadable
11
+ #
12
+ GemNotFoundError = Class.new LoadError
13
+
14
+ # initialize the loader with a gem name.
15
+ # @param [string] gem The gem to require
16
+ # @return [Exchange::Gemloader] an instance of the gemloader
17
+ # @example initialize a loader for the nokogiri gem
18
+ # Exchange::GemLoader.new('nokogiri')
19
+ #
20
+ def initialize gem
21
+ @gem = gem
22
+ end
23
+
24
+ # Try to require the gem specified on initialization.
25
+ # @raise [GemNotFoundError] an error indicating that the gem could not be found rather than a load error
26
+ # @example Try to load the JSON gem
27
+ # Exchange::GemLoader.new('json').try_load
28
+ #
29
+ def try_load
30
+ require @gem
31
+ rescue LoadError => e
32
+ 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
+ end
34
+ end
35
+ end
@@ -1,27 +1,34 @@
1
+ require 'singleton'
2
+ require 'forwardable'
3
+
1
4
  module Exchange
5
+
2
6
  # Helper Functions that get used throughout the gem can be placed here
3
7
  # @author Beat Richartz
4
- # @version 0.3
8
+ # @version 0.6
5
9
  # @since 0.3
6
-
10
+ #
7
11
  class Helper
8
- class << self
12
+ include Singleton
13
+ extend SingleForwardable
9
14
 
10
- # A helper function to assure a value is an instance of time
11
- # @param [Time, String, NilClass] The value to be asserted
12
- # @param [Hash] opts Options for assertion
13
- # @option opts [Symbol] :default a method that can be sent to Time if the argument is nil (:now for example)
14
-
15
- def assure_time(arg=nil, opts={})
16
- if arg
17
- arg.kind_of?(Time) ? arg : Time.gm(*arg.split('-'))
18
- elsif opts[:default]
19
- Time.send(opts[:default])
20
- end
15
+ # A helper function to assure a value is an instance of time
16
+ # @param [Time, String, NilClass] The value to be asserted
17
+ # @param [Hash] opts Options for assertion
18
+ # @option opts [Symbol] :default a method that can be sent to Time if the argument is nil (:now for example)
19
+ #
20
+ def assure_time(arg=nil, opts={})
21
+ if arg
22
+ arg.kind_of?(Time) ? arg : Time.gm(*arg.split('-'))
23
+ elsif opts[:default]
24
+ Time.send(opts[:default])
21
25
  end
22
-
23
26
  end
24
27
 
28
+ # Forwards the assure_time method to the instance using singleforwardable
29
+ #
30
+ def_delegator :instance, :assure_time
31
+
25
32
  end
26
33
 
27
34
  end
@@ -1,67 +1,74 @@
1
+ require 'singleton'
2
+ require 'forwardable'
3
+
1
4
  module Exchange
2
5
 
3
6
  # This class handles everything that has to do with certified formatting of the different currencies. The standard is stored in
4
7
  # the iso4217 YAML file.
5
- # @version 0.3
8
+ # @version 0.6
6
9
  # @since 0.3
7
10
  # @author Beat Richartz
8
-
11
+ #
9
12
  class ISO4217
13
+ include Singleton
14
+ extend SingleForwardable
15
+
10
16
  class << self
11
17
 
12
- # The ISO 4217 that have to be loaded. Nothing much to say here. Just use this method to get to the definitions
13
- # They are static, so they can be stored in a class variable without many worries
14
- # @return [Hash] The iso427 Definitions with the currency code as keys
15
-
16
- def definitions
17
- @@definitions ||= YAML.load_file(File.join(EXCHANGE_GEM_ROOT_PATH, 'iso4217.yml'))
18
- end
19
-
20
- # Use this to instantiate a currency amount. For one, it is important that we use BigDecimal here so nothing gets lost because
21
- # of floating point errors. For the other, This allows us to set the precision exactly according to the iso definition
22
- # @param [BigDecimal, Fixed, Float, String] amount The amount of money you want to instantiate
23
- # @param [String, Symbol] currency The currency you want to instantiate the money in
24
- # @return [BigDecimal] The instantiated currency
25
- # @example instantiate a currency from a string
26
- # Exchange::ISO4217.instantiate("4523", "usd") #=> #<Bigdecimal 4523.00>
27
-
28
- def instantiate(amount, currency)
29
- BigDecimal.new(amount.to_s, definitions[currency.to_s.upcase]['minor_unit'])
30
- end
31
-
32
- # Converts the currency to a string in ISO 4217 standardized format, either with or without the currency. This leaves you
33
- # with no worries how to display the currency.
34
- # @param [BigDecimal, Fixed, Float] amount The amount of currency you want to stringify
35
- # @param [String, Symbol] currency The currency you want to stringify
36
- # @param [Hash] opts The options for formatting
37
- # @option opts [Boolean] :amount_only Whether you want to have the currency in the string or not
38
- # @return [String] The formatted string
39
- # @example Convert a currency to a string
40
- # Exchange::ISO4217.stringify(49.567, :usd) #=> "USD 49.57"
41
- # @example Convert a currency without minor to a string
42
- # Exchange::ISO4217.stringif(45, :jpy) #=> "JPY 45"
43
- # @example Convert a currency with a three decimal minor to a string
44
- # Exchange::ISO4217.stringif(34.34, :omr) #=> "OMR 34.340"
45
- # @example Convert a currency to a string without the currency
46
- # Exchange::ISO4217.stringif(34.34, :omr, :amount_only => true) #=> "34.340"
47
-
48
- def stringify(amount, currency, opts={})
49
- format = "%.#{definitions[currency.to_s.upcase]['minor_unit']}f"
50
- "#{currency.to_s.upcase + ' ' unless opts[:amount_only]}#{format % amount}"
51
- end
52
-
53
18
  private
54
19
  # @private
55
20
  # @macro [attach] install_operations
56
21
 
57
22
  def install_operation op
58
23
  self.class_eval <<-EOV
59
- def self.#{op}(amount, currency, precision=nil)
24
+ def #{op}(amount, currency, precision=nil)
60
25
  minor = definitions[currency.to_s.upcase]['minor_unit']
61
26
  (amount.is_a?(BigDecimal) ? amount : BigDecimal.new(amount.to_s, minor)).#{op}(precision || minor)
62
27
  end
63
28
  EOV
64
29
  end
30
+
31
+ end
32
+
33
+ # The ISO 4217 that have to be loaded. Nothing much to say here. Just use this method to get to the definitions
34
+ # They are static, so they can be stored in a class variable without many worries
35
+ # @return [Hash] The iso427 Definitions with the currency code as keys
36
+ #
37
+ def definitions
38
+ @@definitions ||= YAML.load_file(File.join(ROOT_PATH, 'iso4217.yml'))
39
+ end
40
+
41
+ # Use this to instantiate a currency amount. For one, it is important that we use BigDecimal here so nothing gets lost because
42
+ # of floating point errors. For the other, This allows us to set the precision exactly according to the iso definition
43
+ # @param [BigDecimal, Fixed, Float, String] amount The amount of money you want to instantiate
44
+ # @param [String, Symbol] currency The currency you want to instantiate the money in
45
+ # @return [BigDecimal] The instantiated currency
46
+ # @example instantiate a currency from a string
47
+ # Exchange::ISO4217.instantiate("4523", "usd") #=> #<Bigdecimal 4523.00>
48
+ #
49
+ def instantiate(amount, currency)
50
+ BigDecimal.new(amount.to_s, definitions[currency.to_s.upcase]['minor_unit'])
51
+ end
52
+
53
+ # Converts the currency to a string in ISO 4217 standardized format, either with or without the currency. This leaves you
54
+ # with no worries how to display the currency.
55
+ # @param [BigDecimal, Fixed, Float] amount The amount of currency you want to stringify
56
+ # @param [String, Symbol] currency The currency you want to stringify
57
+ # @param [Hash] opts The options for formatting
58
+ # @option opts [Boolean] :amount_only Whether you want to have the currency in the string or not
59
+ # @return [String] The formatted string
60
+ # @example Convert a currency to a string
61
+ # Exchange::ISO4217.stringify(49.567, :usd) #=> "USD 49.57"
62
+ # @example Convert a currency without minor to a string
63
+ # Exchange::ISO4217.stringif(45, :jpy) #=> "JPY 45"
64
+ # @example Convert a currency with a three decimal minor to a string
65
+ # Exchange::ISO4217.stringif(34.34, :omr) #=> "OMR 34.340"
66
+ # @example Convert a currency to a string without the currency
67
+ # Exchange::ISO4217.stringif(34.34, :omr, :amount_only => true) #=> "34.340"
68
+ #
69
+ def stringify(amount, currency, opts={})
70
+ format = "%.#{definitions[currency.to_s.upcase]['minor_unit']}f"
71
+ "#{currency.to_s.upcase + ' ' unless opts[:amount_only]}#{format % amount}"
65
72
  end
66
73
 
67
74
  # Use this to round a currency amount. This allows us to round exactly to the number of minors the currency has in the
@@ -91,5 +98,9 @@ module Exchange
91
98
 
92
99
  install_operation :floor
93
100
 
101
+ # Forwards the assure_time method to the instance using singleforwardable
102
+ #
103
+ def_delegators :instance, :definitions, :instantiate, :stringify, :round, :ceil, :floor
104
+
94
105
  end
95
106
  end
@@ -2,14 +2,14 @@ require 'spec_helper'
2
2
 
3
3
  describe "Exchange::Conversability" do
4
4
  before(:all) do
5
- Exchange::Configuration.cache = false
5
+ Exchange.configuration = Exchange::Configuration.new { |c| c.cache = { :subclass => :no_cache } }
6
6
  end
7
7
  before(:each) do
8
8
  @time = Time.gm(2012,8,27)
9
9
  Time.stub! :now => @time
10
10
  end
11
11
  after(:all) do
12
- Exchange::Configuration.cache = :memcached
12
+ Exchange::configuration = Exchange::Configuration.new { |c| c.cache = { :subclass => :memcached } }
13
13
  end
14
14
  it "should define all currencies on Fixnum, Float and BigDecimal" do
15
15
  Exchange::ISO4217.definitions.keys.each do |c|
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Exchange::Cache::Base" do
4
- subject { Exchange::Cache::Base }
4
+ subject { Exchange::Cache::Base.instance }
5
5
  describe "key generation" do
6
6
  before(:each) do
7
7
  time = Time.gm 2012, 03, 01, 23, 23, 23
@@ -9,20 +9,20 @@ describe "Exchange::Cache::Base" do
9
9
  end
10
10
  context "with a daily cache" do
11
11
  it "should build a timestamped key with the class given, the yearday and the year" do
12
- Exchange::Cache::Base.send(:key, :xavier_media).should == 'exchange_xavier_media_2012_61'
13
- Exchange::Cache::Base.send(:key, :currency_bot).should == 'exchange_currency_bot_2012_61'
12
+ subject.send(:key, :xavier_media).should == 'exchange_xavier_media_2012_61'
13
+ subject.send(:key, :currency_bot).should == 'exchange_currency_bot_2012_61'
14
14
  end
15
15
  end
16
16
  context "with an hourly cache" do
17
17
  before(:each) do
18
- Exchange::Configuration.update = :hourly
18
+ Exchange.configuration.cache.expire = :hourly
19
19
  end
20
20
  after(:each) do
21
- Exchange::Configuration.update = :daily
21
+ Exchange.configuration.cache.expire = :daily
22
22
  end
23
23
  it "should build a timestamped key with the class given, the yearday, the year and the hour" do
24
- Exchange::Cache::Base.send(:key, :xavier_media).should == 'exchange_xavier_media_2012_61_23'
25
- Exchange::Cache::Base.send(:key, :currency_bot).should == 'exchange_currency_bot_2012_61_23'
24
+ subject.send(:key, :xavier_media).should == 'exchange_xavier_media_2012_61_23'
25
+ subject.send(:key, :currency_bot).should == 'exchange_currency_bot_2012_61_23'
26
26
  end
27
27
  end
28
28
  end
@@ -1,16 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Exchange::Cache::File" do
4
- subject { Exchange::Cache::File }
4
+ subject { Exchange::Cache::File.instance }
5
5
  before(:each) do
6
- Exchange::Configuration.define do |c|
7
- c.cache = :file
8
- end
6
+ Exchange.configuration = Exchange::Configuration.new { |c|
7
+ c.cache = {
8
+ :subclass => :file,
9
+ :path => 'STORE/PATH'
10
+ }
11
+ }
9
12
  end
10
13
  after(:each) do
11
- Exchange::Configuration.define do |c|
12
- c.cache = :memcached
13
- end
14
+ Exchange.configuration = Exchange::Configuration.new { |c|
15
+ c.cache = {
16
+ :subclass => :memcached
17
+ }
18
+ }
14
19
  end
15
20
  describe "cached" do
16
21
  it "should raise an error if no block was given" do
@@ -20,9 +25,8 @@ describe "Exchange::Cache::File" do
20
25
  context "with a daily cache" do
21
26
  before(:each) do
22
27
  subject.should_receive(:key).with('API_CLASS', nil).and_return('KEY')
23
- Exchange::Configuration.should_receive(:filestore_path).and_return('STORE')
24
- ::File.should_receive(:exists?).with('STORE/KEY').and_return(true)
25
- ::File.should_receive(:read).with('STORE/KEY').and_return 'CONTENT'
28
+ ::File.should_receive(:exists?).with('STORE/PATH/KEY').and_return(true)
29
+ ::File.should_receive(:read).with('STORE/PATH/KEY').and_return 'CONTENT'
26
30
  end
27
31
  it "should return the file contents" do
28
32
  subject.cached('API_CLASS') { 'something' }.should == 'CONTENT'
@@ -31,9 +35,8 @@ describe "Exchange::Cache::File" do
31
35
  context "with an monthly cache" do
32
36
  before(:each) do
33
37
  subject.should_receive(:key).with('API_CLASS', an_instance_of(Symbol)).and_return('KEY')
34
- Exchange::Configuration.should_receive(:filestore_path).and_return('STORE')
35
- ::File.should_receive(:exists?).with('STORE/KEY').and_return(true)
36
- ::File.should_receive(:read).with('STORE/KEY').and_return 'CONTENT'
38
+ ::File.should_receive(:exists?).with('STORE/PATH/KEY').and_return(true)
39
+ ::File.should_receive(:read).with('STORE/PATH/KEY').and_return 'CONTENT'
37
40
  end
38
41
  it "should return the file contents" do
39
42
  subject.cached('API_CLASS', :cache_period => :monthly) { 'something' }.should == 'CONTENT'
@@ -43,9 +46,8 @@ describe "Exchange::Cache::File" do
43
46
  context "when no file is cached yet" do
44
47
  before(:each) do
45
48
  subject.should_receive(:key).with('API_CLASS', an_instance_of(Symbol)).at_most(3).times.and_return('KEY')
46
- Exchange::Configuration.should_receive(:filestore_path).and_return('STORE')
47
- ::File.should_receive(:exists?).with('STORE/KEY').and_return(false)
48
- Dir.should_receive(:entries).with('STORE').once.and_return(%W(entry entry2))
49
+ ::File.should_receive(:exists?).with('STORE/PATH/KEY').and_return(false)
50
+ Dir.should_receive(:entries).with('STORE/PATH').once.and_return(%W(entry entry2))
49
51
  ::File.should_receive(:delete).with(an_instance_of(String)).twice
50
52
  FileUtils.should_receive(:mkdir_p).once
51
53
  ::File.should_receive(:open).once
@@ -57,8 +59,7 @@ describe "Exchange::Cache::File" do
57
59
  context "when no result is returned" do
58
60
  before(:each) do
59
61
  subject.should_receive(:key).with('API_CLASS', an_instance_of(Symbol)).at_most(3).times.and_return('KEY')
60
- Exchange::Configuration.should_receive(:filestore_path).and_return('STORE')
61
- ::File.should_receive(:exists?).with('STORE/KEY').and_return(false)
62
+ ::File.should_receive(:exists?).with('STORE/PATH/KEY').and_return(false)
62
63
  FileUtils.should_receive(:mkdir_p).never
63
64
  ::File.should_receive(:open).never
64
65
  end
@@ -1,41 +1,48 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Exchange::Cache::Memcached" do
4
- subject { Exchange::Cache::Memcached }
3
+ describe "Exchange::CacheDalli::Client" do
4
+ subject { Exchange::Cache::Memcached.instance }
5
5
  before(:each) do
6
- Exchange::Configuration.define do |c|
7
- c.cache_host = 'HOST'
8
- c.cache_port = 'PORT'
9
- end
6
+ Exchange.configuration = Exchange::Configuration.new { |c|
7
+ c.cache = {
8
+ :host => 'HOST',
9
+ :port => 'PORT'
10
+ }
11
+ }
10
12
  end
11
13
  after(:each) do
12
- Exchange::Configuration.define do |c|
13
- c.cache_host = 'localhost'
14
- c.cache_port = 11211
15
- end
14
+ Exchange.configuration = Exchange::Configuration.new { |c|
15
+ c.cache = {
16
+ :host => 'localhost',
17
+ :port => 11211
18
+ }
19
+ }
16
20
  end
17
21
  describe "client" do
18
22
  let(:client) { mock('memcached') }
23
+ before(:each) do
24
+ Exchange::GemLoader.new('dalli').try_load
25
+ end
19
26
  after(:each) do
20
- subject.send(:remove_class_variable, "@@client")
27
+ subject.instance_variable_set "@client", nil
21
28
  end
22
29
  it "should set up a client on the specified host and port for the cache" do
23
- ::Memcached.should_receive(:new).with("HOST:PORT").and_return(client)
30
+ Dalli::Client.should_receive(:new).with("HOST:PORT").and_return(client)
24
31
  subject.client.should == client
25
32
  end
26
33
  end
27
34
  describe "cached" do
35
+ let(:client) { mock('memcached', :get => '') }
36
+ before(:each) do
37
+ Dalli::Client.should_receive(:new).with("HOST:PORT").and_return(client)
38
+ end
39
+ after(:each) do
40
+ subject.instance_variable_set "@client", nil
41
+ end
28
42
  it "should raise an error if no block was given" do
29
43
  lambda { subject.cached('API_CLASS') }.should raise_error(Exchange::Cache::CachingWithoutBlockError)
30
44
  end
31
45
  context "when a cached result exists" do
32
- let(:client) { mock('memcached') }
33
- before(:each) do
34
- ::Memcached.should_receive(:new).with("HOST:PORT").and_return(client)
35
- end
36
- after(:each) do
37
- subject.send(:remove_class_variable, "@@client")
38
- end
39
46
  context "when loading json" do
40
47
  before(:each) do
41
48
  subject.should_receive(:key).with('API_CLASS', {}).and_return('KEY')
@@ -63,11 +70,7 @@ describe "Exchange::Cache::Memcached" do
63
70
  let(:client) { mock('memcached') }
64
71
  before(:each) do
65
72
  subject.should_receive(:key).with('API_CLASS', {}).twice.and_return('KEY')
66
- ::Memcached.should_receive(:new).with("HOST:PORT").and_return(client)
67
- client.should_receive(:get).with('KEY').and_raise(::Memcached::NotFound)
68
- end
69
- after(:each) do
70
- subject.send(:remove_class_variable, "@@client")
73
+ client.should_receive(:get).with('KEY').and_return(nil)
71
74
  end
72
75
  context "with daily cache" do
73
76
  it "should call the block and set and return the result" do
@@ -77,10 +80,10 @@ describe "Exchange::Cache::Memcached" do
77
80
  end
78
81
  context "with hourly cache" do
79
82
  before(:each) do
80
- Exchange::Configuration.update = :hourly
83
+ Exchange.configuration.cache.expire = :hourly
81
84
  end
82
85
  after(:each) do
83
- Exchange::Configuration.update = :daily
86
+ Exchange.configuration.cache.expire = :daily
84
87
  end
85
88
  it "should call the block and set and return the result" do
86
89
  client.should_receive(:set).with('KEY', "{\"RESULT\":\"YAY\"}", 3600).once