exchange 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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