rtomayko-rack-cache 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
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: rtomayko-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