rack-cache 0.4 → 0.5

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.

Potentially problematic release.


This version of rack-cache might be problematic. Click here for more details.

data/CHANGES CHANGED
@@ -1,3 +1,27 @@
1
+ ## 0.5.0 / May 2009
2
+
3
+ * Added meta and entity store implementations based on the
4
+ memcache-client library. These are the default unless the memcached
5
+ library has already been required.
6
+
7
+ * The "allow_reload" and "allow_revalidate" options now default to
8
+ false instead of true. This means we break with RFC 2616 out of
9
+ the box but this is the expected configuration in a huge majority
10
+ of gateway cache scenarios. See the docs on configuration
11
+ options for more information on these options:
12
+ http://tomayko.com/src/rack-cache/configuration
13
+
14
+ * Added Google AppEngine memcache entity store and metastore
15
+ implementations. To use GAE's memcache with rack-cache, set the
16
+ "metastore" and "entitystore" options as follows:
17
+
18
+ use Rack::Cache,
19
+ :metastore => 'gae://cache-meta',
20
+ :entitystore => 'gae://cache-body'
21
+
22
+ The 'cache-meta' and 'cache-body' parts are memcache namespace
23
+ prefixes and should be set to different values.
24
+
1
25
  ## 0.4.0 / March 2009
2
26
 
3
27
  * Ruby 1.9.1 / Rack 1.0 compatible.
data/TODO CHANGED
@@ -1,31 +1,27 @@
1
- ## Backlog
1
+ ## 0.5
2
2
 
3
- - Move breakers.rb configuration file into rack-contrib as a middleware
4
- component.
3
+ - Document allow_revalidate and allow_reload options.
4
+ - Support multiple memcache servers.
5
+ - Purge/invalidate everything
6
+ - Explicit expiration/invalidation based on response headers or via an
7
+ object interface passed in the rack env.
5
8
  - Sample apps: Rack, Rails, Sinatra, Merb, etc.
9
+ - Move old breakers.rb configuration file into rack-contrib as a
10
+ middleware component.
11
+
12
+ ## Backlog
13
+
6
14
  - Use Bacon instead of test/spec
7
- - Work with both memcache and memcached gems (memcached hasn't built on MacOS
8
- for some time now).
9
15
  - Fast path pass processing. We do a lot more than necessary just to determine
10
16
  that the response should be passed through untouched.
11
- - Don't purge/remove cache entries when invalidating. The entries should be
12
- marked as stale and be forced revalidated on the next request instead of
13
- being removed entirely.
14
- - Add missing Expires header if we have a max-age.
15
- - Purge/invalidate everything
16
17
  - Invalidate at the URI of the Location or Content-Location response header
17
18
  on POST, PUT, or DELETE that results in a redirect.
18
19
  - Maximum size of cached entity
19
20
  - Last-Modified factor: requests that have a Last-Modified header but no Expires
20
21
  header have a TTL assigned based on the last modified age of the response:
21
22
  TTL = (Age * Factor), or, 1h = (10h * 0.1)
22
- - Run under multiple-threads with an option to lock before making requests
23
- to the backend. The idea is to be able to serve requests from cache in
24
- separate threads. This should probably be implemented as a separate
25
- middleware component.
26
23
  - Consider implementing ESI (http://www.w3.org/TR/esi-lang). This should
27
24
  probably be implemented as a separate middleware component.
28
- - Sqlite3 (meta store)
29
25
  - stale-while-revalidate
30
26
  - Serve cached copies when down (see: stale-if-error) - e.g., database
31
27
  connection drops and the cache takes over what it can.
@@ -16,8 +16,8 @@ __Environment__.
16
16
  When the __Rack::Cache__ object is instantiated:
17
17
 
18
18
  use Rack::Cache,
19
- :verbose => true,
20
- :metastore => 'memcached://localhost:11211/',
19
+ :verbose => true,
20
+ :metastore => 'memcached://localhost:11211/',
21
21
  :entitystore => 'file:/var/cache/rack'
22
22
 
23
23
  Using __Rack__'s __Environment__:
@@ -81,6 +81,32 @@ If any of these headers are present in the request, the response is considered
81
81
  private and will not be cached _unless_ the response is explicitly marked public
82
82
  (e.g., `Cache-Control: public`).
83
83
 
84
+ ### `allow_reload`
85
+
86
+ A boolean specifying whether reload requests sent by the client should be
87
+ honored by the cache. When this option is enabled (`rack-cache.allow_reload`
88
+ is `true`), requests that include a `Cache-Control: no-cache` header cause
89
+ the cache to discard anything it has stored for the request and ask that the
90
+ response be fully generated.
91
+
92
+ Most browsers include a `Cache-Control: no-cache` header when the user performs
93
+ a "hard refresh" (e.g., holding `Shift` while clicking the "Refresh" button).
94
+
95
+ *IMPORTANT: Enabling this option globally allows all clients to break your cache.*
96
+
97
+ ### `allow_revalidate`
98
+
99
+ A boolean specifying whether revalidate requests sent by the client should be
100
+ honored by the cache. When this option is enabled (`rack-cache.allow_revalidate`
101
+ is `true`), requests that include a `Cache-Control: max-age=0` header cause the
102
+ cache to assume its copy of the response is stale, resulting in a conditional
103
+ GET / validation request to be sent to the server.
104
+
105
+ Most browsers include a `Cache-Control: max-age=0` header when the user performs
106
+ a refresh (e.g., clicking the "Refresh" button).
107
+
108
+ *IMPORTANT: Enabling this option globally allows all clients to break your cache.*
109
+
84
110
  ### `cache_key`
85
111
 
86
112
  TODO: Document custom cache keys
data/doc/index.markdown CHANGED
@@ -12,6 +12,9 @@ for [Rack][]-based applications that produce freshness (`Expires`,
12
12
  News
13
13
  ----
14
14
 
15
+ * Rack::Cache 0.5 was released on May 25, 2009. See the
16
+ [`CHANGES`](http://github.com/rtomayko/rack-cache/blob/0.5.0/CHANGES) file
17
+ for details.
15
18
  * [How to use Rack::Cache with Rails 2.3](http://snippets.aktagon.com/snippets/302-How-to-setup-and-use-Rack-Cache-with-Rails-2-3-0-RC-1) - it's really easy.
16
19
  * [RailsLab's Advanced HTTP Caching Screencast](http://railslab.newrelic.com/2009/02/26/episode-11-advanced-http-caching)
17
20
  is a really great review of HTTP caching concepts and shows how to
@@ -0,0 +1,52 @@
1
+ require 'base64'
2
+
3
+ module Rack::Cache::AppEngine
4
+
5
+ module MC
6
+ require 'java'
7
+
8
+ import com.google.appengine.api.memcache.Expiration;
9
+ import com.google.appengine.api.memcache.MemcacheService;
10
+ import com.google.appengine.api.memcache.MemcacheServiceFactory;
11
+ import com.google.appengine.api.memcache.Stats;
12
+
13
+ Service = MemcacheServiceFactory.getMemcacheService
14
+ end unless defined?(Rack::Cache::AppEngine::MC)
15
+
16
+ class MemCache
17
+
18
+ def initialize(options = {})
19
+ @cache = MC::Service
20
+ @cache.namespace = options[:namespace] if options[:namespace]
21
+ end
22
+
23
+ def contains?(key)
24
+ MC::Service.contains(key)
25
+ end
26
+
27
+ def get(key)
28
+ value = MC::Service.get(key)
29
+ Marshal.load(Base64.decode64(value)) if value
30
+ end
31
+
32
+ def put(key, value, ttl = nil)
33
+ expiration = ttl ? MC::Expiration.byDeltaSeconds(ttl) : nil
34
+ value = Base64.encode64(Marshal.dump(value)).gsub(/\n/, '')
35
+ MC::Service.put(key, value, expiration)
36
+ end
37
+
38
+ def namespace
39
+ MC::Service.getNamespace
40
+ end
41
+
42
+ def namespace=(value)
43
+ MC::Service.setNamespace(value.to_s)
44
+ end
45
+
46
+ def delete(key)
47
+ MC::Service.delete(key)
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -19,7 +19,7 @@ module Rack::Cache
19
19
  yield part
20
20
  end
21
21
  body.close if body.respond_to? :close
22
- [ digest.hexdigest, size ]
22
+ [digest.hexdigest, size]
23
23
  end
24
24
 
25
25
  if ''.respond_to?(:bytesize)
@@ -168,35 +168,136 @@ module Rack::Cache
168
168
  DISK = Disk
169
169
  FILE = Disk
170
170
 
171
- # Stores entity bodies in memcached.
172
- class MemCache < EntityStore
173
-
171
+ # Base class for memcached entity stores.
172
+ class MemCacheBase < EntityStore
174
173
  # The underlying Memcached instance used to communicate with the
175
- # memcahced daemon.
174
+ # memcached daemon.
176
175
  attr_reader :cache
177
176
 
177
+ extend Rack::Utils
178
+
179
+ def open(key)
180
+ data = read(key)
181
+ data && [data]
182
+ end
183
+
184
+ def self.resolve(uri)
185
+ server = "#{uri.host}:#{uri.port || '11211'}"
186
+ options = parse_query(uri.query)
187
+ options.keys.each do |key|
188
+ value =
189
+ case value = options.delete(key)
190
+ when 'true' ; true
191
+ when 'false' ; false
192
+ else value.to_sym
193
+ end
194
+ options[k.to_sym] = value
195
+ end
196
+ options[:namespace] = uri.path.sub(/^\//, '')
197
+ new server, options
198
+ end
199
+ end
200
+
201
+ # Uses the memcache-client ruby library. This is the default unless
202
+ # the memcached library has already been required.
203
+ class MemCache < MemCacheBase
204
+ def initialize(server="localhost:11211", options={})
205
+ @cache =
206
+ if server.respond_to?(:stats)
207
+ server
208
+ else
209
+ require 'memcache'
210
+ ::MemCache.new(server, options)
211
+ end
212
+ end
213
+
214
+ def exist?(key)
215
+ !cache.get(key).nil?
216
+ end
217
+
218
+ def read(key)
219
+ cache.get(key)
220
+ end
221
+
222
+ def write(body)
223
+ buf = StringIO.new
224
+ key, size = slurp(body){|part| buf.write(part) }
225
+ [key, size] if cache.set(key, buf.string)
226
+ end
227
+
228
+ def purge(key)
229
+ cache.delete(key)
230
+ nil
231
+ end
232
+ end
233
+
234
+ # Uses the memcached client library. The ruby based memcache-client is used
235
+ # in preference to this store unless the memcached library has already been
236
+ # required.
237
+ class MemCached < MemCacheBase
178
238
  def initialize(server="localhost:11211", options={})
239
+ options[:prefix_key] ||= options.delete(:namespace) if options.key?(:namespace)
179
240
  @cache =
180
241
  if server.respond_to?(:stats)
181
242
  server
182
243
  else
183
244
  require 'memcached'
184
- Memcached.new(server, options)
245
+ ::Memcached.new(server, options)
185
246
  end
186
247
  end
187
248
 
188
249
  def exist?(key)
189
250
  cache.append(key, '')
190
251
  true
191
- rescue Memcached::NotStored
252
+ rescue ::Memcached::NotStored
192
253
  false
193
254
  end
194
255
 
195
256
  def read(key)
196
257
  cache.get(key, false)
197
- rescue Memcached::NotFound
258
+ rescue ::Memcached::NotFound
259
+ nil
260
+ end
261
+
262
+ def write(body)
263
+ buf = StringIO.new
264
+ key, size = slurp(body){|part| buf.write(part) }
265
+ cache.set(key, buf.string, 0, false)
266
+ [key, size]
267
+ end
268
+
269
+ def purge(key)
270
+ cache.delete(key)
271
+ nil
272
+ rescue ::Memcached::NotFound
198
273
  nil
199
274
  end
275
+ end
276
+
277
+ MEMCACHE =
278
+ if defined?(::Memcached)
279
+ MemCached
280
+ else
281
+ MemCache
282
+ end
283
+
284
+ MEMCACHED = MEMCACHE
285
+
286
+ class GAEStore < EntityStore
287
+ attr_reader :cache
288
+
289
+ def initialize(options = {})
290
+ require 'rack/cache/appengine'
291
+ @cache = Rack::Cache::AppEngine::MemCache.new(options)
292
+ end
293
+
294
+ def exist?(key)
295
+ cache.contains?(key)
296
+ end
297
+
298
+ def read(key)
299
+ cache.get(key)
300
+ end
200
301
 
201
302
  def open(key)
202
303
  if data = read(key)
@@ -209,45 +310,24 @@ module Rack::Cache
209
310
  def write(body)
210
311
  buf = StringIO.new
211
312
  key, size = slurp(body){|part| buf.write(part) }
212
- cache.set(key, buf.string, 0, false)
313
+ cache.put(key, buf.string)
213
314
  [key, size]
214
315
  end
215
316
 
216
317
  def purge(key)
217
318
  cache.delete(key)
218
319
  nil
219
- rescue Memcached::NotFound
220
- nil
221
320
  end
222
321
 
223
- extend Rack::Utils
224
-
225
- # Create MemCache store for the given URI. The URI must specify
226
- # a host and may specify a port, namespace, and options:
227
- #
228
- # memcached://example.com:11211/namespace?opt1=val1&opt2=val2
229
- #
230
- # Query parameter names and values are documented with the memcached
231
- # library: http://tinyurl.com/4upqnd
232
322
  def self.resolve(uri)
233
- server = "#{uri.host}:#{uri.port || '11211'}"
234
- options = parse_query(uri.query)
235
- options.keys.each do |key|
236
- value =
237
- case value = options.delete(key)
238
- when 'true' ; true
239
- when 'false' ; false
240
- else value.to_sym
241
- end
242
- options[k.to_sym] = value
243
- end
244
- options[:namespace] = uri.path.sub(/^\//, '')
245
- new server, options
323
+ self.new(:namespace => uri.host)
246
324
  end
325
+
247
326
  end
248
327
 
249
- MEMCACHE = MemCache
250
- MEMCACHED = MemCache
328
+ GAECACHE = GAEStore
329
+ GAE = GAEStore
330
+
251
331
  end
252
332
 
253
333
  end
@@ -259,8 +259,65 @@ module Rack::Cache
259
259
  # Stores request/response pairs in memcached. Keys are not stored
260
260
  # directly since memcached has a 250-byte limit on key names. Instead,
261
261
  # the SHA1 hexdigest of the key is used.
262
- class MemCache < MetaStore
262
+ class MemCacheBase < MetaStore
263
+ extend Rack::Utils
264
+
265
+ # The MemCache object used to communicated with the memcached
266
+ # daemon.
267
+ attr_reader :cache
268
+
269
+ # Create MemCache store for the given URI. The URI must specify
270
+ # a host and may specify a port, namespace, and options:
271
+ #
272
+ # memcached://example.com:11211/namespace?opt1=val1&opt2=val2
273
+ #
274
+ # Query parameter names and values are documented with the memcached
275
+ # library: http://tinyurl.com/4upqnd
276
+ def self.resolve(uri)
277
+ server = "#{uri.host}:#{uri.port || '11211'}"
278
+ options = parse_query(uri.query)
279
+ options.keys.each do |key|
280
+ value =
281
+ case value = options.delete(key)
282
+ when 'true' ; true
283
+ when 'false' ; false
284
+ else value.to_sym
285
+ end
286
+ options[k.to_sym] = value
287
+ end
288
+ options[:namespace] = uri.path.sub(/^\//, '')
289
+ new server, options
290
+ end
291
+ end
292
+
293
+ class MemCache < MemCacheBase
294
+ def initialize(server="localhost:11211", options={})
295
+ @cache =
296
+ if server.respond_to?(:stats)
297
+ server
298
+ else
299
+ require 'memcache'
300
+ ::MemCache.new(server, options)
301
+ end
302
+ end
303
+
304
+ def read(key)
305
+ key = hexdigest(key)
306
+ cache.get(key) || []
307
+ end
308
+
309
+ def write(key, entries)
310
+ key = hexdigest(key)
311
+ cache.set(key, entries)
312
+ end
313
+
314
+ def purge(key)
315
+ cache.delete(hexdigest(key))
316
+ nil
317
+ end
318
+ end
263
319
 
320
+ class MemCached < MemCacheBase
264
321
  # The Memcached instance used to communicated with the memcached
265
322
  # daemon.
266
323
  attr_reader :cache
@@ -294,35 +351,49 @@ module Rack::Cache
294
351
  rescue Memcached::NotFound
295
352
  nil
296
353
  end
354
+ end
297
355
 
298
- extend Rack::Utils
356
+ MEMCACHE =
357
+ if defined?(::Memcached)
358
+ MemCached
359
+ else
360
+ MemCache
361
+ end
362
+ MEMCACHED = MemCache
363
+
364
+ class GAEStore < MetaStore
365
+ attr_reader :cache
366
+
367
+ def initialize(options = {})
368
+ require 'rack/cache/appengine'
369
+ @cache = Rack::Cache::AppEngine::MemCache.new(options)
370
+ end
371
+
372
+ def read(key)
373
+ key = hexdigest(key)
374
+ cache.get(key) || []
375
+ end
376
+
377
+ def write(key, entries)
378
+ key = hexdigest(key)
379
+ cache.put(key, entries)
380
+ end
381
+
382
+ def purge(key)
383
+ key = hexdigest(key)
384
+ cache.delete(key)
385
+ nil
386
+ end
299
387
 
300
- # Create MemCache store for the given URI. The URI must specify
301
- # a host and may specify a port, namespace, and options:
302
- #
303
- # memcached://example.com:11211/namespace?opt1=val1&opt2=val2
304
- #
305
- # Query parameter names and values are documented with the memcached
306
- # library: http://tinyurl.com/4upqnd
307
388
  def self.resolve(uri)
308
- server = "#{uri.host}:#{uri.port || '11211'}"
309
- options = parse_query(uri.query)
310
- options.keys.each do |key|
311
- value =
312
- case value = options.delete(key)
313
- when 'true' ; true
314
- when 'false' ; false
315
- else value.to_sym
316
- end
317
- options[k.to_sym] = value
318
- end
319
- options[:namespace] = uri.path.sub(/^\//, '')
320
- new server, options
389
+ self.new(:namespace => uri.host)
321
390
  end
391
+
322
392
  end
323
393
 
324
- MEMCACHE = MemCache
325
- MEMCACHED = MemCache
394
+ GAECACHE = GAEStore
395
+ GAE = GAEStore
396
+
326
397
  end
327
398
 
328
399
  end
@@ -133,8 +133,8 @@ module Rack::Cache
133
133
  'rack-cache.entitystore' => 'heap:/',
134
134
  'rack-cache.default_ttl' => 0,
135
135
  'rack-cache.private_headers' => ['Authorization', 'Cookie'],
136
- 'rack-cache.allow_reload' => true,
137
- 'rack-cache.allow_revalidate' => true
136
+ 'rack-cache.allow_reload' => false,
137
+ 'rack-cache.allow_revalidate' => false
138
138
  }
139
139
  self.options = options
140
140
  end
data/rack-cache.gemspec CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'rack-cache'
6
- s.version = '0.4'
7
- s.date = '2009-03-16'
6
+ s.version = '0.5'
7
+ s.date = '2009-05-25'
8
8
 
9
9
  s.description = "HTTP Caching for Rack"
10
10
  s.summary = "HTTP Caching for Rack"
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
30
30
  example/sinatra/app.rb
31
31
  example/sinatra/views/index.erb
32
32
  lib/rack/cache.rb
33
+ lib/rack/cache/appengine.rb
33
34
  lib/rack/cache/cachecontrol.rb
34
35
  lib/rack/cache/context.rb
35
36
  lib/rack/cache/entitystore.rb
@@ -58,7 +59,7 @@ Gem::Specification.new do |s|
58
59
  s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/}
59
60
 
60
61
  s.extra_rdoc_files = %w[README COPYING TODO CHANGES]
61
- s.add_dependency 'rack', '~> 0.4'
62
+ s.add_dependency 'rack', '>= 0.4'
62
63
 
63
64
  s.has_rdoc = true
64
65
  s.homepage = "http://tomayko.com/src/rack-cache/"
data/test/context_test.rb CHANGED
@@ -124,7 +124,8 @@ describe 'Rack::Cache::Context' do
124
124
  response.headers.should.include 'Age'
125
125
  end
126
126
 
127
- it 'reloads responses when cache hits but no-cache request directive present' do
127
+ it 'reloads responses when cache hits but no-cache request directive present ' +
128
+ 'when allow_reload is set true' do
128
129
  count = 0
129
130
  respond_with 200, 'Cache-Control' => 'max-age=10000' do |req,res|
130
131
  count+= 1
@@ -141,14 +142,16 @@ describe 'Rack::Cache::Context' do
141
142
  response.body.should.equal 'Hello World'
142
143
  cache.trace.should.include :fresh
143
144
 
144
- get '/', 'HTTP_CACHE_CONTROL' => 'no-cache'
145
+ get '/',
146
+ 'rack-cache.allow_reload' => true,
147
+ 'HTTP_CACHE_CONTROL' => 'no-cache'
145
148
  response.should.be.ok
146
149
  response.body.should.equal 'Goodbye World'
147
150
  cache.trace.should.include :reload
148
151
  cache.trace.should.include :store
149
152
  end
150
153
 
151
- it 'does not reload responses when allow_reload is set false' do
154
+ it 'does not reload responses when allow_reload is set false (default)' do
152
155
  count = 0
153
156
  respond_with 200, 'Cache-Control' => 'max-age=10000' do |req,res|
154
157
  count+= 1
@@ -171,9 +174,17 @@ describe 'Rack::Cache::Context' do
171
174
  response.should.be.ok
172
175
  response.body.should.equal 'Hello World'
173
176
  cache.trace.should.not.include :reload
177
+
178
+ # test again without explicitly setting the allow_reload option to false
179
+ get '/',
180
+ 'HTTP_CACHE_CONTROL' => 'no-cache'
181
+ response.should.be.ok
182
+ response.body.should.equal 'Hello World'
183
+ cache.trace.should.not.include :reload
174
184
  end
175
185
 
176
- it 'revalidates fresh cache entry when max-age request directive is exceeded' do
186
+ it 'revalidates fresh cache entry when max-age request directive is exceeded ' +
187
+ 'when allow_revalidate option is set true' do
177
188
  count = 0
178
189
  respond_with do |req,res|
179
190
  count+= 1
@@ -192,7 +203,9 @@ describe 'Rack::Cache::Context' do
192
203
  response.body.should.equal 'Hello World'
193
204
  cache.trace.should.include :fresh
194
205
 
195
- get '/', 'HTTP_CACHE_CONTROL' => 'max-age=0'
206
+ get '/',
207
+ 'rack-cache.allow_revalidate' => true,
208
+ 'HTTP_CACHE_CONTROL' => 'max-age=0'
196
209
  response.should.be.ok
197
210
  response.body.should.equal 'Goodbye World'
198
211
  cache.trace.should.include :stale
@@ -200,7 +213,7 @@ describe 'Rack::Cache::Context' do
200
213
  cache.trace.should.include :store
201
214
  end
202
215
 
203
- it 'does not revalidate fresh cache entry when enable_revalidate option is set false' do
216
+ it 'does not revalidate fresh cache entry when enable_revalidate option is set false (default)' do
204
217
  count = 0
205
218
  respond_with do |req,res|
206
219
  count+= 1
@@ -227,6 +240,15 @@ describe 'Rack::Cache::Context' do
227
240
  cache.trace.should.not.include :stale
228
241
  cache.trace.should.not.include :invalid
229
242
  cache.trace.should.include :fresh
243
+
244
+ # test again without explicitly setting the allow_revalidate option to false
245
+ get '/',
246
+ 'HTTP_CACHE_CONTROL' => 'max-age=0'
247
+ response.should.be.ok
248
+ response.body.should.equal 'Hello World'
249
+ cache.trace.should.not.include :stale
250
+ cache.trace.should.not.include :invalid
251
+ cache.trace.should.include :fresh
230
252
  end
231
253
  it 'fetches response from backend when cache misses' do
232
254
  respond_with 200, 'Expires' => (Time.now + 5).httpdate
@@ -176,10 +176,51 @@ describe 'Rack::Cache::EntityStore' do
176
176
  end
177
177
 
178
178
  need_memcached 'entity store tests' do
179
+ describe 'MemCached' do
180
+ it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
181
+ before do
182
+ @store = Rack::Cache::EntityStore::MemCached.new($memcached)
183
+ end
184
+ after do
185
+ @store = nil
186
+ end
187
+ end
188
+ end
189
+
190
+
191
+ need_memcache 'entity store tests' do
179
192
  describe 'MemCache' do
180
193
  it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
181
194
  before do
182
- @store = Rack::Cache::EntityStore::MemCache.new($memcached)
195
+ $memcache.flush_all
196
+ @store = Rack::Cache::EntityStore::MemCache.new($memcache)
197
+ end
198
+ after do
199
+ @store = nil
200
+ end
201
+ end
202
+ end
203
+
204
+ need_java 'entity store testing' do
205
+ module Rack::Cache::AppEngine
206
+ module MC
207
+ class << (Service = {})
208
+
209
+ def contains(key); include?(key); end
210
+ def get(key); self[key]; end;
211
+ def put(key, value, ttl = nil)
212
+ self[key] = value
213
+ end
214
+ end
215
+
216
+ end
217
+ end
218
+
219
+ describe 'GAEStore' do
220
+ it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
221
+ before do
222
+ puts Rack::Cache::AppEngine::MC::Service.inspect
223
+ @store = Rack::Cache::EntityStore::GAEStore.new
183
224
  end
184
225
  after do
185
226
  @store = nil
@@ -250,14 +250,53 @@ describe 'Rack::Cache::MetaStore' do
250
250
  end
251
251
 
252
252
  need_memcached 'metastore tests' do
253
- describe 'MemCache' do
253
+ describe 'MemCached' do
254
254
  it_should_behave_like 'A Rack::Cache::MetaStore Implementation'
255
255
  before :each do
256
256
  @temp_dir = create_temp_directory
257
257
  $memcached.flush
258
- @store = Rack::Cache::MetaStore::MemCache.new($memcached)
258
+ @store = Rack::Cache::MetaStore::MemCached.new($memcached)
259
259
  @entity_store = Rack::Cache::EntityStore::Heap.new
260
260
  end
261
261
  end
262
262
  end
263
+
264
+ need_memcache 'metastore tests' do
265
+ describe 'MemCache' do
266
+ it_should_behave_like 'A Rack::Cache::MetaStore Implementation'
267
+ before :each do
268
+ @temp_dir = create_temp_directory
269
+ $memcache.flush_all
270
+ @store = Rack::Cache::MetaStore::MemCache.new($memcache)
271
+ @entity_store = Rack::Cache::EntityStore::Heap.new
272
+ end
273
+ end
274
+ end
275
+
276
+ need_java 'entity store testing' do
277
+ module Rack::Cache::AppEngine
278
+ module MC
279
+ class << (Service = {})
280
+
281
+ def contains(key); include?(key); end
282
+ def get(key); self[key]; end;
283
+ def put(key, value, ttl = nil)
284
+ self[key] = value
285
+ end
286
+
287
+ end
288
+ end
289
+ end
290
+
291
+ describe 'GAEStore' do
292
+ it_should_behave_like 'A Rack::Cache::MetaStore Implementation'
293
+ before :each do
294
+ Rack::Cache::AppEngine::MC::Service.clear
295
+ @store = Rack::Cache::MetaStore::GAEStore.new
296
+ @entity_store = Rack::Cache::EntityStore::Heap.new
297
+ end
298
+ end
299
+
300
+ end
301
+
263
302
  end
data/test/spec_setup.rb CHANGED
@@ -15,6 +15,7 @@ end
15
15
  # of the MemCached meta and entity stores.
16
16
  ENV['MEMCACHED'] ||= 'localhost:11215'
17
17
  $memcached = nil
18
+ $memcache = nil
18
19
 
19
20
  def have_memcached?(server=ENV['MEMCACHED'])
20
21
  return $memcached unless $memcached.nil?
@@ -35,14 +36,49 @@ end
35
36
 
36
37
  have_memcached?
37
38
 
39
+ def have_memcache?(server=ENV['MEMCACHED'])
40
+ return $memcache unless $memcache.nil?
41
+ require 'memcache'
42
+ $memcache = MemCache.new(server)
43
+ $memcache.set('ping', '')
44
+ true
45
+ rescue LoadError => boom
46
+ $memcache = false
47
+ false
48
+ rescue => boom
49
+ STDERR.puts "memcache not working. related tests will be skipped."
50
+ $memcache = false
51
+ false
52
+ end
53
+
54
+ have_memcache?
55
+
38
56
  def need_memcached(forwhat)
39
57
  if have_memcached?
40
58
  yield
41
59
  else
42
- STDERR.puts "skipping memcached #{forwhat} (MEMCACHED environment variable not set)"
60
+ STDERR.puts "skipping memcached #{forwhat}"
43
61
  end
44
62
  end
45
63
 
64
+ def need_memcache(forwhat)
65
+ if have_memcache?
66
+ yield
67
+ else
68
+ STDERR.puts "skipping memcache #{forwhat}"
69
+ end
70
+ end
71
+
72
+ def need_java(forwhat)
73
+
74
+ if RUBY_PLATFORM =~ /java/
75
+ yield
76
+ else
77
+ STDERR.puts "skipping app engine #{forwhat}"
78
+ end
79
+ end
80
+
81
+
46
82
  # Setup the load path ..
47
83
  $LOAD_PATH.unshift File.dirname(File.dirname(__FILE__)) + '/lib'
48
84
  $LOAD_PATH.unshift File.dirname(__FILE__)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-cache
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.4"
4
+ version: "0.5"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Tomayko
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-16 00:00:00 -07:00
12
+ date: 2009-05-25 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -18,7 +18,7 @@ dependencies:
18
18
  version_requirement:
19
19
  version_requirements: !ruby/object:Gem::Requirement
20
20
  requirements:
21
- - - ~>
21
+ - - ">="
22
22
  - !ruby/object:Gem::Version
23
23
  version: "0.4"
24
24
  version:
@@ -50,6 +50,7 @@ files:
50
50
  - example/sinatra/app.rb
51
51
  - example/sinatra/views/index.erb
52
52
  - lib/rack/cache.rb
53
+ - lib/rack/cache/appengine.rb
53
54
  - lib/rack/cache/cachecontrol.rb
54
55
  - lib/rack/cache/context.rb
55
56
  - lib/rack/cache/entitystore.rb