benjaminjackson-sinatra-cache 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ ## OS STUFF
2
+ .DS_Store
3
+
4
+ ## TM SPECIFIC
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## PROJECT::GENERAL
9
+ *.sw?
10
+ coverage
11
+ rdoc
12
+ pkg
13
+ doc
14
+
15
+ ## PROJECT::SPECIFIC
16
+ spec/fixtures/public/*
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 kematzy
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,417 @@
1
+ = Sinatra::Cache
2
+
3
+ A Sinatra Extension that makes Page and Fragment Caching easy.
4
+
5
+
6
+ == IMPORTANT INFORMATION
7
+
8
+ <b>This is a completely rewritten extension that basically breaks all previous versions of it.</b>
9
+
10
+ So use with care! You have been warned ;-)
11
+
12
+ ----
13
+
14
+
15
+
16
+ With that said, on to the real stuff.
17
+
18
+
19
+ == Installation
20
+
21
+ # Add RubyGems.org (former Gemcutter) to your RubyGems sources
22
+ $ gem sources -a http://rubygems.org
23
+
24
+ $ (sudo)? gem install sinatra-cache
25
+
26
+ == Dependencies
27
+
28
+ This Gem depends upon the following:
29
+
30
+ === Runtime:
31
+
32
+ * sinatra ( >= 1.0.a )
33
+ * sinatra-outputbuffer[http://github.com/kematzy/sinatra-outputbuffer] (>= 0.1.0)
34
+
35
+ Optionals:
36
+
37
+ * sinatra-settings[http://github.com/kematzy/sinatra-settings] (>= 0.1.1) # to view default settings in a browser display.
38
+
39
+ === Development & Tests:
40
+
41
+ * sinatra-tests (>= 0.1.6)
42
+ * rspec (>= 1.3.0 )
43
+ * rack-test (>= 0.5.3)
44
+ * rspec_hpricot_matchers (>= 0.1.0)
45
+ * fileutils
46
+ * sass
47
+ * ostruct
48
+ * yaml
49
+ * json
50
+
51
+
52
+ == Getting Started
53
+
54
+ To start caching your app's ouput, just require and register
55
+ the extension in your sub-classed Sinatra app:
56
+
57
+ require 'sinatra/cache'
58
+
59
+ class YourApp < Sinatra::Base
60
+
61
+ # NB! you need to set the root of the app first
62
+ set :root, '/path/2/the/root/of/your/app'
63
+
64
+ register(Sinatra::Cache)
65
+
66
+ set :cache_enabled, true # turn it on
67
+
68
+ <snip...>
69
+
70
+ end
71
+
72
+
73
+ In your "classic" Sinatra app, you just require the extension and set some key settings, like this:
74
+
75
+ require 'rubygems'
76
+ require 'sinatra'
77
+ require 'sinatra/cache'
78
+
79
+ # NB! you need to set the root of the app first
80
+ set :root, '/path/2/the/root/of/your/app'
81
+ set :public, '/path/2/public'
82
+
83
+ set :cache_enabled, true # turn it on
84
+
85
+ <snip...>
86
+
87
+
88
+
89
+ That's more or less it.
90
+
91
+ You should now be caching your output by default, in <tt>:production</tt> mode, as long as you use
92
+ one of Sinatra's render methods:
93
+
94
+ erb(), erubis(), haml(), sass(), builder(), etc..
95
+
96
+ ...or any render method that uses <tt>Sinatra::Templates#render()</tt> as its base.
97
+
98
+
99
+
100
+ == Configuration Settings
101
+
102
+ The default settings should help you get moving quickly, and are fairly common sense based.
103
+
104
+
105
+ ==== <tt>:cache_enabled</tt>
106
+
107
+ This setting toggles the cache functionality On / Off.
108
+ Default is: <tt>false</tt>
109
+
110
+
111
+ ==== <tt>:cache_environment</tt>
112
+
113
+ Sets the environment during which the cache functionality is active.
114
+ Default is: <tt>:production</tt>
115
+
116
+
117
+ ==== <tt>:cache_page_extension</tt>+
118
+
119
+ Sets the default file extension for cached files.
120
+ Default is: <tt>.html</tt>
121
+
122
+
123
+ ==== <tt>:cache_output_dir</tt>
124
+
125
+ Sets cache directory where the cached files are stored.
126
+ Default is: == "/path/2/your/app/public"
127
+
128
+ Although you can set it to the more ideal '<tt>..public/system/cache/</tt>'
129
+ if you can get that to work with your webserver setup.
130
+
131
+
132
+ ==== <tt>:cache_fragments_output_dir</tt>
133
+
134
+ Sets the directory where cached fragments are stored.
135
+ Default is the '../tmp/cache_fragments/' directory at the root of your app.
136
+
137
+ This is for security reasons since you don't really want your cached fragments publically available.
138
+
139
+
140
+ ==== <tt>:cache_fragments_wrap_with_html_comments</tt>
141
+
142
+ This setting toggles the wrapping of cached fragments in HTML comments. (see below)
143
+ Default is: <tt>true</tt>
144
+
145
+
146
+ ==== <tt>:cache_logging</tt>
147
+
148
+ This setting toggles the logging of various cache calls. If the app has access to the <tt>#logger</tt> method,
149
+ curtesy of Sinatra::Logger[http://github.com/kematzy/sinatra-logger] then it will log there, otherwise logging
150
+ is silent.
151
+
152
+ Default is: <tt>true</tt>
153
+
154
+
155
+ ==== <tt>:cache_logging_level</tt>
156
+
157
+ Sets the level at which the cache logger should log it's messages.
158
+ Default is: <tt>:info</tt>
159
+
160
+ Available options are: [:fatal, :error, :warn, :info, :debug]
161
+
162
+
163
+ == Basic Page Caching
164
+
165
+ By default caching only happens in <tt>:production</tt> mode, and via the Sinatra render methods, erb(), etc,
166
+
167
+ So asuming we have the following setup (continued from above)
168
+
169
+
170
+ class YourApp
171
+
172
+ <snip...>
173
+
174
+ set :cache_output_dir, "/full/path/2/app/root/public/system/cache"
175
+
176
+ <snip...>
177
+
178
+ get('/') { erb(:index) } # => cached as '../index.html'
179
+
180
+ get('/contact') { erb(:contact) } # => cached as '../contact.html'
181
+
182
+ # NB! the trailing slash on the URL
183
+ get('/about/') { erb(:about) } # => cached as '../about/index.html'
184
+
185
+ get('/feed.rss') { builder(:feed) } # => cached as '../feed.rss'
186
+ # NB! uses the extension of the passed URL,
187
+ # but DOES NOT ensure the format of the content based on the extension provided.
188
+
189
+ # complex URL with multiple possible params
190
+ get %r{/articles/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
191
+ erb(:articles)
192
+ end
193
+ # with the '/articles/a/b/c => cached as ../articles/a/b/c.html
194
+
195
+ # NB! the trailing slash on the URL
196
+ # with the '/articles/a/b/c/ => cached as ../articles/a/b/c/index.html
197
+
198
+ # CSS caching via Sass # => cached as '.../css/screen.css'
199
+ get '/css/screen.css' do
200
+ content_type 'text/css'
201
+ sass(:'css/screen')
202
+ end
203
+
204
+ # to turn off caching on certain pages.
205
+ get('/dont/cache/this/page') { erb(:aview, :cache => false) } # => is NOT cached
206
+
207
+
208
+ # NB! any query string params - [ /?page=X&id=y ] - are stripped off and TOTALLY IGNORED
209
+ # during the caching process.
210
+
211
+ end
212
+
213
+ OK, that's about all you need to know about basic Page Caching right there. Read the above example
214
+ carefully until you understand all the variations.
215
+
216
+
217
+ == Fragment Caching
218
+
219
+ If you just need to cache a fragment of a page, then you'd do as follows:
220
+
221
+ class YourApp
222
+
223
+ set :cache_fragments_output_dir, "/full/path/2/fragments/store/location"
224
+
225
+ end
226
+
227
+ Then in your views / layouts add the following:
228
+
229
+ <% cache_fragment(:name_of_fragment) do %>
230
+ # do something worth caching
231
+ <% end %>
232
+
233
+
234
+ Each fragment is stored in the same directory structure as your request
235
+ so, if you have a request like this:
236
+
237
+ get '/articles/2010/02' ...
238
+
239
+ ...the cached fragment will be stored as:
240
+
241
+ ../tmp/cache_fragments/articles/2010/02/< name_of_fragment >.html
242
+
243
+ This enables you to use similar names for your fragments or have
244
+ multiple URLs use the same view / layout.
245
+
246
+
247
+ === An important limitation
248
+
249
+ The fragment caching is dependent upon the final URL, so in the case of
250
+ a blog, where each article uses the same view, but through different URLs,
251
+ each of the articles would cache it's own fragment, which is ineffecient.
252
+
253
+ To sort-of deal with this limitation I have temporarily added a very hackish
254
+ 'fix' through adding a 2nd parameter (see example below), which will remove the
255
+ last part of the URL and use the rest of the URL as the stored fragment path.
256
+
257
+ So given the URL:
258
+
259
+ get '/articles/2010/02/fragment-caching-with-sinatra-cache' ...
260
+
261
+ and the following <tt>#cache_fragment</tt> declaration in your view
262
+
263
+ <% cache_fragment(:name_of_fragment, :shared) do %>
264
+ # do something worth caching
265
+ <% end %>
266
+
267
+ ...the cached fragment would be stored as:
268
+
269
+ ../tmp/cache_fragments/articles/2010/02/< name_of_fragment >.html
270
+
271
+ Any other URLs with the same URL root, like...
272
+
273
+ get '/articles/2010/02/writing-sinatra-extensions' ...
274
+
275
+ ... would use the same cached fragment.
276
+
277
+
278
+ <b>NB!</b> currently only supports one level, but Your fork might fix that ;-)
279
+
280
+
281
+ == Cache Expiration
282
+
283
+ <b>Under development, and not entirely final.</b> See Todo's below for more info.
284
+
285
+
286
+ To expire a cached item - file or fragment you use the :cache_expire() method.
287
+
288
+
289
+ cache_expire('/contact') => expires ../contact.html
290
+
291
+
292
+ # NB! notice the trailing slash
293
+ cache_expire('/contact/') => expires ../contact/index.html
294
+
295
+
296
+ cache_expire('/feed.rss') => expires ../feed.rss
297
+
298
+
299
+ To expire a cached fragment:
300
+
301
+ cache_expire('/some/path', :fragment => :name_of_fragment )
302
+
303
+ => expires ../some/path/:name_of_fragment.html
304
+
305
+
306
+
307
+ == A few important points to consider
308
+
309
+
310
+ === The DANGERS of URL query string params
311
+
312
+ By default the caching ignores the query string params, but that's not the only problem with query params.
313
+
314
+ Let's say you have a URL like this:
315
+
316
+ /products/?product_id=111
317
+
318
+ and then inside that template [ .../views/products.erb ], you use the <tt>params[:product_id]</tt>
319
+ param passed in for some purpose.
320
+
321
+ <ul>
322
+ <li>Product ID: <%= params[:product_id] %></li> # => 111
323
+ ...
324
+ </ul>
325
+
326
+ If you cache this URL, then the cached file [ ../cache/products.html ] will be stored with that
327
+ value embedded. Obviously not ideal for any other similar URLs with different <tt>product_id</tt>'s
328
+
329
+ To overcome this issue, use either of these two methods.
330
+
331
+ # in your_app.rb
332
+
333
+ # turning off caching on this page
334
+
335
+ get '/products/' do
336
+ ...
337
+ erb(:products, :cache => false)
338
+ end
339
+
340
+ # or
341
+
342
+ # rework the URLs to something like '/products/111 '
343
+
344
+ get '/products/:product_id' do
345
+ ...
346
+ erb(:products)
347
+ end
348
+
349
+
350
+
351
+ Thats's about all the information you need to know.
352
+
353
+
354
+ == RTFM
355
+
356
+ If the above is not clear enough, please check the Specs for a better understanding.
357
+
358
+
359
+ == Errors / Bugs
360
+
361
+ If something is not behaving intuitively, it is a bug, and should be reported.
362
+ Report it here: http://github.com/kematzy/sinatra-cache/issues
363
+
364
+
365
+ == TODOs
366
+
367
+ * Improve the fragment caching functionality
368
+
369
+ * Decide on how to handle site-wide shared fragments.
370
+
371
+ * Make the shared fragments more dynamic or usable
372
+
373
+ * Work out how to use the <tt>cache_expire()</tt> functionality in a logical way.
374
+
375
+ * Work out and include instructions on how to use a '../public/custom/cache/dir' with Passenger.
376
+
377
+ * Enable time-based / date-based cache expiry and regeneration of the cached-pages. [ht oakleafs]
378
+
379
+ * Enable .gz version of the cached file, further reducing the processing on the server. [ht oakleafs]
380
+ It would be killer to have <b>an extra .gz file next to the cached file</b>. That way, in Apache, you set it up like that:
381
+
382
+ RewriteCond %{HTTP:Accept-Encoding} gzip
383
+ RewriteCond %{REQUEST_FILENAME}.gz$ -f
384
+ RewriteRule ^(.*)$ $1.gz [L,QSA]
385
+
386
+ And it should serve the compressed file if available.
387
+
388
+ * Write more tests to ensure everything is very solid.
389
+
390
+ * Any other improvements you or I can think of.
391
+
392
+
393
+ == Note on Patches/Pull Requests
394
+
395
+ * Fork the project.
396
+ * Make your feature addition or bug fix.
397
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
398
+ * Commit, do not mess with rakefile, version, or history.
399
+ * (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
400
+ * Send me a pull request. Bonus points for topic branches.
401
+
402
+ == Copyright
403
+
404
+ Copyright (c) 2009-2010 kematzy. Released under the MIT License.
405
+
406
+ See LICENSE for details.
407
+
408
+ === Credits
409
+
410
+ A big <b>Thank You!</b> goes to rtomayko[http://github/rtomayko], blakemizerany[http://github.com/blakemizerany/]
411
+ and others working on the Sinatra framework.
412
+
413
+ === Inspirations
414
+
415
+ Inspired by code from Rails[http://rubyonrails.com/] & Merb[http://merbivore.com/]
416
+ and other sources
417
+