tennpipes-memory 3.6.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6aa32adf8716a8e228a1bbdde8fcb6d6e551b8ae
4
+ data.tar.gz: f69505b074658b20a7e246ea8a54ed7b33ac74bc
5
+ SHA512:
6
+ metadata.gz: 7fa0da078e266bc68a5e96486d3537f29010e301a5c6acfda152a4283959ca44fcdbdc6e159b505716e09fbe494cb58831919ba73969c78d5a8558f42a744fed
7
+ data.tar.gz: 4f3b4d3ec1aeeedc9c9d63c7d8cb5c2a95d7427a2560d2e722482dbc2003985f525a3b0558f247f96510ca961714247b79cd7c76fc8d6b2f095cb692ceb99bae
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Tennpipes
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,346 @@
1
+ = Painless Page and Fragment Caching (tennpipes-memory)
2
+
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. Most popular key/value stores work out of the box. Take a look
9
+ at the [Moneta documentation](http://rubydoc.info/gems/moneta) for a list of all supported stores.
10
+
11
+ == Caching Quickstart
12
+
13
+ Tennpipes-cache can reduce the processing load on your site very effectively
14
+ with minimal configuration.
15
+
16
+ By default, the component caches pages in a file store at <tt>tmp/cache</tt>
17
+ within your project root. Entries in this store correspond directly
18
+ to the request issued to your server. In other words, responses are
19
+ cached based on request URL, with one cache entry per URL.
20
+
21
+ This behavior is referred to as "page-level caching." If this strategy meets
22
+ your needs, you can enable it very easily:
23
+
24
+ # Basic, page-level caching
25
+ class SimpleApp < Tennpipes::Application
26
+ register Tennpipes::Cache
27
+ enable :caching
28
+
29
+ get '/foo', :cache => true do
30
+ expires 30 # expire cached version at least every 30 seconds
31
+ 'Hello world'
32
+ end
33
+ end
34
+
35
+ You can also cache on a controller-wide basis:
36
+
37
+ # Controller-wide caching example
38
+ class SimpleApp < Tennpipes::Application
39
+ register Tennpipes::Cache
40
+ enable :caching
41
+
42
+ get '/' do
43
+ 'Hello world'
44
+ end
45
+
46
+ # Requests to routes within '/admin'
47
+ controller '/admin', :cache => true do
48
+ expires 60
49
+
50
+ get '/foo' do
51
+ 'Url is /admin/foo'
52
+ end
53
+
54
+ get '/bar' do
55
+ 'Url is /admin/bar'
56
+ end
57
+
58
+ post '/baz' do # We cache only GET and HEAD request
59
+ 'This will not be cached'
60
+ end
61
+ end
62
+ end
63
+
64
+ You can also provide a custom <tt>cache_key</tt> in any route:
65
+
66
+ class SimpleApp < Tennpipes::Application
67
+ register Tennpipes::Cache
68
+ enable :caching
69
+
70
+ get '/post/:id', :cache => true do
71
+ @post = Post.find(params[:id])
72
+ cache_key :my_name
73
+ end
74
+ end
75
+
76
+ In this way you can manually expire cache with CachedApp.cache.delete(:my_name)
77
+ for example from the Post model after an update.
78
+
79
+ If you specify <tt>:cache => true</tt> but do not invoke <tt>expires</tt>,
80
+ the response will be cached indefinitely. Most of the time, you will want to
81
+ specify the expiry of a cache entry by <tt>expires</tt>. Even a relatively
82
+ low value--1 or 2 seconds--can greatly increase application efficiency, especially
83
+ when enabled on a very active part of your domain.
84
+
85
+ == Helpers
86
+
87
+ When an application registers tennpipes-memory, it gains access to several helper
88
+ methods. These methods are used according to your caching strategy, so they are
89
+ explained here likewise--by functionality.
90
+
91
+ As with all code optimization, you may want to start simply (at "page level"),
92
+ and continue if necessary into sub-page (or "fragment level" ) caching. There
93
+ is no one way to approach caching, but it's always good to avoid complexity
94
+ until you need it. Start at the page level and see if it works for you.
95
+
96
+ The tennpipes-memory helpers are made available to your application thusly:
97
+
98
+ # Enable caching
99
+ class CachedApp < Tennpipes::Application
100
+ register Tennpipes::Cache # includes helpers
101
+ enable :caching # turns on caching
102
+
103
+ # ... controllers/routes ...
104
+ end
105
+
106
+ === Page Caching
107
+
108
+ As described above in the "Caching Quickstart" section, page caching is very
109
+ easy to integrate into your application. To turn it on, simply provide the
110
+ <tt>:cache => true</tt> option on either a controller or one of its routes.
111
+ By default, cached content is persisted with a "file store"--that is, in a
112
+ subdirectory of your application root.
113
+
114
+ ==== <tt>expires( seconds )</tt>
115
+
116
+ This helper is used within a controller or route to indicate how often cached
117
+ <em>page-level</em> content should persist in the cache.
118
+
119
+ After <tt>seconds</tt> seconds have passed, content previously cached will
120
+ be discarded and re-rendered. Code associated with that route will <em>not</em>
121
+ be executed; rather, its previous output will be sent to the client with a
122
+ 200 OK status code.
123
+
124
+ # Setting content expiry time
125
+ class CachedApp < Tennpipes::Application
126
+ register Tennpipes::Cache # includes helpers
127
+ enable :caching # turns on caching
128
+
129
+ controller '/blog', :cache => true do
130
+ expires 15
131
+
132
+ get '/entries' do
133
+ 'just broke up eating twinkies lol'
134
+ end
135
+ end
136
+ end
137
+
138
+ Note that the "latest" method call to <tt>expires</tt> determines its value: if
139
+ called within a route, as opposed to a controller definition, the route's
140
+ value will be assumed.
141
+
142
+ === Fragment Caching
143
+
144
+ Whereas page-level caching, described in the first section of this document, works by
145
+ grabbing the entire output of a route, fragment caching gives the developer fine-grained
146
+ control of what gets cached. This type of caching occurs at whatever level you choose.
147
+
148
+ Possible uses for fragment caching might include:
149
+
150
+ * a 'feed' of some items on a page
151
+ * output fetched (by proxy) from an API on a third-party site
152
+ * parts of your page which are largely static/do not need re-rendering every request
153
+ * any output which is expensive to render
154
+
155
+ ==== <tt>cache( key, opts, &block )</tt>
156
+
157
+ This helper is used anywhere in your application you would like to associate a fragment
158
+ to be cached. It can be used in within a route:
159
+
160
+ # Caching a fragment
161
+ class MyTweets < Tennpipes::Application
162
+ register Tennpipes::Cache # includes helpers
163
+ enable :caching # turns on caching
164
+
165
+ controller '/tweets' do
166
+ get :feed, :map => '/:username' do
167
+ username = params[:username]
168
+
169
+ @feed = cache( "feed_for_#{username}", :expires => 3 ) do
170
+ @tweets = Tweet.all( :username => username )
171
+ render 'partials/feedcontent'
172
+ end
173
+
174
+ # Below outputs @feed somewhere in its markup
175
+ render 'feeds/show'
176
+ end
177
+ end
178
+ end
179
+
180
+ This example adds a key to the cache of format <tt>feed_for_#{username}</tt> which
181
+ contains the contents of that user's feed. Any subsequent action within the next 3 seconds
182
+ will fetch the pre-rendered version of <tt>feed_for_#{username}</tt> from the cache
183
+ instead of re-rendering it. The rest of the page code will, however, be re-executed.
184
+
185
+ Note that any other action will reference the same content if it uses the same key:
186
+
187
+ # Multiple routes sharing the same cached fragment
188
+ class MyTweets < Tennpipes::Application
189
+ register Tennpipes::Cache # includes helpers
190
+ enable :caching # turns on caching
191
+
192
+ controller :tweets do
193
+ get :feed, :map => '/:username' do
194
+ username = params[:username]
195
+
196
+ @feed = cache( "feed_for_#{username}", :expires => 3 ) do
197
+ @tweets = Tweet.all( :username => username )
198
+ render 'partials/feedcontent'
199
+ end
200
+
201
+ # Below outputs @feed somewhere in its markup
202
+ render 'feeds/show'
203
+ end
204
+
205
+ get :mobile_feed, :map => '/:username.iphone' do
206
+ username = params[:username]
207
+
208
+ @feed = cache( "feed_for_#{username}", :expires => 3 ) do
209
+ @tweets = Tweet.all( :username => username )
210
+ render 'partials/feedcontent'
211
+ end
212
+
213
+ render 'feeds/show.iphone'
214
+ end
215
+ end
216
+ end
217
+
218
+ The <tt>opts</tt> argument is actually passed to the underlying store. The stores support the <tt>:expires</tt> option out of the box or
219
+ are enhanced by Moneta to support it.
220
+
221
+ Finally, to DRY up things a bit, we might do:
222
+
223
+ # Multiple routes sharing the same cached fragment
224
+ class MyTweets < Tennpipes::Application
225
+ register Tennpipes::Cache # includes helpers
226
+ enable :caching # turns on caching
227
+
228
+ controller :tweets do
229
+ # This works because all routes in this controller specify :username
230
+ before do
231
+ @feed = cache( "feed_for_#{params[:username]}", :expires => 3 ) do
232
+ @tweets = Tweet.all( :username => params[:username] )
233
+ render 'partials/feedcontent'
234
+ end
235
+ end
236
+
237
+ get :feed, :map => '/:username' do
238
+ render 'feeds/show'
239
+ end
240
+
241
+ get :mobile_feed, :map => '/:username.iphone' do
242
+ render 'feeds/show.iphone'
243
+ end
244
+ end
245
+ end
246
+
247
+ Of course, this example assumes the markup generated by rendering
248
+ <tt>partials/feedcontent</tt> would be suitable for both feed formats. This may or
249
+ may not be the case in your application, but the principle applies: fragments
250
+ are shared between all code which accesses the cache using the same key.
251
+
252
+ == Caching Store
253
+
254
+ You can set a global caching option or a per app caching options.
255
+
256
+ === Global Caching Options
257
+
258
+ Tennpipes.cache = Tennpipes::Cache.new(:LRUHash) # default choice
259
+ Tennpipes.cache = Tennpipes::Cache.new(:File, :dir => Tennpipes.root('tmp', app_name.to_s, 'cache')) # Keeps cached values in file
260
+ Tennpipes.cache = Tennpipes::Cache.new(:Memcached) # Uses default server at localhost
261
+ Tennpipes.cache = Tennpipes::Cache.new(:Memcached, :server => '127.0.0.1:11211', :exception_retry_limit => 1)
262
+ Tennpipes.cache = Tennpipes::Cache.new(:Memcached, :backend => memcached_or_dalli_instance)
263
+ Tennpipes.cache = Tennpipes::Cache.new(:Redis) # Uses default server at localhost
264
+ Tennpipes.cache = Tennpipes::Cache.new(:Redis, :host => '127.0.0.1', :port => 6379, :db => 0)
265
+ Tennpipes.cache = Tennpipes::Cache.new(:Redis, :backend => redis_instance)
266
+ Tennpipes.cache = Tennpipes::Cache.new(:Mongo) # Uses default server at localhost
267
+ Tennpipes.cache = Tennpipes::Cache.new(:Mongo, :backend => mongo_client_instance)
268
+
269
+ You can manage your cache from anywhere in your app:
270
+
271
+ Tennpipes.cache['val'] = 'test'
272
+ Tennpipes.cache['val'] # => 'test'
273
+ Tennpipes.cache.delete('val')
274
+ Tennpipes.cache.clear
275
+
276
+ The Tennpipes cache constructor `Tennpipes::Cache.new` calls `Moneta.new` to create a cache instance. Please refer to the [Moneta documentation](http://rubydoc.info/gems/moneta) if you
277
+ have special requirements, for example if you want to configure the marshalling mechanism or use a more exotic backend.
278
+
279
+ ==== Application Caching Options
280
+
281
+ set :cache, Tennpipes::Cache.new(:LRUHash)
282
+ set :cache, Tennpipes::Cache.new(:Memcached)
283
+ set :cache, Tennpipes::Cache.new(:Redis)
284
+ set :cache, Tennpipes::Cache.new(:File, :dir => Tennpipes.root('tmp', app_name.to_s, 'cache')) # default choice
285
+
286
+ You can manage your cache from anywhere in your app:
287
+
288
+ MyApp.cache['val'] = 'test'
289
+ MyApp.cache['val'] # => 'test'
290
+ MyApp.cache.delete('val')
291
+ MyApp.cache.clear
292
+
293
+ == Expiring Cached Content
294
+
295
+ In certain circumstances, cached content becomes stale. The <tt>expire</tt>
296
+ helper removes content associated with a key or keys, which your app is then
297
+ free to re-generate.
298
+
299
+ === <tt>expire( *key )</tt>
300
+
301
+ ==== Fragment-level expiration
302
+
303
+ Using the example above of a tweet server, let's suppose our users have a
304
+ tendency to post things they quickly regret. When we query our database
305
+ for new tweets, let's check to see if any have been deleted. If so, we'll
306
+ do our user a favor and instantly re-render the feed.
307
+
308
+ # Expiring fragment-level cached content
309
+ class MyTweets < Tennpipes::Application
310
+ register Tennpipes::Cache # includes helpers
311
+ enable :caching # turns on caching
312
+ enable :session # we'll use this to store last time visited
313
+
314
+ COMPANY_FOUNDING = Time.utc( 2010, "April" )
315
+
316
+ controller :tweets do
317
+ get :feed, :map => '/:username' do
318
+ last_visit = session[:last_visit] || params[:since] || COMPANY_FOUNDING
319
+
320
+ username = params[:username]
321
+ @tweets = Tweet.since( last_visit, :username => username ).limit( 100 )
322
+
323
+ expire( "feed since #{last_visit}" ) if @tweets.any? { |t| t.deleted_since?( last_visit ) }
324
+
325
+ session[:last_visit] = Time.now
326
+ @feed = cache( "feed since #{last_visit}", :expires => 60 ) do
327
+ @tweets = @tweets.find_all { |t| !t.deleted? }
328
+ render 'partials/feedcontent'
329
+ end
330
+
331
+ render 'feeds/show'
332
+ end
333
+ end
334
+ end
335
+
336
+ Normally, this example will only re-cache feed content every 60 seconds,
337
+ but it will do so immediately if any tweets have been deleted.
338
+
339
+ ==== Page-level expiration
340
+
341
+ Page-level expiration works exactly like the example above--by using
342
+ <tt>expire</tt> in your controller. The key is typically <tt>env['PATH_INFO']</tt>.
343
+
344
+ == Copyright
345
+
346
+ Copyright (c) 2011-2013 Tennpipes. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../gem_rake_helper')
@@ -0,0 +1,125 @@
1
+ require 'fileutils' unless defined?(FileUtils)
2
+ require 'tennpipes-base'
3
+ require 'tennpipes-helper'
4
+ FileSet.glob_require('tennpipes-memory/{helpers}/*.rb', __FILE__)
5
+ require 'moneta'
6
+
7
+ module Tennpipes
8
+ class << self
9
+ ##
10
+ # Returns the caching engine.
11
+ #
12
+ # @example
13
+ # # with: Tennpipes.cache = Tennpipes::Cache.new(:File, :dir => /my/cache/path)
14
+ # Tennpipes.cache['val'] = 'test'
15
+ # Tennpipes.cache['val'] # => 'test'
16
+ # Tennpipes.cache.delete('val')
17
+ # Tennpipes.cache.clear
18
+ #
19
+ def cache
20
+ @_cache
21
+ end
22
+
23
+ ##
24
+ # Set the caching engine.
25
+ #
26
+ # @param value
27
+ # Instance of Moneta store
28
+ #
29
+ # @example
30
+ # Tennpipes.cache = Tennpipes::Cache.new(:LRUHash) # default choice
31
+ # Tennpipes.cache = Tennpipes::Cache.new(:File, :dir => Tennpipes.root('tmp', app_name.to_s, 'cache')) # Keeps cached values in file
32
+ # Tennpipes.cache = Tennpipes::Cache.new(:Memcached) # Uses default server at localhost
33
+ # Tennpipes.cache = Tennpipes::Cache.new(:Memcached, :server => '127.0.0.1:11211', :exception_retry_limit => 1)
34
+ # Tennpipes.cache = Tennpipes::Cache.new(:Memcached, :backend => memcached_or_dalli_instance)
35
+ # Tennpipes.cache = Tennpipes::Cache.new(:Redis) # Uses default server at localhost
36
+ # Tennpipes.cache = Tennpipes::Cache.new(:Redis, :host => '127.0.0.1', :port => 6379, :db => 0)
37
+ # Tennpipes.cache = Tennpipes::Cache.new(:Redis, :backend => redis_instance)
38
+ # Tennpipes.cache = Tennpipes::Cache.new(:Mongo) # Uses default server at localhost
39
+ # Tennpipes.cache = Tennpipes::Cache.new(:Mongo, :backend => mongo_client_instance)
40
+ #
41
+ # # You can manage your cache from anywhere in your app:
42
+ #
43
+ # Tennpipes.cache['val'] = 'test'
44
+ # Tennpipes.cache['val'] # => 'test'
45
+ # Tennpipes.cache.delete('val')
46
+ # Tennpipes.cache.clear
47
+ #
48
+ def cache=(value)
49
+ @_cache= value
50
+ end
51
+ end
52
+
53
+ ##
54
+ # This component enables caching of an application's response contents on
55
+ # both page- and fragment-levels. Output cached in this manner is
56
+ # persisted, until it expires or is actively expired, in a configurable store
57
+ # of your choosing. Several common caching stores are supported out of the box.
58
+ #
59
+ module Cache
60
+ class << self
61
+ ##
62
+ # Register these helpers:
63
+ #
64
+ # Tennpipes::Cache::Helpers::ObjectCache
65
+ # Tennpipes::Cache::Helpers::CacheStore
66
+ # Tennpipes::Cache::Helpers::Fragment
67
+ # Tennpipes::Cache::Helpers::Page
68
+ #
69
+ # for Tennpipes::Application.
70
+ #
71
+ # By default we use FileStore as showed below:
72
+ #
73
+ # set :cache, Tennpipes::Cache.new(:File, :dir => Tennpipes.root('tmp', app_name.to_s, 'cache'))
74
+ #
75
+ # However, you can also change the file store easily in your app.rb:
76
+ #
77
+ # set :cache, Tennpipes::Cache.new(:LRUHash) # Keeps cached values in memory
78
+ # set :cache, Tennpipes::Cache.new(:Memcached) # Uses default server at localhost
79
+ # set :cache, Tennpipes::Cache.new(:Memcached, '127.0.0.1:11211', :exception_retry_limit => 1)
80
+ # set :cache, Tennpipes::Cache.new(:Memcached, :backend => memcached_or_dalli_instance)
81
+ # set :cache, Tennpipes::Cache.new(:Redis) # Uses default server at localhost
82
+ # set :cache, Tennpipes::Cache.new(:Redis, :host => '127.0.0.1', :port => 6379, :db => 0)
83
+ # set :cache, Tennpipes::Cache.new(:Redis, :backend => redis_instance)
84
+ # set :cache, Tennpipes::Cache.new(:Mongo) # Uses default server at localhost
85
+ # set :cache, Tennpipes::Cache.new(:Mongo, :backend => mongo_client_instance)
86
+ # set :cache, Tennpipes::Cache.new(:File, :dir => Tennpipes.root('tmp', app_name.to_s, 'cache')) # default choice
87
+ #
88
+ # You can manage your cache from anywhere in your app:
89
+ #
90
+ # MyApp.cache['val'] = 'test'
91
+ # MyApp.cache['val'] # => 'test'
92
+ # MyApp.cache.delete('val')
93
+ # MyApp.cache.clear
94
+ #
95
+ def registered(app)
96
+ app.helpers Tennpipes::Cache::Helpers::ObjectCache
97
+ app.helpers Tennpipes::Cache::Helpers::CacheStore
98
+ app.helpers Tennpipes::Cache::Helpers::Fragment
99
+ app.helpers Tennpipes::Cache::Helpers::Page
100
+ unless app.respond_to?(:cache)
101
+ cache_dir = Tennpipes.root('tmp', defined?(app.app_name) ? app.app_name.to_s : '', 'cache')
102
+ app.set :cache, Tennpipes::Cache.new(:File, :dir => cache_dir)
103
+ end
104
+ app.disable :caching unless app.respond_to?(:caching)
105
+ included(app)
106
+ end
107
+
108
+ def included(base)
109
+ base.extend Tennpipes::Cache::Helpers::Page::ClassMethods
110
+ end
111
+
112
+ def tennpipes_route_added(route, verb, path, args, options, block)
113
+ Tennpipes::Cache::Helpers::Page.tennpipes_route_added(route, verb, path, args, options, block)
114
+ end
115
+ end
116
+
117
+ def self.new(name, options = {})
118
+ # Activate expiration by default
119
+ options[:expires] = true unless options.include?(:expires)
120
+ Moneta.new(name, options)
121
+ end
122
+
123
+ Tennpipes.cache = Tennpipes::Cache.new(:LRUHash)
124
+ end
125
+ end