exchange 0.6.0 → 0.8.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.
- data/.gitignore +54 -0
- data/.travis.yml +4 -0
- data/Gemfile +1 -12
- data/Gemfile.lock +14 -28
- data/README.rdoc +91 -68
- data/changelog.rdoc +20 -1
- data/exchange-0.7.5.gem +0 -0
- data/exchange-0.7.6.gem +0 -0
- data/exchange.gemspec +22 -102
- data/lib/core_extensions/conversability.rb +5 -3
- data/lib/exchange/base.rb +1 -1
- data/lib/exchange/cache/base.rb +11 -3
- data/lib/exchange/cache/memcached.rb +4 -3
- data/lib/exchange/configuration.rb +5 -14
- data/lib/exchange/currency.rb +69 -19
- data/lib/exchange/external_api/base.rb +75 -15
- data/lib/exchange/external_api/call.rb +7 -4
- data/lib/exchange/external_api/currency_bot.rb +12 -2
- data/lib/exchange/external_api/ecb.rb +62 -8
- data/lib/exchange/external_api/json.rb +3 -13
- data/lib/exchange/external_api/xavier_media.rb +51 -8
- data/lib/exchange/helper.rb +1 -1
- data/lib/exchange/iso_4217.rb +1 -0
- data/spec/exchange/cache/memcached_spec.rb +41 -15
- data/spec/exchange/currency_spec.rb +185 -7
- data/spec/exchange/external_api/call_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- metadata +37 -31
- data/VERSION +0 -1
@@ -13,23 +13,37 @@ module Exchange
|
|
13
13
|
# # Define here which currencies your API can handle
|
14
14
|
# CURRENCIES = %W(usd chf)
|
15
15
|
#
|
16
|
-
# # Every instance of ExternalAPI Class has to have an update function which
|
16
|
+
# # Every instance of ExternalAPI Class has to have an update function which
|
17
|
+
# # gets the rates from the API
|
18
|
+
# #
|
17
19
|
# def update(opts={})
|
18
20
|
# # assure that you will get a Time object for the historical dates
|
19
|
-
#
|
21
|
+
# #
|
22
|
+
# time = helper.assure_time(opts[:at])
|
20
23
|
#
|
21
|
-
# #
|
24
|
+
# # Call your API (shown here with a helper function that builds your API URL).
|
25
|
+
# # Like this, your calls will get cached.
|
26
|
+
# #
|
22
27
|
# Call.new(api_url(time), :at => time) do |result|
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
28
|
+
#
|
29
|
+
# # Assign the currency conversion base.
|
30
|
+
# # Attention, this is readonly, self.base= won't work
|
31
|
+
# #
|
32
|
+
# @base = result['base']
|
33
|
+
#
|
34
|
+
# # assign the rates, this has to be a hash with the following format:
|
35
|
+
# # {'USD' => 1.23242, 'CHF' => 1.34323}.
|
36
|
+
# #
|
37
|
+
# # Attention, this is readonly, self.rates= won't work
|
38
|
+
# #
|
39
|
+
# @rates = result['rates']
|
40
|
+
#
|
41
|
+
# # Timestamp the api call result. This may come in handy to assure you have
|
42
|
+
# # the right result.
|
43
|
+
# #
|
44
|
+
# # Attention, this is readonly, self.timestamp= won't work
|
45
|
+
# #
|
46
|
+
# @timestamp = result['timestamp'].to_i
|
33
47
|
# end
|
34
48
|
#
|
35
49
|
# private
|
@@ -41,8 +55,11 @@ module Exchange
|
|
41
55
|
# end
|
42
56
|
# end
|
43
57
|
# end
|
58
|
+
#
|
44
59
|
# # Now, you can configure your API in the configuration. The Symbol will get camelcased and constantized
|
60
|
+
# #
|
45
61
|
# Exchange::Configuration.api.subclass = :my_custom
|
62
|
+
#
|
46
63
|
# # Have fun, and don't forget to write tests.
|
47
64
|
#
|
48
65
|
module ExternalAPI
|
@@ -69,6 +86,28 @@ module Exchange
|
|
69
86
|
#
|
70
87
|
attr_reader :rates
|
71
88
|
|
89
|
+
# @attr_reader
|
90
|
+
# @return [Exchange::Cache] The cache subclass
|
91
|
+
attr_reader :cache
|
92
|
+
|
93
|
+
# @attr_reader
|
94
|
+
# @return [Exchange::API] The api subclass
|
95
|
+
attr_reader :api
|
96
|
+
|
97
|
+
# @attr_reader
|
98
|
+
# @return [Exchange::Helper] The Exchange Helper
|
99
|
+
attr_reader :helper
|
100
|
+
|
101
|
+
# Initialize with a convenience accessor for the Cache and the api subclass
|
102
|
+
# @param [Any] args The args to initialize with
|
103
|
+
#
|
104
|
+
def initialize *args
|
105
|
+
@cache = Exchange.configuration.cache.subclass
|
106
|
+
@api = Exchange.configuration.api.subclass
|
107
|
+
@helper = Exchange::Helper.instance
|
108
|
+
|
109
|
+
super *args
|
110
|
+
end
|
72
111
|
|
73
112
|
# Delivers an exchange rate from one currency to another with the option of getting a historical exchange rate. This rate
|
74
113
|
# has to be multiplied with the amount of the currency which you define in from
|
@@ -82,13 +121,17 @@ module Exchange
|
|
82
121
|
# #=> 1.232231231
|
83
122
|
#
|
84
123
|
def rate(from, to, opts={})
|
85
|
-
rate =
|
124
|
+
rate = cache.cached(api, opts.merge(:key_for => [from, to], :plain => true)) do
|
86
125
|
update(opts)
|
126
|
+
|
87
127
|
rate_from = self.rates[to.to_s.upcase]
|
88
128
|
rate_to = self.rates[from.to_s.upcase]
|
89
|
-
|
129
|
+
|
130
|
+
test_for_rates_and_raise_if_nil rate_from, rate_to, opts[:at]
|
131
|
+
|
90
132
|
rate_from / rate_to
|
91
133
|
end
|
134
|
+
|
92
135
|
BigDecimal.new(rate.to_s)
|
93
136
|
end
|
94
137
|
|
@@ -107,6 +150,23 @@ module Exchange
|
|
107
150
|
amount * rate(from, to, opts)
|
108
151
|
end
|
109
152
|
|
153
|
+
# Converts an array to a hash
|
154
|
+
# @param [Array] array The array to convert
|
155
|
+
# @return [Hash] The hash out of the array
|
156
|
+
#
|
157
|
+
def to_hash! array
|
158
|
+
Hash[*array]
|
159
|
+
end
|
160
|
+
|
161
|
+
# Test for a error to be thrown when no rates are present
|
162
|
+
# @param [String] rate_from The rate from which should be converted
|
163
|
+
# @param [String] rate_to The rate to which should be converted
|
164
|
+
# @param [Time] time The time at which should be converted
|
165
|
+
# @raise [NoRateError] An error indicating that there is no rate present when there is no rate present
|
166
|
+
#
|
167
|
+
def test_for_rates_and_raise_if_nil rate_from, rate_to, time=nil
|
168
|
+
raise NoRateError.new("No rates where found for #{from} to #{to} #{'at ' + time.to_s if time}") unless rate_from && rate_to
|
169
|
+
end
|
110
170
|
end
|
111
171
|
end
|
112
172
|
end
|
@@ -30,14 +30,16 @@ module Exchange
|
|
30
30
|
# result = Exchange::ExternalAPI::Call.new('http://yourapiurl.com', :format => :xml)
|
31
31
|
# # Do something with that result
|
32
32
|
#
|
33
|
-
def initialize url, options={}, &block
|
33
|
+
def initialize url, options={}, &block
|
34
34
|
Exchange::GemLoader.new('nokogiri').try_load if options[:format] == :xml
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
api_config = Exchange.configuration.api
|
37
|
+
|
38
|
+
result = Exchange.configuration.cache.subclass.cached(options[:api] || api_config.subclass, options) do
|
39
|
+
load_url(url, options[:retries] || api_config.retries, options[:retry_with])
|
38
40
|
end
|
39
41
|
|
40
|
-
parsed = options[:format] == :xml ? Nokogiri.parse(result) : ::JSON.load(result)
|
42
|
+
parsed = options[:format] == :xml ? Nokogiri::XML.parse(result.sub("\n", '')) : ::JSON.load(result)
|
41
43
|
|
42
44
|
return parsed unless block_given?
|
43
45
|
yield parsed
|
@@ -49,6 +51,7 @@ module Exchange
|
|
49
51
|
# @param [String] url The url to be loaded
|
50
52
|
# @param [Integer] retries The number of retries to do if the API Call should fail with a HTTP Error
|
51
53
|
# @param [Array] retry_with An array of urls to retry the API call with if the call to the original URL should fail. These values will be shifted until a call succeeds or the number of maximum retries is reached
|
54
|
+
# @todo install a timeout for slow requests, but respect when loading large files
|
52
55
|
#
|
53
56
|
def load_url(url, retries, retry_with)
|
54
57
|
begin
|
@@ -24,17 +24,27 @@ module Exchange
|
|
24
24
|
# Exchange::ExternalAPI::CurrencyBot.new.update(:at => Time.gm(3,2,2010))
|
25
25
|
#
|
26
26
|
def update(opts={})
|
27
|
-
time =
|
27
|
+
time = helper.assure_time(opts[:at])
|
28
28
|
|
29
29
|
Call.new(api_url(time), :at => time) do |result|
|
30
30
|
@base = result['base']
|
31
|
-
@rates =
|
31
|
+
@rates = extract_rates(result)
|
32
32
|
@timestamp = result['timestamp'].to_i
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
+
# Helper method to extract rates from the api call result
|
39
|
+
# @param [JSON] parsed The parsed result
|
40
|
+
# @return [Hash] A hash with rates
|
41
|
+
# @since 0.7
|
42
|
+
# @version 0.7
|
43
|
+
#
|
44
|
+
def extract_rates parsed
|
45
|
+
to_hash! parsed['rates'].keys.zip(parsed['rates'].values.map{|v| BigDecimal.new(v.to_s) }).flatten
|
46
|
+
end
|
47
|
+
|
38
48
|
# A helper function to build an api url for either a specific time or the latest available rates
|
39
49
|
# @param [Time] time The time to build the api url for
|
40
50
|
# @return [String] an api url for the time specified
|
@@ -4,7 +4,7 @@ module Exchange
|
|
4
4
|
# The ECB class, handling communication with the European Central Bank XML File API
|
5
5
|
# You can find further information on the European Central Bank XML API API here: http://www.ecb.int/stats/exchange/eurofxref/html/index.en.html
|
6
6
|
# @author Beat Richartz
|
7
|
-
# @version 0.
|
7
|
+
# @version 0.7
|
8
8
|
# @since 0.3
|
9
9
|
#
|
10
10
|
class Ecb < XML
|
@@ -28,22 +28,23 @@ module Exchange
|
|
28
28
|
# @example Update the ecb API to use the file of March 2, 2010
|
29
29
|
# Exchange::ExternalAPI::Ecb.new.update(:at => Time.gm(3,2,2010))
|
30
30
|
#
|
31
|
+
# @since 0.1
|
32
|
+
# @version 0.7
|
33
|
+
#
|
31
34
|
def update(opts={})
|
32
|
-
time =
|
33
|
-
times =
|
35
|
+
time = helper.assure_time(opts[:at], :default => :now)
|
36
|
+
times = map_retry_times time
|
34
37
|
|
35
38
|
# Since the Ecb File retrieved can be very large (> 5MB for the history file) and parsing takes a fair amount of time,
|
36
39
|
# caching is doubled on this API
|
37
40
|
#
|
38
|
-
|
41
|
+
cache.cached(self.class, :at => time) do
|
39
42
|
Call.new(api_url(time), call_opts(time)) do |result|
|
40
43
|
t = time
|
41
44
|
|
42
45
|
# Weekends do not have rates present
|
43
46
|
#
|
44
|
-
while (r = result
|
45
|
-
t = times.shift
|
46
|
-
end
|
47
|
+
t = times.shift while (r = find_rate!(result, t)).empty? && !times.empty?
|
47
48
|
|
48
49
|
@callresult = r.to_s
|
49
50
|
end
|
@@ -52,7 +53,7 @@ module Exchange
|
|
52
53
|
parsed = Nokogiri.parse(self.callresult)
|
53
54
|
|
54
55
|
@base = 'EUR' # We just have to assume, since it's the ECB
|
55
|
-
@rates =
|
56
|
+
@rates = extract_rates(parsed.children.children)
|
56
57
|
@timestamp = time.to_i
|
57
58
|
end
|
58
59
|
|
@@ -72,9 +73,62 @@ module Exchange
|
|
72
73
|
].join('/')
|
73
74
|
end
|
74
75
|
|
76
|
+
# A helper method to find rates from the callresult given a certain time
|
77
|
+
# ECB packs the rates in «Cubes», so we try to find the cube appropriate to the time
|
78
|
+
# @param [Nokogiri::XML] parsed The parsed callresult
|
79
|
+
# @param [Time] time The time to parse for
|
80
|
+
# @return [Nokogiri::XML, NilClass] the rate, hopefully
|
81
|
+
# @since 0.7
|
82
|
+
# @version 0.7
|
83
|
+
#
|
84
|
+
def find_rate! parsed, time
|
85
|
+
parsed.css("Cube[time=\"#{time.strftime("%Y-%m-%d")}\"]")
|
86
|
+
end
|
87
|
+
|
88
|
+
# A helper method to extract rates from the callresult
|
89
|
+
# @param [Nokogiri::XML] parsed the parsed api data
|
90
|
+
# @return [Hash] a hash with rates
|
91
|
+
# @since 0.7
|
92
|
+
# @version 0.7
|
93
|
+
#
|
94
|
+
def extract_rates parsed
|
95
|
+
rate_array = parsed.map { |c|
|
96
|
+
map_to_currency_or_rate c
|
97
|
+
}.compact.flatten
|
98
|
+
|
99
|
+
to_hash!(['EUR', BigDecimal.new("1")] + rate_array)
|
100
|
+
end
|
101
|
+
|
102
|
+
# a helper method to map a key value pair to either currency or rate
|
103
|
+
# @param [Nokogiri::XML] xml a parsed xml part of the document
|
104
|
+
# @return [Array] An array with the following structure [currency, value, currency, value]
|
105
|
+
# @since 0.7
|
106
|
+
# @version 0.7
|
107
|
+
#
|
108
|
+
def map_to_currency_or_rate xml
|
109
|
+
unless (values = xml.attributes.values).empty?
|
110
|
+
values.map { |v|
|
111
|
+
val = v.value
|
112
|
+
val.match(/\d+/) ? BigDecimal.new(val) : val
|
113
|
+
}.sort_by(&:to_s).reverse
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Helper method to map retry times
|
118
|
+
# @param [Time] time The time to start with
|
119
|
+
# @return [Array] An array of times to retry api operation with
|
120
|
+
# @since 0.7
|
121
|
+
# @version 0.7
|
122
|
+
#
|
123
|
+
def map_retry_times time
|
124
|
+
Exchange.configuration.api.retries.times.map{ |i| time - 86400 * (i+1) }
|
125
|
+
end
|
126
|
+
|
75
127
|
# a wrapper for the call options, since the cache period is quite complex
|
76
128
|
# @param [Time] time The date of the exchange rate
|
77
129
|
# @return [Hash] a hash with the call options
|
130
|
+
# @since 0.6
|
131
|
+
# @version 0.6
|
78
132
|
#
|
79
133
|
def call_opts time
|
80
134
|
{:format => :xml, :at => time, :cache => :file, :cache_period => time >= Time.now - 90 * 86400 ? :daily : :monthly}
|
@@ -1,22 +1,12 @@
|
|
1
1
|
module Exchange
|
2
2
|
module ExternalAPI
|
3
3
|
|
4
|
-
# The json base class takes care of JSON apis.
|
5
|
-
#
|
4
|
+
# The json base class takes care of JSON apis.
|
5
|
+
# This may serve as a base for some operations which might be common to the json apis
|
6
6
|
# @author Beat Richartz
|
7
7
|
# @version 0.6
|
8
8
|
# @since 0.6
|
9
9
|
#
|
10
|
-
|
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
|
10
|
+
JSON = Class.new Base
|
21
11
|
end
|
22
12
|
end
|
@@ -4,7 +4,7 @@ module Exchange
|
|
4
4
|
# The XavierMedia API class, handling communication with the Xavier Media Currency API
|
5
5
|
# You can find further information on the Xaviermedia API here: http://www.xavierforum.com/viewtopic.php?f=5&t=10979&sid=671a685edbfa5dbec219fbc6793d5057
|
6
6
|
# @author Beat Richartz
|
7
|
-
# @version 0.
|
7
|
+
# @version 0.7
|
8
8
|
# @since 0.1
|
9
9
|
#
|
10
10
|
class XavierMedia < XML
|
@@ -16,21 +16,20 @@ module Exchange
|
|
16
16
|
|
17
17
|
# Updates the rates by getting the information from Xaviermedia API for today or a defined historical date
|
18
18
|
# The call gets cached for a maximum of 24 hours.
|
19
|
-
# @version 0.
|
19
|
+
# @version 0.7
|
20
20
|
# @param [Hash] opts Options to define for the API Call
|
21
21
|
# @option opts [Time, String] :at a historical date to get the exchange rates for
|
22
22
|
# @example Update the currency bot API to use the file of March 2, 2010
|
23
23
|
# Exchange::ExternalAPI::XavierMedia.new.update(:at => Time.gm(3,2,2010))
|
24
24
|
#
|
25
25
|
def update(opts={})
|
26
|
-
time =
|
26
|
+
time = helper.assure_time(opts[:at], :default => :now)
|
27
27
|
api_url = api_url(time)
|
28
|
-
retry_urls = Exchange.configuration.api.retries.times.map{ |i| api_url(time - 86400 * (i+1)) }
|
29
28
|
|
30
|
-
Call.new(api_url, :
|
31
|
-
@base = result
|
32
|
-
@rates =
|
33
|
-
@timestamp =
|
29
|
+
Call.new(api_url, api_opts(opts.merge(:at => time))) do |result|
|
30
|
+
@base = extract_base_currency result
|
31
|
+
@rates = extract_rates result
|
32
|
+
@timestamp = extract_timestamp result
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
@@ -44,6 +43,50 @@ module Exchange
|
|
44
43
|
[API_URL, "#{time.strftime("%Y/%m/%d")}.xml"].join('/')
|
45
44
|
end
|
46
45
|
|
46
|
+
# Options for the API call to make
|
47
|
+
# @param [Hash] opts The options to generate the call options with
|
48
|
+
# @option opts [Time, String] :at a historical date to get the exchange rates for
|
49
|
+
# @return [Hash] The options hash for the API call
|
50
|
+
# @since 0.6
|
51
|
+
# @version 0.6
|
52
|
+
#
|
53
|
+
def api_opts(opts={})
|
54
|
+
retry_urls = Exchange.configuration.api.retries.times.map { |i| api_url(opts[:at] - 86400 * (i+1)) }
|
55
|
+
|
56
|
+
{ :format => :xml, :at => opts[:at], :retry_with => retry_urls }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Extract a timestamp of the callresult
|
60
|
+
# @param [Nokogiri::XML] result the callresult
|
61
|
+
# @return [Integer] A unix timestamp
|
62
|
+
# @since 0.7
|
63
|
+
# @version 0.7
|
64
|
+
#
|
65
|
+
def extract_timestamp(result)
|
66
|
+
Time.gm(*result.css('fx_date').children[0].to_s.split('-')).to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
# Extract rates from the callresult
|
70
|
+
# @param [Nokogiri::XML] result the callresult
|
71
|
+
# @return [Hash] A hash with currency / rate pairs
|
72
|
+
# @since 0.7
|
73
|
+
# @version 0.7
|
74
|
+
#
|
75
|
+
def extract_rates(result)
|
76
|
+
rates_array = result.css('fx currency_code').children.map(&:to_s).zip(result.css('fx rate').children.map{|c| BigDecimal.new(c.to_s) }).flatten
|
77
|
+
to_hash!(rates_array)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Extract the base currency from the callresult
|
81
|
+
# @param [Nokogiri::XML] result the callresult
|
82
|
+
# @return [String] The base currency for the rates
|
83
|
+
# @since 0.7
|
84
|
+
# @version 0.7
|
85
|
+
#
|
86
|
+
def extract_base_currency(result)
|
87
|
+
result.css('basecurrency').children[0].to_s
|
88
|
+
end
|
89
|
+
|
47
90
|
end
|
48
91
|
end
|
49
92
|
end
|
data/lib/exchange/helper.rb
CHANGED
@@ -13,7 +13,7 @@ module Exchange
|
|
13
13
|
extend SingleForwardable
|
14
14
|
|
15
15
|
# A helper function to assure a value is an instance of time
|
16
|
-
# @param [Time, String, NilClass] The value to be asserted
|
16
|
+
# @param [Time, String, NilClass] arg The value to be asserted
|
17
17
|
# @param [Hash] opts Options for assertion
|
18
18
|
# @option opts [Symbol] :default a method that can be sent to Time if the argument is nil (:now for example)
|
19
19
|
#
|
data/lib/exchange/iso_4217.rb
CHANGED
@@ -68,26 +68,52 @@ describe "Exchange::CacheDalli::Client" do
|
|
68
68
|
end
|
69
69
|
context "when no cached result exists" do
|
70
70
|
let(:client) { mock('memcached') }
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
71
|
+
context "when returning nil" do
|
72
|
+
before(:each) do
|
73
|
+
subject.should_receive(:key).with('API_CLASS', {}).twice.and_return('KEY')
|
74
|
+
client.should_receive(:get).with('KEY').and_return(nil)
|
75
|
+
end
|
76
|
+
context "with daily cache" do
|
77
|
+
it "should call the block and set and return the result" do
|
78
|
+
client.should_receive(:set).with('KEY', "{\"RESULT\":\"YAY\"}", 86400).once
|
79
|
+
subject.cached('API_CLASS') { {'RESULT' => 'YAY'} }.should == {'RESULT' => 'YAY'}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
context "with hourly cache" do
|
83
|
+
before(:each) do
|
84
|
+
Exchange.configuration.cache.expire = :hourly
|
85
|
+
end
|
86
|
+
after(:each) do
|
87
|
+
Exchange.configuration.cache.expire = :daily
|
88
|
+
end
|
89
|
+
it "should call the block and set and return the result" do
|
90
|
+
client.should_receive(:set).with('KEY', "{\"RESULT\":\"YAY\"}", 3600).once
|
91
|
+
subject.cached('API_CLASS') { {'RESULT' => 'YAY'} }.should == {'RESULT' => 'YAY'}
|
92
|
+
end
|
79
93
|
end
|
80
94
|
end
|
81
|
-
context "
|
95
|
+
context "when returning an empty string" do
|
82
96
|
before(:each) do
|
83
|
-
|
97
|
+
subject.should_receive(:key).with('API_CLASS', {}).twice.and_return('KEY')
|
98
|
+
client.should_receive(:get).with('KEY').and_return('')
|
84
99
|
end
|
85
|
-
|
86
|
-
|
100
|
+
context "with daily cache" do
|
101
|
+
it "should call the block and set and return the result" do
|
102
|
+
client.should_receive(:set).with('KEY', "{\"RESULT\":\"YAY\"}", 86400).once
|
103
|
+
subject.cached('API_CLASS') { {'RESULT' => 'YAY'} }.should == {'RESULT' => 'YAY'}
|
104
|
+
end
|
87
105
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
106
|
+
context "with hourly cache" do
|
107
|
+
before(:each) do
|
108
|
+
Exchange.configuration.cache.expire = :hourly
|
109
|
+
end
|
110
|
+
after(:each) do
|
111
|
+
Exchange.configuration.cache.expire = :daily
|
112
|
+
end
|
113
|
+
it "should call the block and set and return the result" do
|
114
|
+
client.should_receive(:set).with('KEY', "{\"RESULT\":\"YAY\"}", 3600).once
|
115
|
+
subject.cached('API_CLASS') { {'RESULT' => 'YAY'} }.should == {'RESULT' => 'YAY'}
|
116
|
+
end
|
91
117
|
end
|
92
118
|
end
|
93
119
|
end
|