padrino-cache 0.9.24 → 0.9.25

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,7 +1,337 @@
1
1
  = Painless Page and Fragment Caching (padrino-cache)
2
2
 
3
- Not documented yet.
3
+ == Overview
4
+
5
+ This component enables caching of an application's response contents on
6
+ both page- and fragment-levels. Output cached in this manner is
7
+ persisted, until it expires or is actively expired, in a configurable store
8
+ of your choosing. Several common caching stores are supported out of the box.
9
+
10
+ == Caching Quickstart
11
+
12
+ Padrino-cache can reduce the processing load on your site very effectively
13
+ with minimal configuration.
14
+
15
+ By default, the component caches pages in a file store at <tt>tmp/cache</tt>
16
+ within your project root. Entries in this store correspond directly
17
+ to the request issued to your server. In other words, responses are
18
+ cached based on request URL, with one cache entry per URL.
19
+
20
+ This behavior is referred to as "page-level caching." If this strategy meets
21
+ your needs, you can enable it very easily:
22
+
23
+ # Basic, page-level caching
24
+ class SimpleApp < Padrino::Application
25
+ register Padrino::Cache
26
+ enable :caching
27
+
28
+ get '/foo', :cache => true do
29
+ expires_in 30 # expire cached version at least every 30 seconds
30
+ 'Hello world'
31
+ end
32
+ end
33
+
34
+ You can also cache on a controller-wide basis:
35
+
36
+ # Controller-wide caching example
37
+ class SimpleApp < Padrino::Application
38
+ register Padrino::Cache
39
+ enable :caching
40
+
41
+ get '/' do
42
+ 'Hello world'
43
+ end
44
+
45
+ # Requests to routes within '/admin'
46
+ controller '/admin', :cache => true do
47
+ expires_in 60
48
+
49
+ get '/foo' do
50
+ 'Url is /admin/foo'
51
+ end
52
+
53
+ get '/bar' do
54
+ 'Url is /admin/bar'
55
+ end
56
+
57
+ post '/baz' do # We cache only GET and HEAD request
58
+ 'This will not be cached'
59
+ end
60
+ end
61
+ end
62
+
63
+ You can also provide a custom <tt>cache_key</tt> in any route:
64
+
65
+ class SimpleApp < Padrino::Application
66
+ register Padrino::Cache
67
+ enable :caching
68
+
69
+ get '/post/:id', :cache => true do
70
+ @post = Post.find(params[:id])
71
+ cache_key :my_name
72
+ end
73
+ end
74
+
75
+ In this way you can manually expire cache with CachedApp.cache.delete(:my_name)
76
+ for example from the Post model after an update.
77
+
78
+ If you specify <tt>:cache => true</tt> but do not invoke <tt>expires_in</tt>,
79
+ the response will be cached indefinitely. Most of the time, you will want to
80
+ specify the expiry of a cache entry by <tt>expires_in</tt>. Even a relatively
81
+ low value--1 or 2 seconds--can greatly increase application efficiency, especially
82
+ when enabled on a very active part of your domain.
83
+
84
+ == Helpers
85
+
86
+ When an application registers padrino-cache, it gains access to several helper
87
+ methods. These methods are used according to your caching strategy, so they are
88
+ explained here likewise--by functionality.
89
+
90
+ As with all code optimization, you may want to start simply (at "page level"),
91
+ and continue if necessary into sub-page (or "fragment level" ) caching. There
92
+ is no one way to approach caching, but it's always good to avoid complexity
93
+ until you need it. Start at the page level and see if it works for you.
94
+
95
+ The padrino-cache helpers are made available to your application thusly:
96
+
97
+ # Enable caching
98
+ class CachedApp < Padrino::Application
99
+ register Padrino::Cache # includes helpers
100
+ enable :caching # turns on caching
101
+
102
+ # ... controllers/routes ...
103
+ end
104
+
105
+ === Page Caching
106
+
107
+ As described above in the "Caching Quickstart" section, page caching is very
108
+ easy to integrate into your application. To turn it on, simply provide the
109
+ <tt>:cache => true</tt> option on either a controller or one of its routes.
110
+ By default, cached content is persisted with a "file store"--that is, in a
111
+ subdirectory of your application root.
112
+
113
+ ==== <tt>expires_in( seconds )</tt>
114
+
115
+ This helper is used within a controller or route to indicate how often cached
116
+ <em>page-level</em> content should persist in the cache.
117
+
118
+ After <tt>seconds</tt> seconds have passed, content previously cached will
119
+ be discarded and re-rendered. Code associated with that route will <em>not</em>
120
+ be executed; rather, its previous output will be sent to the client with a
121
+ 200 OK status code.
122
+
123
+ # Setting content expiry time
124
+ class CachedApp < Padrino::Application
125
+ register Padrino::Cache # includes helpers
126
+ enable :caching # turns on caching
127
+
128
+ controller '/blog', :cache => true do
129
+ expires_in 15
130
+
131
+ get '/entries' do
132
+ 'just broke up eating twinkies lol'
133
+ end
134
+ end
135
+ end
136
+
137
+ Note that the "latest" method call to <tt>expires_in</tt> determines its value: if
138
+ called within a route, as opposed to a controller definition, the route's
139
+ value will be assumed.
140
+
141
+ === Fragment Caching
142
+
143
+ Whereas page-level caching, described in the first section of this document, works by
144
+ grabbing the entire output of a route, fragment caching gives the developer fine-grained
145
+ control of what gets cached. This type of caching occurs at whatever level you choose.
146
+
147
+ Possible uses for fragment caching might include:
148
+
149
+ * a 'feed' of some items on a page
150
+ * output fetched (by proxy) from an API on a third-party site
151
+ * parts of your page which are largely static/do not need re-rendering every request
152
+ * any output which is expensive to render
153
+
154
+ ==== <tt>cache( key, opts, &block )</tt>
155
+
156
+ This helper is used anywhere in your application you would like to associate a fragment
157
+ to be cached. It can be used in within a route:
158
+
159
+ # Caching a fragment
160
+ class MyTweets < Padrino::Application
161
+ register Padrino::Cache # includes helpers
162
+ enable :caching # turns on caching
163
+
164
+ controller '/tweets' do
165
+ get :feed, :map => '/:username' do
166
+ username = params[:username]
167
+
168
+ @feed = cache( "feed_for_#{username}", :expires_in => 3 ) do
169
+ @tweets = Tweet.all( :username => username )
170
+ render 'partials/feedcontent'
171
+ end
172
+
173
+ # Below outputs @feed somewhere in its markup
174
+ render 'feeds/show'
175
+ end
176
+ end
177
+ end
178
+
179
+ This example adds a key to the cache of format <tt>feed_for_#{username}</tt> which
180
+ contains the contents of that user's feed. Any subsequent action within the next 3 seconds
181
+ will fetch the pre-rendered version of <tt>feed_for_#{username}</tt> from the cache
182
+ instead of re-rendering it. The rest of the page code will, however, be re-executed.
183
+
184
+ Note that any other action will reference the same content if it uses the same key:
185
+
186
+ # Multiple routes sharing the same cached fragment
187
+ class MyTweets < Padrino::Application
188
+ register Padrino::Cache # includes helpers
189
+ enable :caching # turns on caching
190
+
191
+ controller :tweets do
192
+ get :feed, :map => '/:username' do
193
+ username = params[:username]
194
+
195
+ @feed = cache( "feed_for_#{username}", :expires_in => 3 ) do
196
+ @tweets = Tweet.all( :username => username )
197
+ render 'partials/feedcontent'
198
+ end
199
+
200
+ # Below outputs @feed somewhere in its markup
201
+ render 'feeds/show'
202
+ end
203
+
204
+ get :mobile_feed, :map => '/:username.iphone' do
205
+ username = params[:username]
206
+
207
+ @feed = cache( "feed_for_#{username}", :expires_in => 3 ) do
208
+ @tweets = Tweet.all( :username => username )
209
+ render 'partials/feedcontent'
210
+ end
211
+
212
+ render 'feeds/show.iphone'
213
+ end
214
+ end
215
+ end
216
+
217
+ The <tt>opts</tt> argument is actually passed to the underlying store. All stores included with Padrino support the <tt>:expires_in</tt> option out of the box.
218
+
219
+ Finally, to DRY up things a bit, we might do:
220
+
221
+ # Multiple routes sharing the same cached fragment
222
+ class MyTweets < Padrino::Application
223
+ register Padrino::Cache # includes helpers
224
+ enable :caching # turns on caching
225
+
226
+ controller :tweets do
227
+ # This works because all routes in this controller specify :username
228
+ before do
229
+ @feed = cache( "feed_for_#{params[:username]}", :expires_in => 3 ) do
230
+ @tweets = Tweet.all( :username => params[:username] )
231
+ render 'partials/feedcontent'
232
+ end
233
+ end
234
+
235
+ get :feed, :map => '/:username' do
236
+ render 'feeds/show'
237
+ end
238
+
239
+ get :mobile_feed, :map => '/:username.iphone' do
240
+ render 'feeds/show.iphone'
241
+ end
242
+ end
243
+ end
244
+
245
+ Of course, this example assumes the markup generated by rendering
246
+ <tt>partials/feedcontent</tt> would be suitable for both feed formats. This may or
247
+ may not be the case in your application, but the principle applies: fragments
248
+ are shared between all code which accesses the cache using the same key.
249
+
250
+ == Caching Store
251
+
252
+ You can set a global caching option or a per app caching options.
253
+
254
+ === Global Caching Options
255
+
256
+ Padrino.cache = Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
257
+ Padrino.cache = Padrino::Cache::Store::Memcache.new(::Dalli::Client.new('127.0.0.1:11211', :exception_retry_limit => 1))
258
+ Padrino.cache = Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
259
+ Padrino.cache = Padrino::Cache::Store::Memory.new(50)
260
+ Padrino.cache = Padrino::Cache::Store::File.new(/my/cache/path)
261
+
262
+ You can manage your cache from anywhere in your app:
263
+
264
+ Padrino.cache.set('val', 'test')
265
+ Padrino.cache.get('val') # => 'test'
266
+ Padrino.cache.delete('val')
267
+ Padrino.cache.flush
268
+
269
+ ==== Application Caching Options
270
+
271
+ set :cache, Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
272
+ set :cache, Padrino::Cache::Store::Memcache.new(::Dalli::Client.new('127.0.0.1:11211', :exception_retry_limit => 1))
273
+ set :cache, Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
274
+ set :cache, Padrino::Cache::Store::Memory.new(50)
275
+ set :cache, Padrino::Cache::Store::File.new(Padrino.root('tmp', app_name, 'cache') # default choice
276
+
277
+ You can manage your cache from anywhere in your app:
278
+
279
+ MyApp.cache.set('val', 'test')
280
+ MyApp.cache.get('val') # => 'test'
281
+ MyApp.cache.delete('val')
282
+ MyApp.cache.flush
283
+
284
+ == Expiring Cached Content
285
+
286
+ In certain circumstances, cached content becomes stale. The <tt>expire</tt>
287
+ helper removes content associated with a key or keys, which your app is then
288
+ free to re-generate.
289
+
290
+ === <tt>expire( *key )</tt>
291
+
292
+ ==== Fragment-level expiration
293
+
294
+ Using the example above of a tweet server, let's suppose our users have a
295
+ tendency to post things they quickly regret. When we query our database
296
+ for new tweets, let's check to see if any have been deleted. If so, we'll
297
+ do our user a favor and instantly re-render the feed.
298
+
299
+ # Expiring fragment-level cached content
300
+ class MyTweets < Padrino::Application
301
+ register Padrino::Cache # includes helpers
302
+ enable :caching # turns on caching
303
+ enable :session # we'll use this to store last time visited
304
+
305
+ COMPANY_FOUNDING = Time.utc( 2010, "April" )
306
+
307
+ controller :tweets do
308
+ get :feed, :map => '/:username' do
309
+ last_visit = session[:last_visit] || params[:since] || COMPANY_FOUNDING
310
+
311
+ username = params[:username]
312
+ @tweets = Tweet.since( last_visit, :username => username ).limit( 100 )
313
+
314
+ expire( "feed since #{last_visit}" ) if @tweets.any? { |t| t.deleted_since?( last_visit ) }
315
+
316
+ session[:last_visit] = Time.now
317
+ @feed = cache( "feed since #{last_visit}", :expires_in => 60 ) do
318
+ @tweets = @tweets.find_all { |t| !t.deleted? }
319
+ render 'partials/feedcontent'
320
+ end
321
+
322
+ render 'feeds/show'
323
+ end
324
+ end
325
+ end
326
+
327
+ Normally, this example will only re-cache feed content every 60 seconds,
328
+ but it will do so immediately if any tweets have been deleted.
329
+
330
+ ==== Page-level expiration
331
+
332
+ Page-level expiration works exactly like the example above--by using
333
+ <tt>expire</tt> in your controller. The key is typically <tt>env['PATH_INFO']</tt>.
4
334
 
5
335
  == Copyright
6
336
 
7
- Copyright (c) 2011 Padrino. See LICENSE for details.
337
+ Copyright (c) 2011 Padrino. See LICENSE for details.
data/lib/padrino-cache.rb CHANGED
@@ -4,31 +4,93 @@ require 'padrino-helpers'
4
4
  FileSet.glob_require('padrino-cache/{helpers}/*.rb', __FILE__)
5
5
 
6
6
  module Padrino
7
- module Cache
7
+ class << self
8
8
  ##
9
- # Register these helpers:
9
+ # Returns the caching engine
10
+ #
11
+ # ==== Examples
12
+ # # with: Padrino.cache = Padrino::Cache::Store::File.new(/my/cache/path)
13
+ # Padrino.cache.set('val', 'test')
14
+ # Padrino.cache.get('val') # => 'test'
15
+ # Padrino.cache.delete('val')
16
+ # Padrino.cache.flush
10
17
  #
11
- # Padrino::Cache::FragmentHelpers
12
- # Padrino::Cache::PageHelpers
18
+ def cache
19
+ @_cache
20
+ end
21
+
22
+ ##
23
+ # Set the caching engine
13
24
  #
14
- # for Padrino::Application
25
+ # === Examples
26
+ # Padrino.cache = Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
27
+ # Padrino.cache = Padrino::Cache::Store::Memcache.new(::Dalli::Client.new('127.0.0.1:11211', :exception_retry_limit => 1))
28
+ # Padrino.cache = Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
29
+ # Padrino.cache = Padrino::Cache::Store::Memory.new(50)
30
+ # Padrino.cache = Padrino::Cache::Store::File.new(/my/cache/path)
15
31
  #
32
+ # You can manage your cache from anywhere in your app:
33
+ #
34
+ # Padrino.cache.set('val', 'test')
35
+ # Padrino.cache.get('val') # => 'test'
36
+ # Padrino.cache.delete('val')
37
+ # Padrino.cache.flush
38
+ #
39
+ def cache=(value)
40
+ @_cache = value
41
+ end
42
+ end # self
16
43
 
44
+ ##
45
+ # This component enables caching of an application's response contents on
46
+ # both page- and fragment-levels. Output cached in this manner is
47
+ # persisted, until it expires or is actively expired, in a configurable store
48
+ # of your choosing. Several common caching stores are supported out of the box.
49
+ #
50
+ module Cache
17
51
  autoload :Store, 'padrino-cache/store'
18
52
 
19
53
  class << self
54
+ ##
55
+ # Register these helpers:
56
+ #
57
+ # Padrino::Cache::Helpers::CacheStore
58
+ # Padrino::Cache::Helpers::Fragment
59
+ # Padrino::Cache::Helpers::Page
60
+ #
61
+ # for Padrino::Application.
62
+ #
63
+ # By default we use FileStore as showed below:
64
+ #
65
+ # set :cache, Padrino::Cache::Store::File.new(File.join(app.root, 'tmp', 'cache'))
66
+ #
67
+ # However, you can also change the file store easiily in your app.rb:
68
+ #
69
+ # set :cache, Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
70
+ # set :cache, Padrino::Cache::Store::Memcache.new(::Dalli::Client.new('127.0.0.1:11211', :exception_retry_limit => 1))
71
+ # set :cache, Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
72
+ # set :cache, Padrino::Cache::Store::Memory.new(50)
73
+ # set :cache, Padrino::Cache::Store::File.new(Padrino.root('tmp', app_name.to_s, 'cache')) # default choice
74
+ #
75
+ # You can manage your cache from anywhere in your app:
76
+ #
77
+ # MyApp.cache.set('val', 'test')
78
+ # MyApp.cache.get('val') # => 'test'
79
+ # MyApp.cache.delete('val')
80
+ # MyApp.cache.flush
81
+ #
20
82
  def registered(app)
21
83
  app.helpers Padrino::Cache::Helpers::CacheStore
22
84
  app.helpers Padrino::Cache::Helpers::Fragment
23
85
  app.helpers Padrino::Cache::Helpers::Page
24
- app.set :cache_store, Padrino::Cache::Store::File.new(File.join(app.root, 'tmp', 'cache'))
86
+ app.set :cache, Padrino::Cache::Store::File.new(Padrino.root('tmp', app.app_name.to_s ,'cache'))
25
87
  app.disable :caching
26
88
  end
27
89
  alias :included :registered
28
90
 
29
- def padrino_route_added(route, verb, path, args, options, block)
91
+ def padrino_route_added(route, verb, path, args, options, block) #:nodoc
30
92
  Padrino::Cache::Helpers::Page.padrino_route_added(route, verb, path, args, options, block)
31
93
  end
32
94
  end
33
- end # Helpers
95
+ end # Cache
34
96
  end # Padrino
@@ -1,12 +1,12 @@
1
1
  module Padrino
2
2
  module Cache
3
3
  module Helpers
4
- module CacheStore
4
+ module CacheStore #:nodoc:
5
5
  def expire(*key)
6
6
  if key.size == 1 and key.first.is_a?(String)
7
- self.class.cache_store.delete(key)
7
+ settings.cache.delete(key)
8
8
  else
9
- self.class.cache_store.delete(self.class.url(*key))
9
+ settings.cache.delete(self.class.url(*key))
10
10
  end
11
11
  end
12
12
  end # CacheStore
@@ -1,16 +1,55 @@
1
1
  module Padrino
2
2
  module Cache
3
3
  module Helpers
4
+ ##
5
+ # Whereas page-level caching, described in the first section of this document, works by
6
+ # grabbing the entire output of a route, fragment caching gives the developer fine-grained
7
+ # control of what gets cached. This type of caching occurs at whatever level you choose.
8
+ #
9
+ # Possible uses for fragment caching might include:
10
+ #
11
+ # * a 'feed' of some items on a page
12
+ # * output fetched (by proxy) from an API on a third-party site
13
+ # * parts of your page which are largely static/do not need re-rendering every request
14
+ # * any output which is expensive to render
15
+ #
4
16
  module Fragment
5
17
  include Padrino::Helpers::OutputHelpers
6
18
 
19
+ ##
20
+ # This helper is used anywhere in your application you would like to associate a fragment
21
+ # to be cached. It can be used in within a route:
22
+ #
23
+ # ==== Examples
24
+ # # Caching a fragment
25
+ # class MyTweets < Padrino::Application
26
+ # enable :caching # turns on caching mechanism
27
+ #
28
+ # controller '/tweets' do
29
+ # get :feed, :map => '/:username' do
30
+ # username = params[:username]
31
+ #
32
+ # @feed = cache( "feed_for_#{username}", :expires_in => 3 ) do
33
+ # @tweets = Tweet.all( :username => username )
34
+ # render 'partials/feedcontent'
35
+ # end
36
+ #
37
+ # # Below outputs @feed somewhere in its markup
38
+ # render 'feeds/show'
39
+ # end
40
+ # end
41
+ # end
42
+ #
7
43
  def cache(key, opts = nil, &block)
8
- if self.class.caching?
9
- if value = self.class.cache_store.get(key)
44
+ if settings.caching?
45
+ began_at = Time.now
46
+ if value = settings.cache.get(key.to_s)
47
+ logger.debug "GET Fragment (%0.4fms) %s" % [Time.now-began_at, key.to_s] if defined?(logger)
10
48
  concat_content(value)
11
49
  else
12
50
  value = capture_html(&block)
13
- self.class.cache_store.set(key, value, opts)
51
+ settings.cache.set(key.to_s, value, opts)
52
+ logger.debug "SET Fragment (%0.4fms) %s" % [Time.now-began_at, key.to_s] if defined?(logger)
14
53
  concat_content(value)
15
54
  end
16
55
  end
@@ -1,27 +1,81 @@
1
1
  module Padrino
2
2
  module Cache
3
3
  module Helpers
4
+ ##
5
+ # Page caching is easy to integrate into your application. To turn it on, simply provide the
6
+ # <tt>:cache => true</tt> option on either a controller or one of its routes.
7
+ # By default, cached content is persisted with a "file store"--that is, in a
8
+ # subdirectory of your application root.
9
+ #
10
+ # ==== Examples
11
+ # # Setting content expiry time
12
+ # class CachedApp < Padrino::Application
13
+ # enable :caching # turns on caching mechanism
14
+ #
15
+ # controller '/blog', :cache => true do
16
+ # expires_in 15
17
+ #
18
+ # get '/entries' do
19
+ # # expires_in 15 => can also be defined inside a single route
20
+ # 'just broke up eating twinkies lol'
21
+ # end
22
+ #
23
+ # get '/post/:id' do
24
+ # cache_key :my_name
25
+ # @post = Post.find(params[:id])
26
+ # end
27
+ # end
28
+ # end
29
+ #
30
+ # You can manually expire cache with CachedApp.cache.delete(:my_name)
31
+ #
32
+ # Note that the "latest" method call to <tt>expires_in</tt> determines its value: if
33
+ # called within a route, as opposed to a controller definition, the route's
34
+ # value will be assumed.
35
+ #
4
36
  module Page
37
+ ##
38
+ # This helper is used within a controller or route to indicate how often content
39
+ # should persist in the cache.
40
+ #
41
+ # After <tt>seconds</tt> seconds have passed, content previously cached will
42
+ # be discarded and re-rendered. Code associated with that route will <em>not</em>
43
+ # be executed; rather, its previous output will be sent to the client with a
44
+ # 200 OK status code.
45
+ #
5
46
  def expires_in(time)
6
47
  @_last_expires_in = time
7
48
  end
8
49
 
9
- def self.padrino_route_added(route, verb, path, args, options, block)
50
+ ##
51
+ # This helper is used within a route or route to indicate the name in the cache.
52
+ #
53
+ def cache_key(name)
54
+ @_cache_key = name
55
+ end
56
+
57
+ def self.padrino_route_added(route, verb, path, args, options, block) #:nodoc:
10
58
  if route.cache and %w(GET HEAD).include?(verb)
11
59
  route.add_before_filter(Proc.new {
12
- if self.class.caching?
13
- value = self.class.cache_store.get(route.cache.respond_to?(:call) ? route.cache.call(request) : env['PATH_INFO'])
60
+ if settings.caching?
61
+ began_at = Time.now
62
+ value = settings.cache.get(@_cache_key || env['PATH_INFO'])
63
+ @_cache_key = nil
64
+ logger.debug "GET Cache (%0.4fms) %s" % [Time.now-began_at, env['PATH_INFO']] if defined?(logger) && value
14
65
  halt 200, value if value
15
66
  end
16
67
  })
17
68
  route.add_after_filter(Proc.new { |something|
18
- if self.class.caching?
69
+ if settings.caching?
70
+ began_at = Time.now
19
71
  if @_last_expires_in
20
- self.class.cache_store.set(route.cache.respond_to?(:call) ? route.cache.call(request) : env['PATH_INFO'], @_response_buffer, :expires_in => @_last_expires_in)
72
+ settings.cache.set(@_cache_key || env['PATH_INFO'], @_response_buffer, :expires_in => @_last_expires_in)
21
73
  @_last_expires_in = nil
22
74
  else
23
- self.class.cache_store.set(route.cache.respond_to?(:call) ? route.cache.call(request) : env['PATH_INFO'], @_response_buffer)
75
+ settings.cache.set(@_cache_key || env['PATH_INFO'], @_response_buffer)
24
76
  end
77
+ @_cache_key = nil
78
+ logger.debug "SET Cache (%0.4fms) %s" % [Time.now-began_at, env['PATH_INFO']] if defined?(logger)
25
79
  end
26
80
  })
27
81
  end
@@ -9,4 +9,4 @@ module Padrino
9
9
  autoload :Redis, 'padrino-cache/store/redis'
10
10
  end # Store
11
11
  end # Cache
12
- end # Padrino
12
+ end # Padrino
@@ -1,11 +1,29 @@
1
1
  module Padrino
2
2
  module Cache
3
3
  module Store
4
+ ##
5
+ # File based Cache Store
6
+ #
4
7
  class File
8
+ ##
9
+ # Initialize File store with File root
10
+ #
11
+ # ==== Examples
12
+ # Padrino.cache = Padrino::Cache::Store::File.new("path/to")
13
+ # # or from your app
14
+ # set :cache, Padrino::Cache::Store::File.new("path/to")
15
+ #
5
16
  def initialize(root)
6
17
  @root = root
7
18
  end
8
19
 
20
+ ##
21
+ # Return the a value for the given key
22
+ #
23
+ # ==== Examples
24
+ # # with MyApp.cache.set('records', records)
25
+ # MyApp.cache.get('records')
26
+ #
9
27
  def get(key)
10
28
  init
11
29
  if ::File.exist?(path_for_key(key))
@@ -13,7 +31,7 @@ module Padrino
13
31
  expires_in, body = contents.split("\n", 2)
14
32
  expires_in = expires_in.to_i
15
33
  if expires_in == -1 or Time.new.to_i < expires_in
16
- body
34
+ Marshal.load(body) if body
17
35
  else
18
36
  delete(key)
19
37
  nil
@@ -23,6 +41,14 @@ module Padrino
23
41
  end
24
42
  end
25
43
 
44
+ ##
45
+ # Set the value for a given key and optionally with an expire time
46
+ # Default expiry time is 86400.
47
+ #
48
+ # ==== Examples
49
+ # MyApp.cache.set('records', records)
50
+ # MyApp.cache.set('records', records, :expires_in => 30) # => 30 seconds
51
+ #
26
52
  def set(key, value, opts = nil)
27
53
  init
28
54
  if opts && opts[:expires_in]
@@ -31,30 +57,42 @@ module Padrino
31
57
  else
32
58
  expires_in = -1
33
59
  end
34
- ::File.open(path_for_key(key), 'w') { |f| f << expires_in.to_s << "\n" << value.to_s } if value
60
+ value = Marshal.dump(value) if value
61
+ ::File.open(path_for_key(key), 'w') { |f| f << expires_in.to_s << "\n" << value } if value
35
62
  end
36
63
 
64
+ ##
65
+ # Delete the value for a given key
66
+ #
67
+ # ==== Examples
68
+ # # with: MyApp.cache.set('records', records)
69
+ # MyApp.cache.delete('records')
70
+ #
37
71
  def delete(key)
38
72
  init
39
73
  Array(key).each { |k| FileUtils.rm_rf(path_for_key(k)) }
40
74
  end
41
75
 
76
+ ##
77
+ # Reinitialize your cache
78
+ #
79
+ # ==== Examples
80
+ # # with: MyApp.cache.set('records', records)
81
+ # MyApp.cache.flush
82
+ # MyApp.cache.get('records') # => nil
83
+ #
42
84
  def flush
43
85
  FileUtils.rm_rf(@root)
44
86
  end
45
87
 
46
88
  private
47
- def path_for_key(key)
48
- ::File.join(@root, Rack::Utils.escape(key.to_s))
49
- end
89
+ def path_for_key(key)
90
+ ::File.join(@root, Rack::Utils.escape(key.to_s))
91
+ end
50
92
 
51
- def init
52
- unless @init
53
- FileUtils.rm_rf(@root)
54
- FileUtils.mkdir_p(@root)
55
- @init = true
93
+ def init
94
+ FileUtils.mkdir_p(@root) unless ::File.exist?(@root)
56
95
  end
57
- end
58
96
  end # File
59
97
  end # Store
60
98
  end # Cache
@@ -1,25 +1,47 @@
1
- begin
2
- require 'memcached'
3
- rescue LoadError
4
- raise "You must install memecached to use the Memecache cache store backend"
5
- end
6
-
7
1
  module Padrino
8
2
  module Cache
9
3
  module Store
4
+ ##
5
+ # Memcache Cache Store
6
+ #
10
7
  class Memcache
11
- def initialize(*args)
12
- @backend = Memcached.new(*args)
8
+ ##
9
+ # Initialize Memcache store with client connection.
10
+ #
11
+ # ==== Examples
12
+ # Padrino.cache = Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211'))
13
+ # Padrino.cache = Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
14
+ # # or from your app
15
+ # set :cache, Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211'))
16
+ # set :cache, Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
17
+ #
18
+ def initialize(client)
19
+ @backend = client
13
20
  rescue
14
21
  raise
15
22
  end
16
23
 
24
+ ##
25
+ # Return the a value for the given key
26
+ #
27
+ # ==== Examples
28
+ # # with MyApp.cache.set('records', records)
29
+ # MyApp.cache.get('records')
30
+ #
17
31
  def get(key)
18
32
  @backend.get(key)
19
33
  rescue Memcached::NotFound
20
34
  nil
21
35
  end
22
36
 
37
+ ##
38
+ # Set the value for a given key and optionally with an expire time
39
+ # Default expiry time is 86400.
40
+ #
41
+ # ==== Examples
42
+ # MyApp.cache.set('records', records)
43
+ # MyApp.cache.set('records', records, :expires_in => 30) # => 30 seconds
44
+ #
23
45
  def set(key, value, opts = nil)
24
46
  if opts && opts[:expires_in]
25
47
  expires_in = opts[:expires_in].to_i
@@ -30,10 +52,25 @@ module Padrino
30
52
  end
31
53
  end
32
54
 
55
+ ##
56
+ # Delete the value for a given key
57
+ #
58
+ # ==== Examples
59
+ # # with: MyApp.cache.set('records', records)
60
+ # MyApp.cache.delete('records')
61
+ #
33
62
  def delete(key)
34
63
  @backend.delete(key)
35
64
  end
36
65
 
66
+ ##
67
+ # Reinitialize your cache
68
+ #
69
+ # ==== Examples
70
+ # # with: MyApp.cache.set('records', records)
71
+ # MyApp.cache.flush
72
+ # MyApp.cache.get('records') # => nil
73
+ #
37
74
  def flush
38
75
  @backend.flush
39
76
  end
@@ -1,11 +1,29 @@
1
1
  module Padrino
2
2
  module Cache
3
3
  module Store
4
+ ##
5
+ # Memory Cache Store
6
+ #
4
7
  class Memory
8
+ ##
9
+ # Initialize Memory Store with memory size
10
+ #
11
+ # ==== Examples
12
+ # Padrino.cache = Padrino::Cache::Store::Memory.new(10000)
13
+ # # or from your app
14
+ # set :cache, Padrino::Cache::Store::Memory.new(10000)
15
+ #
5
16
  def initialize(size = 5000)
6
17
  @size, @entries, @index = size, [], {}
7
18
  end
8
19
 
20
+ ##
21
+ # Return the a value for the given key
22
+ #
23
+ # ==== Examples
24
+ # # with MyApp.cache.set('records', records)
25
+ # MyApp.cache.get('records')
26
+ #
9
27
  def get(key)
10
28
  if @index.key?(key) and value = @index[key]
11
29
  expires_in, body = value
@@ -21,6 +39,14 @@ module Padrino
21
39
  end
22
40
  end
23
41
 
42
+ ##
43
+ # Set the value for a given key and optionally with an expire time.
44
+ # Default expiry is 86400.
45
+ #
46
+ # ==== Examples
47
+ # MyApp.cache.set('records', records)
48
+ # MyApp.cache.set('records', records, :expires_in => 30) # => 30 seconds
49
+ #
24
50
  def set(key, value, opts = nil)
25
51
  delete(key) if @index.key?(key)
26
52
  if opts && opts[:expires_in]
@@ -37,10 +63,25 @@ module Padrino
37
63
  end
38
64
  end
39
65
 
66
+ ##
67
+ # Delete the value for a given key
68
+ #
69
+ # ==== Examples
70
+ # # with: MyApp.cache.set('records', records)
71
+ # MyApp.cache.delete('records')
72
+ #
40
73
  def delete(key)
41
74
  @index.delete(key)
42
75
  end
43
76
 
77
+ ##
78
+ # Reinitialize your cache
79
+ #
80
+ # ==== Examples
81
+ # # with: MyApp.cache.set('records', records)
82
+ # MyApp.cache.flush
83
+ # MyApp.cache.get('records') # => nil
84
+ #
44
85
  def flush
45
86
  @index = Hash.new
46
87
  end
@@ -1,34 +1,72 @@
1
- begin
2
- require 'redis'
3
- rescue LoadError
4
- raise "You must install redis to use the Redis cache store backend"
5
- end
6
-
7
1
  module Padrino
8
2
  module Cache
9
3
  module Store
4
+ ##
5
+ # Redis Cache Store
6
+ #
10
7
  class Redis
11
- def initialize(*args)
12
- @backend = ::Redis.new(*args)
8
+ ##
9
+ # Initialize Redis store with client connection.
10
+ #
11
+ # ==== Examples
12
+ # Padrino.cache = Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
13
+ # # or from your app
14
+ # set :cache, Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
15
+ #
16
+ def initialize(client)
17
+ @backend = client
13
18
  end
14
19
 
20
+ ##
21
+ # Return the a value for the given key
22
+ #
23
+ # ==== Examples
24
+ # # with MyApp.cache.set('records', records)
25
+ # MyApp.cache.get('records')
26
+ #
15
27
  def get(key)
16
- @backend.get(key)
28
+ code = @backend.get(key)
29
+ Marshal.load(code) if code.present?
17
30
  end
18
31
 
32
+ ##
33
+ # Set the value for a given key and optionally with an expire time
34
+ # Default expiry is 86400.
35
+ #
36
+ # ==== Examples
37
+ # MyApp.cache.set('records', records)
38
+ # MyApp.cache.set('records', records, :expires_in => 30) # => 30 seconds
39
+ #
19
40
  def set(key, value, opts = nil)
41
+ value = Marshal.dump(value) if value
20
42
  if opts && opts[:expires_in]
21
43
  expires_in = opts[:expires_in].to_i
44
+ expires_in = expires_in if expires_in < EXPIRES_EDGE
22
45
  @backend.setex(key, expires_in, value)
23
46
  else
24
47
  @backend.set(key, value)
25
48
  end
26
49
  end
27
50
 
51
+ ##
52
+ # Delete the value for a given key
53
+ #
54
+ # ==== Examples
55
+ # # with: MyApp.cache.set('records', records)
56
+ # MyApp.cache.delete('records')
57
+ #
28
58
  def delete(key)
29
59
  @backend.del(key)
30
60
  end
31
61
 
62
+ ##
63
+ # Reinitialize your cache
64
+ #
65
+ # ==== Examples
66
+ # # with: MyApp.cache.set('records', records)
67
+ # MyApp.cache.flush
68
+ # MyApp.cache.get('records') # => nil
69
+ #
32
70
  def flush
33
71
  @backend.flushdb
34
72
  end
data/test/helper.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  ENV['PADRINO_ENV'] = 'test'
2
- PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
2
+ PADRINO_ROOT = File.dirname(__FILE__) unless defined?(PADRINO_ROOT)
3
3
 
4
4
  require File.expand_path('../../../load_paths', __FILE__)
5
5
  require File.join(File.dirname(__FILE__), '..', '..', 'padrino-core', 'test', 'helper')
@@ -55,15 +55,17 @@ class TestPadrinoCache < Test::Unit::TestCase
55
55
  mock_app do
56
56
  register Padrino::Cache
57
57
  enable :caching
58
- get('/foo', :cache => proc{|req| "cached"}){ 'test' }
59
- get('/bar', :cache => proc{|req| "cached"}){ halt 500 }
58
+ get('/foo', :cache => true){ cache_key :foo; 'foo' }
59
+ get('/bar', :cache => true){ cache_key :bar; 'bar' }
60
60
  end
61
61
  get "/foo"
62
62
  assert_equal 200, status
63
- assert_equal 'test', body
63
+ assert_equal 'foo', body
64
+ assert_equal 'foo', @app.cache.get(:foo)
64
65
  get "/bar"
65
66
  assert_equal 200, status
66
- assert_equal 'test', body
67
+ assert_equal 'bar', body
68
+ assert_equal 'bar', @app.cache.get(:bar)
67
69
  end
68
70
 
69
71
  should 'delete based on urls' do
data/test/test_stores.rb CHANGED
@@ -1,61 +1,97 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/helper')
2
2
 
3
+ class Foo
4
+ def bar; "bar"; end
5
+ end
6
+
3
7
  COMMON_TESTS = <<-HERE_DOC
4
- should 'set and get a value' do
5
- @cache.set('val', 'test')
6
- assert_equal 'test', @cache.get('val')
8
+ should 'set and get an object' do
9
+ Padrino.cache.set('val', Foo.new)
10
+ assert_equal "bar", Padrino.cache.get('val').bar
11
+ end
12
+
13
+ should 'set ang get a nil value' do
14
+ Padrino.cache.set('val', nil)
15
+ assert_equal nil, Padrino.cache.get('val')
16
+ end
17
+
18
+ should 'set and get a raw value' do
19
+ Padrino.cache.set('val', 'foo')
20
+ assert_equal 'foo', Padrino.cache.get('val')
7
21
  end
8
22
 
9
23
  should "return nil trying to get a value that doesn't exist" do
10
- assert_equal nil, @cache.get('test')
24
+ assert_equal nil, Padrino.cache.get('test')
11
25
  end
12
26
 
13
27
  should "set a value that expires" do
14
- @cache.set('val', 'test', :expires_in => 1)
15
- assert_equal 'test', @cache.get('val')
28
+ Padrino.cache.set('val', 'test', :expires_in => 1)
29
+ assert_equal 'test', Padrino.cache.get('val')
16
30
  sleep 2
17
- assert_equal nil, @cache.get('val')
31
+ assert_equal nil, Padrino.cache.get('val')
18
32
  end
19
33
 
20
34
  should 'delete a value' do
21
- @cache.set('val', 'test')
22
- assert_equal 'test', @cache.get('val')
23
- @cache.delete('val')
24
- assert_equal nil, @cache.get('val')
35
+ Padrino.cache.set('val', 'test')
36
+ assert_equal 'test', Padrino.cache.get('val')
37
+ Padrino.cache.delete('val')
38
+ assert_equal nil, Padrino.cache.get('val')
25
39
  end
26
40
  HERE_DOC
27
41
 
28
42
  begin
43
+ require 'memcached'
29
44
  # we're just going to assume memcached is running on the default port
30
- Padrino::Cache::Store::Memcache.new('127.0.0.1:11211', :exception_retry_limit => 1).set('ping','alive')
45
+ Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1)).set('ping','alive')
31
46
 
32
47
  class TestMemcacheStore < Test::Unit::TestCase
33
48
  def setup
34
- @cache = Padrino::Cache::Store::Memcache.new('127.0.0.1:11211', :exception_retry_limit => 1)
35
- @cache.flush
49
+ Padrino.cache = Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
50
+ Padrino.cache.flush
36
51
  end
37
52
 
38
53
  def teardown
39
- @cache.flush
54
+ Padrino.cache.flush
40
55
  end
41
56
 
42
57
  eval COMMON_TESTS
43
58
  end
44
- rescue
45
- warn "Skipping memcached tests"
59
+ rescue LoadError
60
+ warn "Skipping memcached with memcached library tests"
46
61
  end
47
62
 
63
+ begin
64
+ require 'dalli'
65
+ # we're just going to assume memcached is running on the default port
66
+ Padrino::Cache::Store::Memcache.new(::Dalli::Client.new('127.0.0.1:11211', :exception_retry_limit => 1).set('ping','alive'))
67
+
68
+ class TestMemcacheWithDalliStore < Test::Unit::TestCase
69
+ def setup
70
+ Padrino.cache = Padrino::Cache::Store::Memcache.new(::Dalli::Client.new('127.0.0.1:11211', :exception_retry_limit => 1))
71
+ Padrino.cache.flush
72
+ end
73
+
74
+ def teardown
75
+ Padrino.cache.flush
76
+ end
77
+
78
+ eval COMMON_TESTS
79
+ end
80
+ rescue LoadError
81
+ warn "Skipping memcached with dalli library tests"
82
+ end
48
83
 
49
84
  begin
50
- Padrino::Cache::Store::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0).set('ping','alive')
85
+ require 'redis'
86
+ Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0).set('ping','alive'))
51
87
  class TestRedisStore < Test::Unit::TestCase
52
88
  def setup
53
- @cache = Padrino::Cache::Store::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0)
54
- @cache.flush
89
+ Padrino.cache = Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
90
+ Padrino.cache.flush
55
91
  end
56
92
 
57
93
  def teardown
58
- @cache.flush
94
+ Padrino.cache.flush
59
95
  end
60
96
 
61
97
  eval COMMON_TESTS
@@ -68,11 +104,11 @@ class TestFileStore < Test::Unit::TestCase
68
104
  def setup
69
105
  @apptmp = "#{Dir.tmpdir}/padrino-tests/#{UUID.new.generate}"
70
106
  FileUtils.mkdir_p(@apptmp)
71
- @cache = Padrino::Cache::Store::File.new(@apptmp)
107
+ Padrino.cache = Padrino::Cache::Store::File.new(@apptmp)
72
108
  end
73
109
 
74
110
  def teardown
75
- @cache.flush
111
+ Padrino.cache.flush
76
112
  end
77
113
 
78
114
  eval COMMON_TESTS
@@ -80,18 +116,18 @@ end
80
116
 
81
117
  class TestInMemoryStore < Test::Unit::TestCase
82
118
  def setup
83
- @cache = Padrino::Cache::Store::Memory.new(50)
119
+ Padrino.cache = Padrino::Cache::Store::Memory.new(50)
84
120
  end
85
121
 
86
122
  def teardown
87
- @cache.flush
123
+ Padrino.cache.flush
88
124
  end
89
125
 
90
126
  eval COMMON_TESTS
91
127
 
92
128
  should "only store 50 entries" do
93
- 51.times { |i| @cache.set(i.to_s, i.to_s) }
94
- assert_equal nil, @cache.get('0')
95
- assert_equal '1', @cache.get('1')
129
+ 51.times { |i| Padrino.cache.set(i.to_s, i.to_s) }
130
+ assert_equal nil, Padrino.cache.get('0')
131
+ assert_equal '1', Padrino.cache.get('1')
96
132
  end
97
- end
133
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: padrino-cache
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 24
10
- version: 0.9.24
9
+ - 25
10
+ version: 0.9.25
11
11
  platform: ruby
12
12
  authors:
13
13
  - Padrino Team
@@ -18,7 +18,7 @@ autorequire:
18
18
  bindir: bin
19
19
  cert_chain: []
20
20
 
21
- date: 2011-04-28 00:00:00 +02:00
21
+ date: 2011-04-27 00:00:00 +02:00
22
22
  default_executable:
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
@@ -29,12 +29,12 @@ dependencies:
29
29
  requirements:
30
30
  - - "="
31
31
  - !ruby/object:Gem::Version
32
- hash: 11
32
+ hash: 9
33
33
  segments:
34
34
  - 0
35
35
  - 9
36
- - 24
37
- version: 0.9.24
36
+ - 25
37
+ version: 0.9.25
38
38
  type: :runtime
39
39
  version_requirements: *id001
40
40
  description: Caching support for memcached, page and fragment
@@ -82,7 +82,6 @@ files:
82
82
  - test/tmp/#<class:0x0000010086b838>/cache/%2Ffoo
83
83
  - test/tmp/#<class:0x0000010086e588>/cache/%2Ffoo
84
84
  - test/tmp/#<class:0x00000100871918>/cache/%2Ffoo
85
- - test/tmp/#<class:0x00000100882218>/cache/%2Ffoo
86
85
  - test/tmp/#<class:0x0000010089b560>/cache/%2Ffoo
87
86
  - test/tmp/#<class:0x000001008c3dd0>/cache/test
88
87
  - test/tmp/#<class:0x000001009036d8>/cache/%2Ffoo
@@ -94,7 +93,6 @@ files:
94
93
  - test/tmp/#<class:0x00000100954b00>/cache/bar
95
94
  - test/tmp/#<class:0x00000100954b00>/cache/foo
96
95
  - test/tmp/#<class:0x0000010095f5a0>/cache/%2Ffoo%2F12
97
- - test/tmp/#<class:0x00000100961058>/cache/%2Ffoo%2F12
98
96
  - test/tmp/#<class:0x00000100967ae8>/cache/test
99
97
  - test/tmp/#<class:0x0000010096afe0>/cache/%2Ffoo
100
98
  - test/tmp/#<class:0x00000100973ff0>/cache/test
@@ -124,7 +122,6 @@ files:
124
122
  - test/tmp/#<class:0x00000100b458d8>/cache/%2Ffoo
125
123
  - test/tmp/#<class:0x00000100b480d8>/cache/%2Ffoo
126
124
  - test/tmp/#<class:0x00000100b53b68>/cache/%2Ffoo%2F12
127
- - test/tmp/#<class:0x00000100b66fd8>/cache/%2Ffoo
128
125
  - test/tmp/#<class:0x00000100b6d130>/cache/test
129
126
  - test/tmp/#<class:0x00000100b7bd70>/cache/test
130
127
  - test/tmp/#<class:0x00000100b7ebb0>/cache/test
@@ -178,27 +175,21 @@ files:
178
175
  - test/tmp/#<class:0x000001011812b0>/cache/foo
179
176
  - test/tmp/#<class:0x000001011868f0>/cache/%2Ffoo%2F12
180
177
  - test/tmp/#<class:0x000001011d55b8>/cache/%2Ffoo
181
- - test/tmp/#<class:0x000001011f4e68>/cache/bar
182
- - test/tmp/#<class:0x000001011f4e68>/cache/foo
183
178
  - test/tmp/#<class:0x000001011fbf88>/cache/%2Ffoo
184
179
  - test/tmp/#<class:0x0000010121ec40>/cache/test
185
180
  - test/tmp/#<class:0x000001012285b0>/cache/test
186
181
  - test/tmp/#<class:0x0000010126c6c0>/cache/test
187
182
  - test/tmp/#<class:0x00000101294378>/cache/%2Ffoo
188
- - test/tmp/#<class:0x000001012ac7c0>/cache/%2Ffoo
189
183
  - test/tmp/#<class:0x000001012fb2a8>/cache/%2Ffoo
190
184
  - test/tmp/#<class:0x00000101300000>/cache/test
191
185
  - test/tmp/#<class:0x000001013029b8>/cache/%2Ffoo
192
186
  - test/tmp/#<class:0x000001013225b0>/cache/%2Ffoo
193
187
  - test/tmp/#<class:0x00000101348bc0>/cache/test
194
- - test/tmp/#<class:0x00000101363448>/cache/test
195
188
  - test/tmp/#<class:0x000001013c8cf8>/cache/test
196
189
  - test/tmp/#<class:0x000001013f3b38>/cache/test
197
190
  - test/tmp/#<class:0x00000101400a40>/cache/%2Ffoo
198
191
  - test/tmp/#<class:0x000001014130f0>/cache/%2Ffoo
199
- - test/tmp/#<class:0x000001014a5540>/cache/%2Ffoo
200
192
  - test/tmp/#<class:0x000001014d8f08>/cache/test
201
- - test/tmp/#<class:0x00000101588070>/cache/test
202
193
  - test/tmp/#<class:0x00000101815e50>/cache/%2Ffoo
203
194
  - test/tmp/#<class:0x0000010185bc70>/cache/test
204
195
  - test/tmp/#<class:0x0000010186eca8>/cache/cached
@@ -329,8 +320,6 @@ files:
329
320
  - test/tmp/#<class:0x107ddff76>/cache/test
330
321
  - test/tmp/#<class:0x108a77250>/cache/%2Ffoo
331
322
  - test/tmp/#<class:0x108aac748>/cache/test
332
- - test/tmp/#<class:0x108aacd38>/cache/bar
333
- - test/tmp/#<class:0x108aacd38>/cache/foo
334
323
  - test/tmp/#<class:0x108aeb0b0>/cache/test
335
324
  - test/tmp/#<class:0x108af8850>/cache/test
336
325
  - test/tmp/#<class:0x108b0a320>/cache/%2Ffoo
@@ -346,13 +335,11 @@ files:
346
335
  - test/tmp/#<class:0x108b21de0>/cache/foo
347
336
  - test/tmp/#<class:0x108b35778>/cache/%2Ffoo
348
337
  - test/tmp/#<class:0x108b374d8>/cache/%2Ffoo
349
- - test/tmp/#<class:0x108b39a30>/cache/%2Ffoo
350
338
  - test/tmp/#<class:0x108b3a908>/cache/%2Ffoo
351
339
  - test/tmp/#<class:0x108b3b4e8>/cache/%2Ffoo
352
340
  - test/tmp/#<class:0x108b3b588>/cache/%2Ffoo
353
341
  - test/tmp/#<class:0x108b3b718>/cache/%2Ffoo
354
342
  - test/tmp/#<class:0x108b46640>/cache/test
355
- - test/tmp/#<class:0x108b51e00>/cache/%2Ffoo
356
343
  - test/tmp/#<class:0x108b5fdc0>/cache/test
357
344
  - test/tmp/#<class:0x108b60c98>/cache/bar
358
345
  - test/tmp/#<class:0x108b60c98>/cache/foo
@@ -372,7 +359,6 @@ files:
372
359
  - test/tmp/#<class:0x108b9c798>/cache/%2Ffoo
373
360
  - test/tmp/#<class:0x108b9c838>/cache/%2Ffoo
374
361
  - test/tmp/#<class:0x108b9c9c8>/cache/%2Ffoo
375
- - test/tmp/#<class:0x108b9f4c0>/cache/%2Ffoo%2F12
376
362
  - test/tmp/#<class:0x108ba3598>/cache/%2Ffoo
377
363
  - test/tmp/#<class:0x108ba8750>/cache/%2Ffoo
378
364
  - test/tmp/#<class:0x108ba8930>/cache/test
@@ -393,7 +379,6 @@ files:
393
379
  - test/tmp/#<class:0x108bd1e48>/cache/test
394
380
  - test/tmp/#<class:0x108bd1ee8>/cache/test
395
381
  - test/tmp/#<class:0x108bd2078>/cache/test
396
- - test/tmp/#<class:0x108bd6330>/cache/%2Ffoo
397
382
  - test/tmp/#<class:0x108bd8720>/cache/%2Ffoo
398
383
  - test/tmp/#<class:0x108bd9210>/cache/%2Ffoo
399
384
  - test/tmp/#<class:0x108bd9cb0>/cache/%2Ffoo
@@ -422,7 +407,6 @@ files:
422
407
  - test/tmp/#<class:0x108c14ec8>/cache/test
423
408
  - test/tmp/#<class:0x108c166b0>/cache/test
424
409
  - test/tmp/#<class:0x108c16cc8>/cache/test
425
- - test/tmp/#<class:0x108c20ac0>/cache/test
426
410
  - test/tmp/#<class:0x108c23b30>/cache/test
427
411
  - test/tmp/#<class:0x108c24008>/cache/test
428
412
  - test/tmp/#<class:0x108c24ff8>/cache/%2Ffoo
@@ -443,7 +427,6 @@ files:
443
427
  - test/tmp/#<class:0x108c73a68>/cache/test
444
428
  - test/tmp/#<class:0x108c74b70>/cache/%2Ffoo%2F12
445
429
  - test/tmp/#<class:0x108c75430>/cache/test
446
- - test/tmp/#<class:0x108c84e30>/cache/%2Ffoo
447
430
  - test/tmp/#<class:0x108c85e20>/cache/bar
448
431
  - test/tmp/#<class:0x108c85e20>/cache/foo
449
432
  - test/tmp/#<class:0x108c87310>/cache/bar
@@ -477,7 +460,6 @@ files:
477
460
  - test/tmp/#<class:0x108cd5100>/cache/%2Ffoo
478
461
  - test/tmp/#<class:0x108cd6e10>/cache/%2Ffoo
479
462
  - test/tmp/#<class:0x108cd7798>/cache/%2Ffoo
480
- - test/tmp/#<class:0x108ced9d0>/cache/test
481
463
  - test/tmp/#<class:0x108cf3c68>/cache/%2Ffoo
482
464
  - test/tmp/#<class:0x108cf5b58>/cache/test
483
465
  - test/tmp/#<class:0x114dee6e2>/cache/%2Ffoo