sinatra-cache 0.2.3 → 0.3.0

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