rack-cache 1.5.1 → 1.6.1

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.
@@ -0,0 +1,429 @@
1
+ require 'fileutils'
2
+ require 'digest/sha1'
3
+ require 'rack/utils'
4
+ require 'rack/cache/key'
5
+
6
+ module Rack::Cache
7
+
8
+ # The MetaStore is responsible for storing meta information about a
9
+ # request/response pair keyed by the request's URL.
10
+ #
11
+ # The meta store keeps a list of request/response pairs for each canonical
12
+ # request URL. A request/response pair is a two element Array of the form:
13
+ # [request, response]
14
+ #
15
+ # The +request+ element is a Hash of Rack environment keys. Only protocol
16
+ # keys (i.e., those that start with "HTTP_") are stored. The +response+
17
+ # element is a Hash of cached HTTP response headers for the paired request.
18
+ #
19
+ # The MetaStore class is abstract and should not be instanstiated
20
+ # directly. Concrete subclasses should implement the protected #read,
21
+ # #write, and #purge methods. Care has been taken to keep these low-level
22
+ # methods dumb and straight-forward to implement.
23
+ class MetaStore
24
+
25
+ # Locate a cached response for the request provided. Returns a
26
+ # Rack::Cache::Response object if the cache hits or nil if no cache entry
27
+ # was found.
28
+ def lookup(request, entity_store)
29
+ key = cache_key(request)
30
+ entries = read(key)
31
+
32
+ # bail out if we have nothing cached
33
+ return nil if entries.empty?
34
+
35
+ # find a cached entry that matches the request.
36
+ env = request.env
37
+ match = entries.detect{|req,res| requests_match?(res['Vary'], env, req)}
38
+ return nil if match.nil?
39
+
40
+ _, res = match
41
+ if body = entity_store.open(res['X-Content-Digest'])
42
+ restore_response(res, body)
43
+ else
44
+ # TODO the metastore referenced an entity that doesn't exist in
45
+ # the entitystore. we definitely want to return nil but we should
46
+ # also purge the entry from the meta-store when this is detected.
47
+ end
48
+ end
49
+
50
+ # Write a cache entry to the store under the given key. Existing
51
+ # entries are read and any that match the response are removed.
52
+ # This method calls #write with the new list of cache entries.
53
+ def store(request, response, entity_store)
54
+ key = cache_key(request)
55
+ stored_env = persist_request(request)
56
+
57
+ # write the response body to the entity store if this is the
58
+ # original response.
59
+ if response.headers['X-Content-Digest'].nil?
60
+ if request.env['rack-cache.use_native_ttl'] && response.fresh?
61
+ digest, size = entity_store.write(response.body, response.ttl)
62
+ else
63
+ digest, size = entity_store.write(response.body)
64
+ end
65
+ response.headers['X-Content-Digest'] = digest
66
+ response.headers['Content-Length'] = size.to_s unless response.headers['Transfer-Encoding']
67
+
68
+ # If the entitystore backend is a Noop, do not try to read the body from the backend, it always returns an empty array
69
+ unless entity_store.is_a? Rack::Cache::EntityStore::Noop
70
+ # A stream body can only be read once and is currently closed by #write.
71
+ # (To avoid having to keep giant objects in memory when writing to disk cache
72
+ # the body is never converted to a single string)
73
+ # We cannot always reply on body to be re-readable,
74
+ # so we have to read it from the cache.
75
+ # BUG: if the cache was unable to store a stream, the stream will be closed
76
+ # and rack will try to read it again, resulting in hard to track down exception
77
+ response.body = entity_store.open(digest) || response.body
78
+ end
79
+ end
80
+
81
+ # read existing cache entries, remove non-varying, and add this one to
82
+ # the list
83
+ vary = response.vary
84
+ entries =
85
+ read(key).reject do |env,res|
86
+ (vary == res['Vary']) &&
87
+ requests_match?(vary, env, stored_env)
88
+ end
89
+
90
+ headers = persist_response(response)
91
+ headers.delete 'Age'
92
+
93
+ entries.unshift [stored_env, headers]
94
+ write key, entries
95
+ key
96
+ end
97
+
98
+ # Generate a cache key for the request.
99
+ def cache_key(request)
100
+ keygen = request.env['rack-cache.cache_key'] || Key
101
+ keygen.call(request)
102
+ end
103
+
104
+ # Invalidate all cache entries that match the request.
105
+ def invalidate(request, entity_store)
106
+ modified = false
107
+ key = cache_key(request)
108
+ entries =
109
+ read(key).map do |req, res|
110
+ response = restore_response(res)
111
+ if response.fresh?
112
+ response.expire!
113
+ modified = true
114
+ [req, persist_response(response)]
115
+ else
116
+ [req, res]
117
+ end
118
+ end
119
+ write key, entries if modified
120
+ end
121
+
122
+ private
123
+
124
+ # Extract the environment Hash from +request+ while making any
125
+ # necessary modifications in preparation for persistence. The Hash
126
+ # returned must be marshalable.
127
+ def persist_request(request)
128
+ env = request.env.dup
129
+ env.reject! { |key,val| key =~ /[^0-9A-Z_]/ || !val.respond_to?(:to_str) }
130
+ env
131
+ end
132
+
133
+ # Converts a stored response hash into a Response object. The caller
134
+ # is responsible for loading and passing the body if needed.
135
+ def restore_response(hash, body=[])
136
+ status = hash.delete('X-Status').to_i
137
+ Rack::Cache::Response.new(status, hash, body)
138
+ end
139
+
140
+ def persist_response(response)
141
+ hash = response.headers.to_hash
142
+ hash['X-Status'] = response.status.to_s
143
+ hash
144
+ end
145
+
146
+ # Determine whether the two environment hashes are non-varying based on
147
+ # the vary response header value provided.
148
+ def requests_match?(vary, env1, env2)
149
+ return true if vary.nil? || vary == ''
150
+ vary.split(/[\s,]+/).all? do |header|
151
+ key = "HTTP_#{header.upcase.tr('-', '_')}"
152
+ env1[key] == env2[key]
153
+ end
154
+ end
155
+
156
+ protected
157
+ # Locate all cached request/response pairs that match the specified
158
+ # URL key. The result must be an Array of all cached request/response
159
+ # pairs. An empty Array must be returned if nothing is cached for
160
+ # the specified key.
161
+ def read(key)
162
+ raise NotImplementedError
163
+ end
164
+
165
+ # Store an Array of request/response pairs for the given key. Concrete
166
+ # implementations should not attempt to filter or concatenate the
167
+ # list in any way.
168
+ def write(key, negotiations)
169
+ raise NotImplementedError
170
+ end
171
+
172
+ # Remove all cached entries at the key specified. No error is raised
173
+ # when the key does not exist.
174
+ def purge(key)
175
+ raise NotImplementedError
176
+ end
177
+
178
+ private
179
+ # Generate a SHA1 hex digest for the specified string. This is a
180
+ # simple utility method for meta store implementations.
181
+ def hexdigest(data)
182
+ Digest::SHA1.hexdigest(data)
183
+ end
184
+
185
+ public
186
+ # Concrete MetaStore implementation that uses a simple Hash to store
187
+ # request/response pairs on the heap.
188
+ class Heap < MetaStore
189
+ def initialize(hash={})
190
+ @hash = hash
191
+ end
192
+
193
+ def read(key)
194
+ if data = @hash[key]
195
+ Marshal.load(data)
196
+ else
197
+ []
198
+ end
199
+ end
200
+
201
+ def write(key, entries)
202
+ @hash[key] = Marshal.dump(entries)
203
+ end
204
+
205
+ def purge(key)
206
+ @hash.delete(key)
207
+ nil
208
+ end
209
+
210
+ def to_hash
211
+ @hash
212
+ end
213
+
214
+ def self.resolve(uri)
215
+ new
216
+ end
217
+ end
218
+
219
+ HEAP = Heap
220
+ MEM = HEAP
221
+
222
+ # Concrete MetaStore implementation that stores request/response
223
+ # pairs on disk.
224
+ class Disk < MetaStore
225
+ attr_reader :root
226
+
227
+ def initialize(root="/tmp/rack-cache/meta-#{ARGV[0]}")
228
+ @root = File.expand_path(root)
229
+ FileUtils.mkdir_p(root, :mode => 0755)
230
+ end
231
+
232
+ def read(key)
233
+ path = key_path(key)
234
+ File.open(path, 'rb') { |io| Marshal.load(io) }
235
+ rescue Errno::ENOENT, IOError
236
+ []
237
+ end
238
+
239
+ def write(key, entries)
240
+ tries = 0
241
+ begin
242
+ path = key_path(key)
243
+ File.open(path, 'wb') { |io| Marshal.dump(entries, io, -1) }
244
+ rescue Errno::ENOENT, IOError
245
+ Dir.mkdir(File.dirname(path), 0755)
246
+ retry if (tries += 1) == 1
247
+ end
248
+ end
249
+
250
+ def purge(key)
251
+ path = key_path(key)
252
+ File.unlink(path)
253
+ nil
254
+ rescue Errno::ENOENT, IOError
255
+ nil
256
+ end
257
+
258
+ private
259
+ def key_path(key)
260
+ File.join(root, spread(hexdigest(key)))
261
+ end
262
+
263
+ def spread(sha, n=2)
264
+ sha = sha.dup
265
+ sha[n,0] = '/'
266
+ sha
267
+ end
268
+
269
+ public
270
+ def self.resolve(uri)
271
+ path = File.expand_path(uri.opaque || uri.path)
272
+ new path
273
+ end
274
+
275
+ end
276
+
277
+ DISK = Disk
278
+ FILE = Disk
279
+
280
+ # Stores request/response pairs in memcached. Keys are not stored
281
+ # directly since memcached has a 250-byte limit on key names. Instead,
282
+ # the SHA1 hexdigest of the key is used.
283
+ class MemCacheBase < MetaStore
284
+ extend Rack::Utils
285
+
286
+ # The MemCache object used to communicated with the memcached
287
+ # daemon.
288
+ attr_reader :cache
289
+
290
+ # Create MemCache store for the given URI. The URI must specify
291
+ # a host and may specify a port, namespace, and options:
292
+ #
293
+ # memcached://example.com:11211/namespace?opt1=val1&opt2=val2
294
+ #
295
+ # Query parameter names and values are documented with the memcached
296
+ # library: http://tinyurl.com/4upqnd
297
+ def self.resolve(uri)
298
+ if uri.respond_to?(:scheme)
299
+ server = "#{uri.host}:#{uri.port || '11211'}"
300
+ options = parse_query(uri.query)
301
+ options.keys.each do |key|
302
+ value =
303
+ case value = options.delete(key)
304
+ when 'true' ; true
305
+ when 'false' ; false
306
+ else value.to_sym
307
+ end
308
+ options[key.to_sym] = value
309
+ end
310
+
311
+ options[:namespace] = uri.path.to_s.sub(/^\//, '')
312
+
313
+ new server, options
314
+ else
315
+ # if the object provided is not a URI, pass it straight through
316
+ # to the underlying implementation.
317
+ new uri
318
+ end
319
+ end
320
+ end
321
+
322
+ class Dalli < MemCacheBase
323
+ def initialize(server="localhost:11211", options={})
324
+ @cache =
325
+ if server.respond_to?(:stats)
326
+ server
327
+ else
328
+ require 'dalli'
329
+ ::Dalli::Client.new(server, options)
330
+ end
331
+ end
332
+
333
+ def read(key)
334
+ key = hexdigest(key)
335
+ cache.get(key) || []
336
+ end
337
+
338
+ def write(key, entries)
339
+ key = hexdigest(key)
340
+ cache.set(key, entries)
341
+ end
342
+
343
+ def purge(key)
344
+ cache.delete(hexdigest(key))
345
+ nil
346
+ end
347
+ end
348
+
349
+ class MemCached < MemCacheBase
350
+ # The Memcached instance used to communicated with the memcached
351
+ # daemon.
352
+ attr_reader :cache
353
+
354
+ def initialize(server="localhost:11211", options={})
355
+ options[:prefix_key] ||= options.delete(:namespace) if options.key?(:namespace)
356
+ @cache =
357
+ if server.respond_to?(:stats)
358
+ server
359
+ else
360
+ require 'memcached'
361
+ Memcached.new(server, options)
362
+ end
363
+ end
364
+
365
+ def read(key)
366
+ key = hexdigest(key)
367
+ cache.get(key)
368
+ rescue Memcached::NotFound
369
+ []
370
+ end
371
+
372
+ def write(key, entries)
373
+ key = hexdigest(key)
374
+ cache.set(key, entries)
375
+ end
376
+
377
+ def purge(key)
378
+ key = hexdigest(key)
379
+ cache.delete(key)
380
+ nil
381
+ rescue Memcached::NotFound
382
+ nil
383
+ end
384
+ end
385
+
386
+ MEMCACHE =
387
+ if defined?(::Memcached)
388
+ MemCached
389
+ else
390
+ Dalli
391
+ end
392
+ MEMCACHED = MEMCACHE
393
+
394
+ class GAEStore < MetaStore
395
+ attr_reader :cache
396
+
397
+ def initialize(options = {})
398
+ require 'rack/cache/app_engine'
399
+ @cache = Rack::Cache::AppEngine::MemCache.new(options)
400
+ end
401
+
402
+ def read(key)
403
+ key = hexdigest(key)
404
+ cache.get(key) || []
405
+ end
406
+
407
+ def write(key, entries)
408
+ key = hexdigest(key)
409
+ cache.put(key, entries)
410
+ end
411
+
412
+ def purge(key)
413
+ key = hexdigest(key)
414
+ cache.delete(key)
415
+ nil
416
+ end
417
+
418
+ def self.resolve(uri)
419
+ self.new(:namespace => uri.host)
420
+ end
421
+
422
+ end
423
+
424
+ GAECACHE = GAEStore
425
+ GAE = GAEStore
426
+
427
+ end
428
+
429
+ end
@@ -1,418 +1,2 @@
1
- require 'fileutils'
2
- require 'digest/sha1'
3
- require 'rack/utils'
4
- require 'rack/cache/key'
5
-
6
- module Rack::Cache
7
-
8
- # The MetaStore is responsible for storing meta information about a
9
- # request/response pair keyed by the request's URL.
10
- #
11
- # The meta store keeps a list of request/response pairs for each canonical
12
- # request URL. A request/response pair is a two element Array of the form:
13
- # [request, response]
14
- #
15
- # The +request+ element is a Hash of Rack environment keys. Only protocol
16
- # keys (i.e., those that start with "HTTP_") are stored. The +response+
17
- # element is a Hash of cached HTTP response headers for the paired request.
18
- #
19
- # The MetaStore class is abstract and should not be instanstiated
20
- # directly. Concrete subclasses should implement the protected #read,
21
- # #write, and #purge methods. Care has been taken to keep these low-level
22
- # methods dumb and straight-forward to implement.
23
- class MetaStore
24
-
25
- # Locate a cached response for the request provided. Returns a
26
- # Rack::Cache::Response object if the cache hits or nil if no cache entry
27
- # was found.
28
- def lookup(request, entity_store)
29
- key = cache_key(request)
30
- entries = read(key)
31
-
32
- # bail out if we have nothing cached
33
- return nil if entries.empty?
34
-
35
- # find a cached entry that matches the request.
36
- env = request.env
37
- match = entries.detect{|req,res| requests_match?(res['Vary'], env, req)}
38
- return nil if match.nil?
39
-
40
- _, res = match
41
- if body = entity_store.open(res['X-Content-Digest'])
42
- restore_response(res, body)
43
- else
44
- # TODO the metastore referenced an entity that doesn't exist in
45
- # the entitystore. we definitely want to return nil but we should
46
- # also purge the entry from the meta-store when this is detected.
47
- end
48
- end
49
-
50
- # Write a cache entry to the store under the given key. Existing
51
- # entries are read and any that match the response are removed.
52
- # This method calls #write with the new list of cache entries.
53
- def store(request, response, entity_store)
54
- key = cache_key(request)
55
- stored_env = persist_request(request)
56
-
57
- # write the response body to the entity store if this is the
58
- # original response.
59
- if response.headers['X-Content-Digest'].nil?
60
- if request.env['rack-cache.use_native_ttl'] && response.fresh?
61
- digest, size = entity_store.write(response.body, response.ttl)
62
- else
63
- digest, size = entity_store.write(response.body)
64
- end
65
- response.headers['X-Content-Digest'] = digest
66
- response.headers['Content-Length'] = size.to_s unless response.headers['Transfer-Encoding']
67
- response.body = entity_store.open(digest) || response.body
68
- end
69
-
70
- # read existing cache entries, remove non-varying, and add this one to
71
- # the list
72
- vary = response.vary
73
- entries =
74
- read(key).reject do |env,res|
75
- (vary == res['Vary']) &&
76
- requests_match?(vary, env, stored_env)
77
- end
78
-
79
- headers = persist_response(response)
80
- headers.delete 'Age'
81
-
82
- entries.unshift [stored_env, headers]
83
- write key, entries
84
- key
85
- end
86
-
87
- # Generate a cache key for the request.
88
- def cache_key(request)
89
- keygen = request.env['rack-cache.cache_key'] || Key
90
- keygen.call(request)
91
- end
92
-
93
- # Invalidate all cache entries that match the request.
94
- def invalidate(request, entity_store)
95
- modified = false
96
- key = cache_key(request)
97
- entries =
98
- read(key).map do |req, res|
99
- response = restore_response(res)
100
- if response.fresh?
101
- response.expire!
102
- modified = true
103
- [req, persist_response(response)]
104
- else
105
- [req, res]
106
- end
107
- end
108
- write key, entries if modified
109
- end
110
-
111
- private
112
-
113
- # Extract the environment Hash from +request+ while making any
114
- # necessary modifications in preparation for persistence. The Hash
115
- # returned must be marshalable.
116
- def persist_request(request)
117
- env = request.env.dup
118
- env.reject! { |key,val| key =~ /[^0-9A-Z_]/ || !val.respond_to?(:to_str) }
119
- env
120
- end
121
-
122
- # Converts a stored response hash into a Response object. The caller
123
- # is responsible for loading and passing the body if needed.
124
- def restore_response(hash, body=nil)
125
- status = hash.delete('X-Status').to_i
126
- Rack::Cache::Response.new(status, hash, body)
127
- end
128
-
129
- def persist_response(response)
130
- hash = response.headers.to_hash
131
- hash['X-Status'] = response.status.to_s
132
- hash
133
- end
134
-
135
- # Determine whether the two environment hashes are non-varying based on
136
- # the vary response header value provided.
137
- def requests_match?(vary, env1, env2)
138
- return true if vary.nil? || vary == ''
139
- vary.split(/[\s,]+/).all? do |header|
140
- key = "HTTP_#{header.upcase.tr('-', '_')}"
141
- env1[key] == env2[key]
142
- end
143
- end
144
-
145
- protected
146
- # Locate all cached request/response pairs that match the specified
147
- # URL key. The result must be an Array of all cached request/response
148
- # pairs. An empty Array must be returned if nothing is cached for
149
- # the specified key.
150
- def read(key)
151
- raise NotImplementedError
152
- end
153
-
154
- # Store an Array of request/response pairs for the given key. Concrete
155
- # implementations should not attempt to filter or concatenate the
156
- # list in any way.
157
- def write(key, negotiations)
158
- raise NotImplementedError
159
- end
160
-
161
- # Remove all cached entries at the key specified. No error is raised
162
- # when the key does not exist.
163
- def purge(key)
164
- raise NotImplementedError
165
- end
166
-
167
- private
168
- # Generate a SHA1 hex digest for the specified string. This is a
169
- # simple utility method for meta store implementations.
170
- def hexdigest(data)
171
- Digest::SHA1.hexdigest(data)
172
- end
173
-
174
- public
175
- # Concrete MetaStore implementation that uses a simple Hash to store
176
- # request/response pairs on the heap.
177
- class Heap < MetaStore
178
- def initialize(hash={})
179
- @hash = hash
180
- end
181
-
182
- def read(key)
183
- if data = @hash[key]
184
- Marshal.load(data)
185
- else
186
- []
187
- end
188
- end
189
-
190
- def write(key, entries)
191
- @hash[key] = Marshal.dump(entries)
192
- end
193
-
194
- def purge(key)
195
- @hash.delete(key)
196
- nil
197
- end
198
-
199
- def to_hash
200
- @hash
201
- end
202
-
203
- def self.resolve(uri)
204
- new
205
- end
206
- end
207
-
208
- HEAP = Heap
209
- MEM = HEAP
210
-
211
- # Concrete MetaStore implementation that stores request/response
212
- # pairs on disk.
213
- class Disk < MetaStore
214
- attr_reader :root
215
-
216
- def initialize(root="/tmp/rack-cache/meta-#{ARGV[0]}")
217
- @root = File.expand_path(root)
218
- FileUtils.mkdir_p(root, :mode => 0755)
219
- end
220
-
221
- def read(key)
222
- path = key_path(key)
223
- File.open(path, 'rb') { |io| Marshal.load(io) }
224
- rescue Errno::ENOENT, IOError
225
- []
226
- end
227
-
228
- def write(key, entries)
229
- tries = 0
230
- begin
231
- path = key_path(key)
232
- File.open(path, 'wb') { |io| Marshal.dump(entries, io, -1) }
233
- rescue Errno::ENOENT, IOError
234
- Dir.mkdir(File.dirname(path), 0755)
235
- retry if (tries += 1) == 1
236
- end
237
- end
238
-
239
- def purge(key)
240
- path = key_path(key)
241
- File.unlink(path)
242
- nil
243
- rescue Errno::ENOENT, IOError
244
- nil
245
- end
246
-
247
- private
248
- def key_path(key)
249
- File.join(root, spread(hexdigest(key)))
250
- end
251
-
252
- def spread(sha, n=2)
253
- sha = sha.dup
254
- sha[n,0] = '/'
255
- sha
256
- end
257
-
258
- public
259
- def self.resolve(uri)
260
- path = File.expand_path(uri.opaque || uri.path)
261
- new path
262
- end
263
-
264
- end
265
-
266
- DISK = Disk
267
- FILE = Disk
268
-
269
- # Stores request/response pairs in memcached. Keys are not stored
270
- # directly since memcached has a 250-byte limit on key names. Instead,
271
- # the SHA1 hexdigest of the key is used.
272
- class MemCacheBase < MetaStore
273
- extend Rack::Utils
274
-
275
- # The MemCache object used to communicated with the memcached
276
- # daemon.
277
- attr_reader :cache
278
-
279
- # Create MemCache store for the given URI. The URI must specify
280
- # a host and may specify a port, namespace, and options:
281
- #
282
- # memcached://example.com:11211/namespace?opt1=val1&opt2=val2
283
- #
284
- # Query parameter names and values are documented with the memcached
285
- # library: http://tinyurl.com/4upqnd
286
- def self.resolve(uri)
287
- if uri.respond_to?(:scheme)
288
- server = "#{uri.host}:#{uri.port || '11211'}"
289
- options = parse_query(uri.query)
290
- options.keys.each do |key|
291
- value =
292
- case value = options.delete(key)
293
- when 'true' ; true
294
- when 'false' ; false
295
- else value.to_sym
296
- end
297
- options[key.to_sym] = value
298
- end
299
-
300
- options[:namespace] = uri.path.to_s.sub(/^\//, '')
301
-
302
- new server, options
303
- else
304
- # if the object provided is not a URI, pass it straight through
305
- # to the underlying implementation.
306
- new uri
307
- end
308
- end
309
- end
310
-
311
- class Dalli < MemCacheBase
312
- def initialize(server="localhost:11211", options={})
313
- @cache =
314
- if server.respond_to?(:stats)
315
- server
316
- else
317
- require 'dalli'
318
- ::Dalli::Client.new(server, options)
319
- end
320
- end
321
-
322
- def read(key)
323
- key = hexdigest(key)
324
- cache.get(key) || []
325
- end
326
-
327
- def write(key, entries)
328
- key = hexdigest(key)
329
- cache.set(key, entries)
330
- end
331
-
332
- def purge(key)
333
- cache.delete(hexdigest(key))
334
- nil
335
- end
336
- end
337
-
338
- class MemCached < MemCacheBase
339
- # The Memcached instance used to communicated with the memcached
340
- # daemon.
341
- attr_reader :cache
342
-
343
- def initialize(server="localhost:11211", options={})
344
- options[:prefix_key] ||= options.delete(:namespace) if options.key?(:namespace)
345
- @cache =
346
- if server.respond_to?(:stats)
347
- server
348
- else
349
- require 'memcached'
350
- Memcached.new(server, options)
351
- end
352
- end
353
-
354
- def read(key)
355
- key = hexdigest(key)
356
- cache.get(key)
357
- rescue Memcached::NotFound
358
- []
359
- end
360
-
361
- def write(key, entries)
362
- key = hexdigest(key)
363
- cache.set(key, entries)
364
- end
365
-
366
- def purge(key)
367
- key = hexdigest(key)
368
- cache.delete(key)
369
- nil
370
- rescue Memcached::NotFound
371
- nil
372
- end
373
- end
374
-
375
- MEMCACHE =
376
- if defined?(::Memcached)
377
- MemCached
378
- else
379
- Dalli
380
- end
381
- MEMCACHED = MEMCACHE
382
-
383
- class GAEStore < MetaStore
384
- attr_reader :cache
385
-
386
- def initialize(options = {})
387
- require 'rack/cache/appengine'
388
- @cache = Rack::Cache::AppEngine::MemCache.new(options)
389
- end
390
-
391
- def read(key)
392
- key = hexdigest(key)
393
- cache.get(key) || []
394
- end
395
-
396
- def write(key, entries)
397
- key = hexdigest(key)
398
- cache.put(key, entries)
399
- end
400
-
401
- def purge(key)
402
- key = hexdigest(key)
403
- cache.delete(key)
404
- nil
405
- end
406
-
407
- def self.resolve(uri)
408
- self.new(:namespace => uri.host)
409
- end
410
-
411
- end
412
-
413
- GAECACHE = GAEStore
414
- GAE = GAEStore
415
-
416
- end
417
-
418
- end
1
+ warn "use require 'rack/cache/meta_store'"
2
+ require 'rack/cache/meta_store'