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 +332 -2
- data/lib/padrino-cache.rb +70 -8
- data/lib/padrino-cache/helpers/cache_store.rb +3 -3
- data/lib/padrino-cache/helpers/fragment.rb +42 -3
- data/lib/padrino-cache/helpers/page.rb +60 -6
- data/lib/padrino-cache/store.rb +1 -1
- data/lib/padrino-cache/store/file.rb +49 -11
- data/lib/padrino-cache/store/memcache.rb +45 -8
- data/lib/padrino-cache/store/memory.rb +41 -0
- data/lib/padrino-cache/store/redis.rb +47 -9
- data/test/helper.rb +1 -1
- data/test/test_padrino_cache.rb +6 -4
- data/test/test_stores.rb +65 -29
- metadata +7 -25
data/README.rdoc
CHANGED
|
@@ -1,7 +1,337 @@
|
|
|
1
1
|
= Painless Page and Fragment Caching (padrino-cache)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
7
|
+
class << self
|
|
8
8
|
##
|
|
9
|
-
#
|
|
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
|
-
|
|
12
|
-
|
|
18
|
+
def cache
|
|
19
|
+
@_cache
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Set the caching engine
|
|
13
24
|
#
|
|
14
|
-
#
|
|
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 :
|
|
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 #
|
|
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
|
-
|
|
7
|
+
settings.cache.delete(key)
|
|
8
8
|
else
|
|
9
|
-
|
|
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
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
13
|
-
|
|
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
|
|
69
|
+
if settings.caching?
|
|
70
|
+
began_at = Time.now
|
|
19
71
|
if @_last_expires_in
|
|
20
|
-
|
|
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
|
-
|
|
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
|
data/lib/padrino-cache/store.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
89
|
+
def path_for_key(key)
|
|
90
|
+
::File.join(@root, Rack::Utils.escape(key.to_s))
|
|
91
|
+
end
|
|
50
92
|
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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?
|
|
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')
|
data/test/test_padrino_cache.rb
CHANGED
|
@@ -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 =>
|
|
59
|
-
get('/bar', :cache =>
|
|
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 '
|
|
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 '
|
|
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
|
|
5
|
-
|
|
6
|
-
assert_equal
|
|
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,
|
|
24
|
+
assert_equal nil, Padrino.cache.get('test')
|
|
11
25
|
end
|
|
12
26
|
|
|
13
27
|
should "set a value that expires" do
|
|
14
|
-
|
|
15
|
-
assert_equal 'test',
|
|
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,
|
|
31
|
+
assert_equal nil, Padrino.cache.get('val')
|
|
18
32
|
end
|
|
19
33
|
|
|
20
34
|
should 'delete a value' do
|
|
21
|
-
|
|
22
|
-
assert_equal 'test',
|
|
23
|
-
|
|
24
|
-
assert_equal nil,
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
+
Padrino.cache = Padrino::Cache::Store::File.new(@apptmp)
|
|
72
108
|
end
|
|
73
109
|
|
|
74
110
|
def teardown
|
|
75
|
-
|
|
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
|
-
|
|
119
|
+
Padrino.cache = Padrino::Cache::Store::Memory.new(50)
|
|
84
120
|
end
|
|
85
121
|
|
|
86
122
|
def teardown
|
|
87
|
-
|
|
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|
|
|
94
|
-
assert_equal nil,
|
|
95
|
-
assert_equal '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:
|
|
4
|
+
hash: 9
|
|
5
5
|
prerelease:
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
8
|
- 9
|
|
9
|
-
-
|
|
10
|
-
version: 0.9.
|
|
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-
|
|
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:
|
|
32
|
+
hash: 9
|
|
33
33
|
segments:
|
|
34
34
|
- 0
|
|
35
35
|
- 9
|
|
36
|
-
-
|
|
37
|
-
version: 0.9.
|
|
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
|