tennpipes-memory 3.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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