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.
- checksums.yaml +4 -4
- data/CHANGES +10 -0
- data/README.md +28 -9
- data/lib/rack/cache.rb +1 -1
- data/lib/rack/cache/app_engine.rb +48 -0
- data/lib/rack/cache/appengine.rb +2 -52
- data/lib/rack/cache/cache_control.rb +209 -0
- data/lib/rack/cache/cachecontrol.rb +2 -208
- data/lib/rack/cache/entity_store.rb +377 -0
- data/lib/rack/cache/entitystore.rb +2 -341
- data/lib/rack/cache/meta_store.rb +429 -0
- data/lib/rack/cache/metastore.rb +2 -418
- data/lib/rack/cache/request.rb +1 -1
- data/lib/rack/cache/response.rb +1 -1
- data/lib/rack/cache/storage.rb +4 -2
- metadata +6 -2
@@ -0,0 +1,377 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module Rack::Cache
|
4
|
+
|
5
|
+
# Entity stores are used to cache response bodies across requests. All
|
6
|
+
# Implementations are required to calculate a SHA checksum of the data written
|
7
|
+
# which becomes the response body's key.
|
8
|
+
class EntityStore
|
9
|
+
|
10
|
+
# Read body calculating the SHA1 checksum and size while
|
11
|
+
# yielding each chunk to the block. If the body responds to close,
|
12
|
+
# call it after iteration is complete. Return a two-tuple of the form:
|
13
|
+
# [ hexdigest, size ].
|
14
|
+
def slurp(body)
|
15
|
+
digest, size = Digest::SHA1.new, 0
|
16
|
+
body.each do |part|
|
17
|
+
size += bytesize(part)
|
18
|
+
digest << part
|
19
|
+
yield part
|
20
|
+
end
|
21
|
+
body.close if body.respond_to? :close
|
22
|
+
[digest.hexdigest, size]
|
23
|
+
end
|
24
|
+
|
25
|
+
if ''.respond_to?(:bytesize)
|
26
|
+
def bytesize(string); string.bytesize; end
|
27
|
+
else
|
28
|
+
def bytesize(string); string.size; end
|
29
|
+
end
|
30
|
+
|
31
|
+
private :slurp, :bytesize
|
32
|
+
|
33
|
+
|
34
|
+
# Stores entity bodies on the heap using a Hash object.
|
35
|
+
class Heap < EntityStore
|
36
|
+
|
37
|
+
# Create the store with the specified backing Hash.
|
38
|
+
def initialize(hash={})
|
39
|
+
@hash = hash
|
40
|
+
end
|
41
|
+
|
42
|
+
# Determine whether the response body with the specified key (SHA1)
|
43
|
+
# exists in the store.
|
44
|
+
def exist?(key)
|
45
|
+
@hash.include?(key)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return an object suitable for use as a Rack response body for the
|
49
|
+
# specified key.
|
50
|
+
def open(key)
|
51
|
+
(body = @hash[key]) && body.dup
|
52
|
+
end
|
53
|
+
|
54
|
+
# Read all data associated with the given key and return as a single
|
55
|
+
# String.
|
56
|
+
def read(key)
|
57
|
+
(body = @hash[key]) && body.join
|
58
|
+
end
|
59
|
+
|
60
|
+
# Write the Rack response body immediately and return the SHA1 key.
|
61
|
+
def write(body, ttl=nil)
|
62
|
+
buf = []
|
63
|
+
key, size = slurp(body) { |part| buf << part }
|
64
|
+
@hash[key] = buf
|
65
|
+
[key, size]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Remove the body corresponding to key; return nil.
|
69
|
+
def purge(key)
|
70
|
+
@hash.delete(key)
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.resolve(uri)
|
75
|
+
new
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
HEAP = Heap
|
80
|
+
MEM = Heap
|
81
|
+
|
82
|
+
# Stores entity bodies on disk at the specified path.
|
83
|
+
class Disk < EntityStore
|
84
|
+
|
85
|
+
# Path where entities should be stored. This directory is
|
86
|
+
# created the first time the store is instansiated if it does not
|
87
|
+
# already exist.
|
88
|
+
attr_reader :root
|
89
|
+
|
90
|
+
def initialize(root)
|
91
|
+
@root = root
|
92
|
+
FileUtils.mkdir_p root, :mode => 0755
|
93
|
+
end
|
94
|
+
|
95
|
+
def exist?(key)
|
96
|
+
File.exist?(body_path(key))
|
97
|
+
end
|
98
|
+
|
99
|
+
def read(key)
|
100
|
+
File.open(body_path(key), 'rb') { |f| f.read }
|
101
|
+
rescue Errno::ENOENT
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
class Body < ::File #:nodoc:
|
106
|
+
def each
|
107
|
+
while part = read(8192)
|
108
|
+
yield part
|
109
|
+
end
|
110
|
+
end
|
111
|
+
alias_method :to_path, :path
|
112
|
+
end
|
113
|
+
|
114
|
+
# Open the entity body and return an IO object. The IO object's
|
115
|
+
# each method is overridden to read 8K chunks instead of lines.
|
116
|
+
def open(key)
|
117
|
+
Body.open(body_path(key), 'rb')
|
118
|
+
rescue Errno::ENOENT
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
122
|
+
def write(body, ttl=nil)
|
123
|
+
filename = ['buf', $$, Thread.current.object_id].join('-')
|
124
|
+
temp_file = storage_path(filename)
|
125
|
+
key, size =
|
126
|
+
File.open(temp_file, 'wb') { |dest|
|
127
|
+
slurp(body) { |part| dest.write(part) }
|
128
|
+
}
|
129
|
+
|
130
|
+
path = body_path(key)
|
131
|
+
if File.exist?(path)
|
132
|
+
File.unlink temp_file
|
133
|
+
else
|
134
|
+
FileUtils.mkdir_p File.dirname(path), :mode => 0755
|
135
|
+
FileUtils.mv temp_file, path
|
136
|
+
end
|
137
|
+
[key, size]
|
138
|
+
end
|
139
|
+
|
140
|
+
def purge(key)
|
141
|
+
File.unlink body_path(key)
|
142
|
+
nil
|
143
|
+
rescue Errno::ENOENT
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
|
147
|
+
protected
|
148
|
+
def storage_path(stem)
|
149
|
+
File.join root, stem
|
150
|
+
end
|
151
|
+
|
152
|
+
def spread(key)
|
153
|
+
key = key.dup
|
154
|
+
key[2,0] = '/'
|
155
|
+
key
|
156
|
+
end
|
157
|
+
|
158
|
+
def body_path(key)
|
159
|
+
storage_path spread(key)
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.resolve(uri)
|
163
|
+
path = File.expand_path(uri.opaque || uri.path)
|
164
|
+
new path
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
DISK = Disk
|
169
|
+
FILE = Disk
|
170
|
+
|
171
|
+
# Base class for memcached entity stores.
|
172
|
+
class MemCacheBase < EntityStore
|
173
|
+
# The underlying Memcached instance used to communicate with the
|
174
|
+
# memcached daemon.
|
175
|
+
attr_reader :cache
|
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
|
+
if uri.respond_to?(:scheme)
|
186
|
+
server = "#{uri.host}:#{uri.port || '11211'}"
|
187
|
+
options = parse_query(uri.query)
|
188
|
+
options.keys.each do |key|
|
189
|
+
value =
|
190
|
+
case value = options.delete(key)
|
191
|
+
when 'true' ; true
|
192
|
+
when 'false' ; false
|
193
|
+
else value.to_sym
|
194
|
+
end
|
195
|
+
options[key.to_sym] = value
|
196
|
+
end
|
197
|
+
options[:namespace] = uri.path.sub(/^\//, '')
|
198
|
+
new server, options
|
199
|
+
else
|
200
|
+
# if the object provided is not a URI, pass it straight through
|
201
|
+
# to the underlying implementation.
|
202
|
+
new uri
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Uses the Dalli ruby library. This is the default unless
|
208
|
+
# the memcached library has already been required.
|
209
|
+
class Dalli < MemCacheBase
|
210
|
+
def initialize(server="localhost:11211", options={})
|
211
|
+
@cache =
|
212
|
+
if server.respond_to?(:stats)
|
213
|
+
server
|
214
|
+
else
|
215
|
+
require 'dalli'
|
216
|
+
::Dalli::Client.new(server, options)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def exist?(key)
|
221
|
+
!cache.get(key).nil?
|
222
|
+
end
|
223
|
+
|
224
|
+
def read(key)
|
225
|
+
data = cache.get(key)
|
226
|
+
data.force_encoding('BINARY') if data.respond_to?(:force_encoding)
|
227
|
+
data
|
228
|
+
end
|
229
|
+
|
230
|
+
def write(body, ttl=nil)
|
231
|
+
buf = StringIO.new
|
232
|
+
key, size = slurp(body){|part| buf.write(part) }
|
233
|
+
[key, size] if cache.set(key, buf.string, ttl)
|
234
|
+
end
|
235
|
+
|
236
|
+
def purge(key)
|
237
|
+
cache.delete(key)
|
238
|
+
nil
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# Uses the memcached client library. The ruby based memcache-client is used
|
243
|
+
# in preference to this store unless the memcached library has already been
|
244
|
+
# required.
|
245
|
+
class MemCached < MemCacheBase
|
246
|
+
def initialize(server="localhost:11211", options={})
|
247
|
+
options[:prefix_key] ||= options.delete(:namespace) if options.key?(:namespace)
|
248
|
+
@cache =
|
249
|
+
if server.respond_to?(:stats)
|
250
|
+
server
|
251
|
+
else
|
252
|
+
require 'memcached'
|
253
|
+
::Memcached.new(server, options)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def exist?(key)
|
258
|
+
cache.append(key, '')
|
259
|
+
true
|
260
|
+
rescue ::Memcached::NotStored
|
261
|
+
false
|
262
|
+
end
|
263
|
+
|
264
|
+
def read(key)
|
265
|
+
cache.get(key, false)
|
266
|
+
rescue ::Memcached::NotFound
|
267
|
+
nil
|
268
|
+
end
|
269
|
+
|
270
|
+
def write(body, ttl=0)
|
271
|
+
buf = StringIO.new
|
272
|
+
key, size = slurp(body){|part| buf.write(part) }
|
273
|
+
cache.set(key, buf.string, ttl, false)
|
274
|
+
[key, size]
|
275
|
+
end
|
276
|
+
|
277
|
+
def purge(key)
|
278
|
+
cache.delete(key)
|
279
|
+
nil
|
280
|
+
rescue ::Memcached::NotFound
|
281
|
+
nil
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
MEMCACHE =
|
286
|
+
if defined?(::Memcached)
|
287
|
+
MemCached
|
288
|
+
else
|
289
|
+
Dalli
|
290
|
+
end
|
291
|
+
|
292
|
+
MEMCACHED = MEMCACHE
|
293
|
+
|
294
|
+
class GAEStore < EntityStore
|
295
|
+
attr_reader :cache
|
296
|
+
|
297
|
+
def initialize(options = {})
|
298
|
+
require 'rack/cache/app_engine'
|
299
|
+
@cache = Rack::Cache::AppEngine::MemCache.new(options)
|
300
|
+
end
|
301
|
+
|
302
|
+
def exist?(key)
|
303
|
+
cache.contains?(key)
|
304
|
+
end
|
305
|
+
|
306
|
+
def read(key)
|
307
|
+
cache.get(key)
|
308
|
+
end
|
309
|
+
|
310
|
+
def open(key)
|
311
|
+
if data = read(key)
|
312
|
+
[data]
|
313
|
+
else
|
314
|
+
nil
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def write(body, ttl=nil)
|
319
|
+
buf = StringIO.new
|
320
|
+
key, size = slurp(body){|part| buf.write(part) }
|
321
|
+
cache.put(key, buf.string, ttl)
|
322
|
+
[key, size]
|
323
|
+
end
|
324
|
+
|
325
|
+
def purge(key)
|
326
|
+
cache.delete(key)
|
327
|
+
nil
|
328
|
+
end
|
329
|
+
|
330
|
+
def self.resolve(uri)
|
331
|
+
self.new(:namespace => uri.host)
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
GAECACHE = GAEStore
|
337
|
+
GAE = GAEStore
|
338
|
+
|
339
|
+
# Noop Entity Store backend.
|
340
|
+
#
|
341
|
+
# Set `entitystore` to 'noop:/'.
|
342
|
+
# Does not persist response bodies (no disk/memory used).
|
343
|
+
# Responses from the cache will have an empty body.
|
344
|
+
# Clients must ignore these empty cached response (check for X-Rack-Cache response header).
|
345
|
+
# Atm cannot handle streamed responses, patch needed.
|
346
|
+
#
|
347
|
+
class Noop < EntityStore
|
348
|
+
def exist?(key)
|
349
|
+
true
|
350
|
+
end
|
351
|
+
|
352
|
+
def read(key)
|
353
|
+
''
|
354
|
+
end
|
355
|
+
|
356
|
+
def open(key)
|
357
|
+
[]
|
358
|
+
end
|
359
|
+
|
360
|
+
def write(body, ttl=nil)
|
361
|
+
key, size = slurp(body) { |part| part }
|
362
|
+
[key, size]
|
363
|
+
end
|
364
|
+
|
365
|
+
def purge(key)
|
366
|
+
nil
|
367
|
+
end
|
368
|
+
|
369
|
+
def self.resolve(uri)
|
370
|
+
new
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
NOOP = Noop
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
@@ -1,341 +1,2 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
module Rack::Cache
|
4
|
-
|
5
|
-
# Entity stores are used to cache response bodies across requests. All
|
6
|
-
# Implementations are required to calculate a SHA checksum of the data written
|
7
|
-
# which becomes the response body's key.
|
8
|
-
class EntityStore
|
9
|
-
|
10
|
-
# Read body calculating the SHA1 checksum and size while
|
11
|
-
# yielding each chunk to the block. If the body responds to close,
|
12
|
-
# call it after iteration is complete. Return a two-tuple of the form:
|
13
|
-
# [ hexdigest, size ].
|
14
|
-
def slurp(body)
|
15
|
-
digest, size = Digest::SHA1.new, 0
|
16
|
-
body.each do |part|
|
17
|
-
size += bytesize(part)
|
18
|
-
digest << part
|
19
|
-
yield part
|
20
|
-
end
|
21
|
-
body.close if body.respond_to? :close
|
22
|
-
[digest.hexdigest, size]
|
23
|
-
end
|
24
|
-
|
25
|
-
if ''.respond_to?(:bytesize)
|
26
|
-
def bytesize(string); string.bytesize; end
|
27
|
-
else
|
28
|
-
def bytesize(string); string.size; end
|
29
|
-
end
|
30
|
-
|
31
|
-
private :slurp, :bytesize
|
32
|
-
|
33
|
-
|
34
|
-
# Stores entity bodies on the heap using a Hash object.
|
35
|
-
class Heap < EntityStore
|
36
|
-
|
37
|
-
# Create the store with the specified backing Hash.
|
38
|
-
def initialize(hash={})
|
39
|
-
@hash = hash
|
40
|
-
end
|
41
|
-
|
42
|
-
# Determine whether the response body with the specified key (SHA1)
|
43
|
-
# exists in the store.
|
44
|
-
def exist?(key)
|
45
|
-
@hash.include?(key)
|
46
|
-
end
|
47
|
-
|
48
|
-
# Return an object suitable for use as a Rack response body for the
|
49
|
-
# specified key.
|
50
|
-
def open(key)
|
51
|
-
(body = @hash[key]) && body.dup
|
52
|
-
end
|
53
|
-
|
54
|
-
# Read all data associated with the given key and return as a single
|
55
|
-
# String.
|
56
|
-
def read(key)
|
57
|
-
(body = @hash[key]) && body.join
|
58
|
-
end
|
59
|
-
|
60
|
-
# Write the Rack response body immediately and return the SHA1 key.
|
61
|
-
def write(body, ttl=nil)
|
62
|
-
buf = []
|
63
|
-
key, size = slurp(body) { |part| buf << part }
|
64
|
-
@hash[key] = buf
|
65
|
-
[key, size]
|
66
|
-
end
|
67
|
-
|
68
|
-
# Remove the body corresponding to key; return nil.
|
69
|
-
def purge(key)
|
70
|
-
@hash.delete(key)
|
71
|
-
nil
|
72
|
-
end
|
73
|
-
|
74
|
-
def self.resolve(uri)
|
75
|
-
new
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
HEAP = Heap
|
80
|
-
MEM = Heap
|
81
|
-
|
82
|
-
# Stores entity bodies on disk at the specified path.
|
83
|
-
class Disk < EntityStore
|
84
|
-
|
85
|
-
# Path where entities should be stored. This directory is
|
86
|
-
# created the first time the store is instansiated if it does not
|
87
|
-
# already exist.
|
88
|
-
attr_reader :root
|
89
|
-
|
90
|
-
def initialize(root)
|
91
|
-
@root = root
|
92
|
-
FileUtils.mkdir_p root, :mode => 0755
|
93
|
-
end
|
94
|
-
|
95
|
-
def exist?(key)
|
96
|
-
File.exist?(body_path(key))
|
97
|
-
end
|
98
|
-
|
99
|
-
def read(key)
|
100
|
-
File.open(body_path(key), 'rb') { |f| f.read }
|
101
|
-
rescue Errno::ENOENT
|
102
|
-
nil
|
103
|
-
end
|
104
|
-
|
105
|
-
class Body < ::File #:nodoc:
|
106
|
-
def each
|
107
|
-
while part = read(8192)
|
108
|
-
yield part
|
109
|
-
end
|
110
|
-
end
|
111
|
-
alias_method :to_path, :path
|
112
|
-
end
|
113
|
-
|
114
|
-
# Open the entity body and return an IO object. The IO object's
|
115
|
-
# each method is overridden to read 8K chunks instead of lines.
|
116
|
-
def open(key)
|
117
|
-
Body.open(body_path(key), 'rb')
|
118
|
-
rescue Errno::ENOENT
|
119
|
-
nil
|
120
|
-
end
|
121
|
-
|
122
|
-
def write(body, ttl=nil)
|
123
|
-
filename = ['buf', $$, Thread.current.object_id].join('-')
|
124
|
-
temp_file = storage_path(filename)
|
125
|
-
key, size =
|
126
|
-
File.open(temp_file, 'wb') { |dest|
|
127
|
-
slurp(body) { |part| dest.write(part) }
|
128
|
-
}
|
129
|
-
|
130
|
-
path = body_path(key)
|
131
|
-
if File.exist?(path)
|
132
|
-
File.unlink temp_file
|
133
|
-
else
|
134
|
-
FileUtils.mkdir_p File.dirname(path), :mode => 0755
|
135
|
-
FileUtils.mv temp_file, path
|
136
|
-
end
|
137
|
-
[key, size]
|
138
|
-
end
|
139
|
-
|
140
|
-
def purge(key)
|
141
|
-
File.unlink body_path(key)
|
142
|
-
nil
|
143
|
-
rescue Errno::ENOENT
|
144
|
-
nil
|
145
|
-
end
|
146
|
-
|
147
|
-
protected
|
148
|
-
def storage_path(stem)
|
149
|
-
File.join root, stem
|
150
|
-
end
|
151
|
-
|
152
|
-
def spread(key)
|
153
|
-
key = key.dup
|
154
|
-
key[2,0] = '/'
|
155
|
-
key
|
156
|
-
end
|
157
|
-
|
158
|
-
def body_path(key)
|
159
|
-
storage_path spread(key)
|
160
|
-
end
|
161
|
-
|
162
|
-
def self.resolve(uri)
|
163
|
-
path = File.expand_path(uri.opaque || uri.path)
|
164
|
-
new path
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
DISK = Disk
|
169
|
-
FILE = Disk
|
170
|
-
|
171
|
-
# Base class for memcached entity stores.
|
172
|
-
class MemCacheBase < EntityStore
|
173
|
-
# The underlying Memcached instance used to communicate with the
|
174
|
-
# memcached daemon.
|
175
|
-
attr_reader :cache
|
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
|
-
if uri.respond_to?(:scheme)
|
186
|
-
server = "#{uri.host}:#{uri.port || '11211'}"
|
187
|
-
options = parse_query(uri.query)
|
188
|
-
options.keys.each do |key|
|
189
|
-
value =
|
190
|
-
case value = options.delete(key)
|
191
|
-
when 'true' ; true
|
192
|
-
when 'false' ; false
|
193
|
-
else value.to_sym
|
194
|
-
end
|
195
|
-
options[key.to_sym] = value
|
196
|
-
end
|
197
|
-
options[:namespace] = uri.path.sub(/^\//, '')
|
198
|
-
new server, options
|
199
|
-
else
|
200
|
-
# if the object provided is not a URI, pass it straight through
|
201
|
-
# to the underlying implementation.
|
202
|
-
new uri
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
# Uses the Dalli ruby library. This is the default unless
|
208
|
-
# the memcached library has already been required.
|
209
|
-
class Dalli < MemCacheBase
|
210
|
-
def initialize(server="localhost:11211", options={})
|
211
|
-
@cache =
|
212
|
-
if server.respond_to?(:stats)
|
213
|
-
server
|
214
|
-
else
|
215
|
-
require 'dalli'
|
216
|
-
::Dalli::Client.new(server, options)
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def exist?(key)
|
221
|
-
!cache.get(key).nil?
|
222
|
-
end
|
223
|
-
|
224
|
-
def read(key)
|
225
|
-
data = cache.get(key)
|
226
|
-
data.force_encoding('BINARY') if data.respond_to?(:force_encoding)
|
227
|
-
data
|
228
|
-
end
|
229
|
-
|
230
|
-
def write(body, ttl=nil)
|
231
|
-
buf = StringIO.new
|
232
|
-
key, size = slurp(body){|part| buf.write(part) }
|
233
|
-
[key, size] if cache.set(key, buf.string, ttl)
|
234
|
-
end
|
235
|
-
|
236
|
-
def purge(key)
|
237
|
-
cache.delete(key)
|
238
|
-
nil
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
# Uses the memcached client library. The ruby based memcache-client is used
|
243
|
-
# in preference to this store unless the memcached library has already been
|
244
|
-
# required.
|
245
|
-
class MemCached < MemCacheBase
|
246
|
-
def initialize(server="localhost:11211", options={})
|
247
|
-
options[:prefix_key] ||= options.delete(:namespace) if options.key?(:namespace)
|
248
|
-
@cache =
|
249
|
-
if server.respond_to?(:stats)
|
250
|
-
server
|
251
|
-
else
|
252
|
-
require 'memcached'
|
253
|
-
::Memcached.new(server, options)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
def exist?(key)
|
258
|
-
cache.append(key, '')
|
259
|
-
true
|
260
|
-
rescue ::Memcached::NotStored
|
261
|
-
false
|
262
|
-
end
|
263
|
-
|
264
|
-
def read(key)
|
265
|
-
cache.get(key, false)
|
266
|
-
rescue ::Memcached::NotFound
|
267
|
-
nil
|
268
|
-
end
|
269
|
-
|
270
|
-
def write(body, ttl=0)
|
271
|
-
buf = StringIO.new
|
272
|
-
key, size = slurp(body){|part| buf.write(part) }
|
273
|
-
cache.set(key, buf.string, ttl, false)
|
274
|
-
[key, size]
|
275
|
-
end
|
276
|
-
|
277
|
-
def purge(key)
|
278
|
-
cache.delete(key)
|
279
|
-
nil
|
280
|
-
rescue ::Memcached::NotFound
|
281
|
-
nil
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
MEMCACHE =
|
286
|
-
if defined?(::Memcached)
|
287
|
-
MemCached
|
288
|
-
else
|
289
|
-
Dalli
|
290
|
-
end
|
291
|
-
|
292
|
-
MEMCACHED = MEMCACHE
|
293
|
-
|
294
|
-
class GAEStore < EntityStore
|
295
|
-
attr_reader :cache
|
296
|
-
|
297
|
-
def initialize(options = {})
|
298
|
-
require 'rack/cache/appengine'
|
299
|
-
@cache = Rack::Cache::AppEngine::MemCache.new(options)
|
300
|
-
end
|
301
|
-
|
302
|
-
def exist?(key)
|
303
|
-
cache.contains?(key)
|
304
|
-
end
|
305
|
-
|
306
|
-
def read(key)
|
307
|
-
cache.get(key)
|
308
|
-
end
|
309
|
-
|
310
|
-
def open(key)
|
311
|
-
if data = read(key)
|
312
|
-
[data]
|
313
|
-
else
|
314
|
-
nil
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
def write(body, ttl=nil)
|
319
|
-
buf = StringIO.new
|
320
|
-
key, size = slurp(body){|part| buf.write(part) }
|
321
|
-
cache.put(key, buf.string, ttl)
|
322
|
-
[key, size]
|
323
|
-
end
|
324
|
-
|
325
|
-
def purge(key)
|
326
|
-
cache.delete(key)
|
327
|
-
nil
|
328
|
-
end
|
329
|
-
|
330
|
-
def self.resolve(uri)
|
331
|
-
self.new(:namespace => uri.host)
|
332
|
-
end
|
333
|
-
|
334
|
-
end
|
335
|
-
|
336
|
-
GAECACHE = GAEStore
|
337
|
-
GAE = GAEStore
|
338
|
-
|
339
|
-
end
|
340
|
-
|
341
|
-
end
|
1
|
+
warn "use require 'rack/cache/entity_store'"
|
2
|
+
require 'rack/cache/entity_store'
|