padrino-cache 0.9.24 → 0.9.25

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.
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