benjaminjackson-sinatra-cache 0.3.8

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