benjaminjackson-sinatra-cache 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,64 @@
1
+
2
+ module Sinatra
3
+
4
+ ##
5
+ # Sinatra::Templates
6
+ #
7
+ # Monkey-patches the private <tt>#render</tt> method,
8
+ # in order to support 'auto-magic' cache functionality.
9
+ #
10
+ #
11
+ module Templates
12
+
13
+ private
14
+
15
+ def render(engine, data, options={}, locals={}, &block)
16
+ # merge app-level options
17
+ options = settings.send(engine).merge(options) if settings.respond_to?(engine)
18
+ options[:outvar] ||= '@_out_buf'
19
+
20
+ # extract generic options
21
+ locals = options.delete(:locals) || locals || {}
22
+ views = options.delete(:views) || settings.views || "./views"
23
+ @default_layout = :layout if @default_layout.nil?
24
+ layout = options.delete(:layout)
25
+ layout = @default_layout if layout.nil? or layout == true
26
+ content_type = options.delete(:content_type) || options.delete(:default_content_type)
27
+
28
+ # set the cache related options
29
+ cache_enabled = settings.respond_to?(:cache_enabled) ? settings.send(:cache_enabled) : false
30
+ cache_output_dir = settings.send(:cache_output_dir) if settings.respond_to?(:cache_output_dir)
31
+ # raise Exception, "The Sinatra::Cache cache_output_dir variable is pointing to a non-existant directory cache_output_dir=[#{cache_output_dir}]" unless test(?d, cache_output_dir)
32
+ cache_option = options[:cache]
33
+ cache_option = true if cache_option.nil?
34
+
35
+ # compile and render template
36
+ layout_was = @default_layout
37
+ @default_layout = false
38
+ template = compile_template(engine, data, options, views)
39
+ output = template.render(self, locals, &block)
40
+ @default_layout = layout_was
41
+
42
+ # render layout
43
+ if layout
44
+ begin
45
+ options = options.merge(:views => views, :layout => false)
46
+ output = render(engine, layout, options, locals) { output }
47
+ # Cache the content or just return it
48
+ (cache_enabled && cache_option && settings.send(:environment) == settings.cache_environment) ?
49
+ cache_write_file(cache_file_path, output.gsub(/\n\r?$/,"")) : output
50
+ rescue Errno::ENOENT
51
+ end
52
+ end
53
+
54
+ # rendering without a layout
55
+ (cache_enabled && cache_option && settings.send(:environment) == settings.cache_environment) ?
56
+ cache_write_file(cache_file_path, output.gsub(/\n\r?$/,"") ) : output
57
+
58
+ output.extend(ContentTyped).content_type = content_type if content_type
59
+ output
60
+ end
61
+
62
+ end #/module Templates
63
+
64
+ end #/module Sinatra
@@ -0,0 +1,73 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{benjaminjackson-sinatra-cache}
8
+ s.version = "0.3.8"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["kematzy", "benjaminjackson"]
12
+ s.date = %q{2012-02-13}
13
+ s.description = %q{A Sinatra Extension that makes Page and Fragment Caching easy.}
14
+ s.email = %q{kematzy@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/sinatra/cache.rb",
26
+ "lib/sinatra/cache/helpers.rb",
27
+ "lib/sinatra/templates.rb",
28
+ "sinatra-cache.gemspec",
29
+ "spec/fixtures/apps/base/views/css.sass",
30
+ "spec/fixtures/apps/base/views/fragments.erb",
31
+ "spec/fixtures/apps/base/views/fragments_shared.erb",
32
+ "spec/fixtures/apps/base/views/index.erb",
33
+ "spec/fixtures/apps/base/views/layout.erb",
34
+ "spec/fixtures/apps/base/views/params.erb",
35
+ "spec/fixtures/apps/partials/views/home/index.erb",
36
+ "spec/fixtures/apps/partials/views/layout.erb",
37
+ "spec/fixtures/apps/partials/views/shared/_header.erb",
38
+ "spec/sinatra/cache_spec.rb",
39
+ "spec/spec_helper.rb"
40
+ ]
41
+ s.homepage = %q{http://github.com/benjaminjackson/sinatra-cache}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.8}
45
+ s.summary = %q{A Sinatra Extension that makes Page and Fragment Caching easy.}
46
+ s.test_files = [
47
+ "spec/sinatra/cache_spec.rb",
48
+ "spec/spec_helper.rb"
49
+ ]
50
+
51
+ if s.respond_to? :specification_version then
52
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
53
+ s.specification_version = 3
54
+
55
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
56
+ s.add_runtime_dependency(%q<sinatra>, [">= 1.1.0"])
57
+ s.add_runtime_dependency(%q<sinatra-outputbuffer>, [">= 0.1.0"])
58
+ s.add_development_dependency(%q<sinatra-tests>, [">= 0.1.6"])
59
+ s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
60
+ else
61
+ s.add_dependency(%q<sinatra>, [">= 1.1.0"])
62
+ s.add_dependency(%q<sinatra-outputbuffer>, [">= 0.1.0"])
63
+ s.add_dependency(%q<sinatra-tests>, [">= 0.1.6"])
64
+ s.add_dependency(%q<rspec>, [">= 1.3.0"])
65
+ end
66
+ else
67
+ s.add_dependency(%q<sinatra>, [">= 1.1.0"])
68
+ s.add_dependency(%q<sinatra-outputbuffer>, [">= 0.1.0"])
69
+ s.add_dependency(%q<sinatra-tests>, [">= 0.1.6"])
70
+ s.add_dependency(%q<rspec>, [">= 1.3.0"])
71
+ end
72
+ end
73
+
@@ -0,0 +1,2 @@
1
+ body
2
+ :color red
@@ -0,0 +1,11 @@
1
+ <h1>FRAGMENTS</h1>
2
+
3
+ <% cache_fragment(:test_fragment) do %>
4
+ <div id="cache-fragment">
5
+ <p><%= "Fragment created: #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}" %></p>
6
+ </div>
7
+ <% sleep 1 %>
8
+ <% end %>
9
+
10
+ <h2>Content After Sleep</h2>
11
+ <p><%= "Time is now: #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}" %></p>
@@ -0,0 +1,11 @@
1
+ <h1>FRAGMENTS - SHARED</h1>
2
+
3
+ <% cache_fragment(:test_fragment, :shared) do %>
4
+ <div id="cache-fragment">
5
+ <p><%= "Shared Fragment created: #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}" %></p>
6
+ </div>
7
+ <% sleep 1 %>
8
+ <% end %>
9
+
10
+ <h2>Content After Sleep</h2>
11
+ <p><%= "Time is now: #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}" %></p>
@@ -0,0 +1 @@
1
+ <h1>HOME</h1>
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title>Sinatra::Cache</title>
4
+ <%= cache_timestamp %>
5
+ </head>
6
+ <body>
7
+ <%= yield %>
8
+ </body>
9
+ </html>
@@ -0,0 +1,3 @@
1
+ <h1>PARAMS</h1>
2
+ <p><%= @captures.join(" | ") %></p>
3
+ <pre><%= params.inspect %></pre>
@@ -0,0 +1,4 @@
1
+ <% content_for :section_header do %>
2
+ <h1>HELLO from [content_for(:section_header)]</h1>
3
+ <% end %>
4
+ <h2>HELLO FROM [FILE <%= __FILE__ %>]</h2>
@@ -0,0 +1,11 @@
1
+ <html>
2
+ <head>
3
+ <title>Sinatra::Cache</title>
4
+ <%= cache_timestamp %>
5
+ </head>
6
+ <body>
7
+ <%= partial("shared/header") %>
8
+
9
+ <%= yield %>
10
+ </body>
11
+ </html>
@@ -0,0 +1,3 @@
1
+ <div id="header">
2
+ <%= yield_content :section_header %>
3
+ </div>
@@ -0,0 +1,967 @@
1
+
2
+ require "#{File.dirname(File.dirname(File.expand_path(__FILE__)))}/spec_helper"
3
+
4
+ describe "Sinatra" do
5
+
6
+ before(:each) do
7
+ @delete_cached_test_files = true #convenience toggle
8
+ end
9
+ describe "Cache" do
10
+
11
+ class MyDefaultsTestApp < MyTestApp
12
+ register(Sinatra::Cache)
13
+ end
14
+
15
+
16
+ class MyTestApp
17
+ register(Sinatra::Cache)
18
+
19
+ # need to set the root of the app for the default :cache_fragments_output_dir to work
20
+ set :root, ::APP_ROOT
21
+
22
+ set :cache_enabled, true
23
+ set :cache_environment, :test
24
+ set :cache_output_dir, "#{test_cache_path('system/cache')}"
25
+ set :cache_fragments_output_dir, "#{test_cache_path('system/cache')}_fragments"
26
+ set :cache_fragments_wrap_with_html_comments, false
27
+
28
+ # NB! Although without tests, the positioning of the custom method in relation to other
29
+ # Cache related declaration has no effect.
30
+ helpers do
31
+ def uncached_erb(template, options={})
32
+ erb(template, options.merge(:cache => false ))
33
+ end
34
+ end
35
+
36
+ get('/') { erb(:index) }
37
+ get('/erb/?') { erb(:index, :layout => false) }
38
+
39
+ # complex regex URL, matches
40
+ get %r{^/params/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
41
+ @captures = params[:captures].nil? ? [] : params[:captures].compact
42
+ erb(:params)
43
+ end
44
+ get('/file-extensions.*') do
45
+ @vars = { :framework => "Sinatra", :url => "www.sinatrarb.com" }
46
+ case params[:splat].first.to_sym
47
+ when :json
48
+ erb(@vars.to_json, :layout => false )
49
+ when :yaml
50
+ erb(@vars.to_yaml,:layout => false)
51
+ else
52
+ # direct output, should NOT be Cached
53
+ @vars.inspect
54
+ # @vars.inspect
55
+ end
56
+ end
57
+
58
+ ## NO CACHING
59
+ get('/uncached/erb'){ erb(:index, :cache => false) }
60
+ get('/uncached/erb/no/layout'){ erb(:index, :cache => false, :layout => false ) }
61
+ get('/uncached/uncached_erb'){ uncached_erb(:index) }
62
+
63
+ get '/css/screen.css' do
64
+ content_type 'text/css'
65
+ sass(:css, :style => :compact)
66
+ end
67
+ get '/css/no/cache.css' do
68
+ content_type 'text/css'
69
+ sass(:css, :style => :compact, :cache => false)
70
+ end
71
+
72
+ # enable :method_override
73
+ post('/post/?') { erb("POST", :layout => false) }
74
+ put('/put/?') { erb('PUT', :layout => false) }
75
+ delete('/delete/?') { erb('DELETE', :layout => false) }
76
+
77
+ get %r{^/fragments/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
78
+ erb(:fragments, :layout => false, :cache => false)
79
+ end
80
+ get %r{^/sharedfragments/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
81
+ erb(:fragments_shared, :layout => false, :cache => false)
82
+ end
83
+
84
+
85
+ end
86
+
87
+ # convenience shared spec that sets up MyTestApp and tests it's OK,
88
+ # without it you will get "stack level too deep" errors
89
+ it_should_behave_like "MyTestApp"
90
+
91
+ # it_should_behave_like "debug => app.methods"
92
+
93
+ describe "VERSION" do
94
+
95
+ it "should return the VERSION string" do
96
+ Sinatra::Cache::VERSION.should be_a_kind_of(String)
97
+ Sinatra::Cache::VERSION.should match(/\d\.\d+\.\d+(\.\d)?/)
98
+ end
99
+
100
+ end #/ VERSION
101
+
102
+ describe "#self.version" do
103
+
104
+ it "should return a version of the Sinatra::Cache VERSION string" do
105
+ Sinatra::Cache.version.should be_a_kind_of(String)
106
+ Sinatra::Cache.version.should match(/Sinatra::Cache v\d\.\d+\.\d+(\.\d)?/)
107
+ end
108
+
109
+ end #/ #self.version
110
+
111
+
112
+ describe "Configuration" do
113
+
114
+ describe "with Default Settings" do
115
+
116
+ it "should set :cache_enabled to false" do
117
+ MyDefaultsTestApp.cache_enabled.should == false
118
+ end
119
+
120
+ it "should set :cache_environment to :production" do
121
+ MyDefaultsTestApp.cache_environment.should == :production
122
+ end
123
+
124
+ it "should set :cache_page_extension to '.html'" do
125
+ MyDefaultsTestApp.cache_page_extension.should == '.html'
126
+ end
127
+
128
+ it "should set :cache_output_dir to '../public'" do
129
+ MyDefaultsTestApp.cache_output_dir.should == "#{public_fixtures_path}"
130
+ end
131
+
132
+ it "should set :cache_fragments_output_dir to '../tmp/cache_fragments'" do
133
+ MyDefaultsTestApp.cache_fragments_output_dir.should == "#{fixtures_path}/tmp/cache_fragments"
134
+ end
135
+
136
+ it "should set :cache_logging to true" do
137
+ MyDefaultsTestApp.cache_logging.should == true
138
+ end
139
+
140
+ it "should set :cache_logging_level to :info" do
141
+ MyDefaultsTestApp.cache_logging_level.should == :info
142
+ end
143
+
144
+ it "should set :cache_fragments_wrap_with_html_comments to true" do
145
+ MyDefaultsTestApp.cache_fragments_wrap_with_html_comments.should == true
146
+ end
147
+
148
+ end #/ with Default Settings
149
+
150
+ describe "with Custom Settings" do
151
+
152
+ it "should set :cache_enabled to true" do
153
+ MyTestApp.cache_enabled.should == true
154
+ end
155
+
156
+ it "should set :cache_environment to :test" do
157
+ MyTestApp.cache_environment.should == :test
158
+ end
159
+
160
+ it "should set :cache_page_extension to '.html'" do
161
+ MyTestApp.cache_page_extension.should == '.html'
162
+ end
163
+
164
+ it "should set :cache_output_dir to '../public/system/cache'" do
165
+ MyTestApp.cache_output_dir.should == "#{test_cache_path('system/cache')}"
166
+ end
167
+
168
+ it "should set :cache_fragments_output_dir to '../public/system/cache_fragments'" do
169
+ MyTestApp.cache_fragments_output_dir.should == "#{test_cache_path('system/cache')}_fragments"
170
+ end
171
+
172
+ it "should set :cache_logging to true" do
173
+ MyTestApp.cache_logging.should == true
174
+ end
175
+
176
+ it "should set :cache_logging_level to :info" do
177
+ MyTestApp.cache_logging_level.should == :info
178
+ end
179
+
180
+ it "should set :cache_fragments_wrap_with_html_comments to false" do
181
+ MyTestApp.cache_fragments_wrap_with_html_comments.should == false
182
+ end
183
+
184
+ end #/ Custom
185
+
186
+ end #/ Configuration
187
+
188
+
189
+ describe "Helpers" do
190
+
191
+ describe "#cache_timestamp" do
192
+
193
+ it "should return an HTML comment with the current timestamp" do
194
+ erb_app "<%= cache_timestamp %>"
195
+ body.should match(/\<\!-- page cached\: #{Regexp.escape(Time.now.strftime("%Y-%m-%d %H:%M:%S"))} -->/)
196
+ end
197
+
198
+ end #/ #cache_timestamp
199
+
200
+ module Sinatra::Cache::Helpers
201
+ public :cache_file_path, :cache_write_file, :cache_file_name, :cache_page_path
202
+ end
203
+
204
+ describe "#cache_file_path" do
205
+
206
+ it "should have some tests"
207
+
208
+ end #/ #cache_file_path
209
+
210
+ describe "#cache_write_file" do
211
+
212
+ it "should have some tests"
213
+
214
+ end #/ #cache_write_file
215
+
216
+ describe "#cache_file_name" do
217
+
218
+ it "should have some tests"
219
+
220
+ end #/ #cache_file_name
221
+
222
+ describe "#cache_page_path" do
223
+
224
+ it "should have some tests"
225
+
226
+ end #/ #cache_page_path
227
+
228
+ end #/ Helpers
229
+
230
+
231
+ describe "with caching enabled" do
232
+
233
+ describe "basic Page caching" do
234
+
235
+ describe "GET requests for" do
236
+
237
+ describe "the Home page - ['/' => /index.html] (with layout)" do
238
+
239
+ before(:each) do
240
+ @cache_file = "#{test_cache_path('system/cache')}/index.html"
241
+ get('/')
242
+ end
243
+
244
+ after(:each) do
245
+ FileUtils.rm(@cache_file) if @delete_cached_test_files
246
+ end
247
+
248
+ it "should render the expected output" do
249
+ body.should have_tag('html > head > title', 'Sinatra::Cache')
250
+ body.should match(/<!-- page cached: #{Regexp.escape(Time.now.strftime("%Y-%m-%d %H:%M:%S"))} -->/)
251
+ body.should have_tag('html > body > h1', 'HOME')
252
+ end
253
+
254
+ it "should create a cached file in the cache output directory" do
255
+ test(?f, @cache_file).should == true
256
+ end
257
+
258
+ end #/ GET ['/' => /index.html]
259
+
260
+ describe "a basic URL without a trailing slash - ['/erb' => /erb.html] (ERB without layout)" do
261
+
262
+ before(:each) do
263
+ @cache_file = "#{test_cache_path('system/cache')}/erb.html"
264
+ get('/erb')
265
+ end
266
+
267
+ after(:each) do
268
+ FileUtils.rm(@cache_file) if @delete_cached_test_files
269
+ end
270
+
271
+ it "should render the expected output" do
272
+ body.should == "<h1>HOME</h1>"
273
+ end
274
+
275
+ it "should create a cached file in the cache output directory" do
276
+ test(?f, @cache_file).should == true
277
+ end
278
+
279
+ end #/ GET ['/erb' => /erb.html]
280
+
281
+ describe "a basic URL with a trailing slash - ['/erb/' => /erb/index.html]" do
282
+
283
+ before(:each) do
284
+ @cache_file = "#{test_cache_path('system/cache')}/erb/index.html"
285
+ get('/erb/')
286
+ end
287
+
288
+ after(:each) do
289
+ FileUtils.rm(@cache_file) if @delete_cached_test_files
290
+ end
291
+
292
+ it "should render the expected output" do
293
+ body.should == "<h1>HOME</h1>"
294
+ end
295
+
296
+ it "should create a cached file at ../cache/< URL >/index.html" do
297
+ test(?f, @cache_file).should == true
298
+ end
299
+
300
+ end #/ GET ['/erb/' => /erb/index.html]
301
+
302
+ describe "a URL with file extension - ['/file-extensions.EXT' => /file-extensions.EXT]" do
303
+
304
+ before(:each) do
305
+ ext = '.yaml'
306
+ @cache_file = "#{test_cache_path('system/cache')}/file-extensions#{ext}"
307
+ get("/file-extensions#{ext}")
308
+ end
309
+
310
+ after(:each) do
311
+ FileUtils.rm(@cache_file) if @delete_cached_test_files
312
+ end
313
+
314
+ it "should render the expected output" do
315
+ body.should_not == ""
316
+ end
317
+
318
+ it "should create the cached file with the extension" do
319
+ test(?f, @cache_file).should == true
320
+ end
321
+
322
+ end #/ GET ['/file-extensions.EXT' => /file-extensions.EXT]
323
+
324
+ describe "URLs with multiple levels and/or with ?params attached" do
325
+
326
+ %w(
327
+ /params/? /params/1.xml?p=2 /params/page/?page=2 /params/page/?page=99
328
+ /params/a/ /params/a/b /params/a/b/c /params/a/b/c/d
329
+ ).each do |url|
330
+
331
+ # does the URL contain a ? or not
332
+ params_free_url = url =~ /\?/ ? url.split('?').first.chomp('?') : url
333
+ file_url = (params_free_url[-1,1] =~ /\//) ? "#{params_free_url}index" : params_free_url
334
+
335
+ # create the string for the test output below
336
+ file_url_str = File.extname(file_url) == '' ? file_url << '.html' : file_url
337
+
338
+ describe "the URL ['#{url}' => #{file_url_str}]" do
339
+
340
+ before(:each) do
341
+ @file_url = file_url
342
+ @cache_file = "#{test_cache_path('system/cache')}#{file_url}"
343
+ @cache_file << ".html" if File.extname(@file_url) == ''
344
+ # puts "when the URL=[#{url}] the @cache_file=[#{@cache_file}] [#{__FILE__}:#{__LINE__}]"
345
+ get(url)
346
+ end
347
+
348
+ after(:each) do
349
+ FileUtils.remove_dir("#{test_cache_path('system/cache')}/params") if @delete_cached_test_files
350
+ end
351
+
352
+ it "should render the expected output" do
353
+ body.should have_tag('html > head > title', 'Sinatra::Cache')
354
+ body.should have_tag('html > body > h1', 'PARAMS')
355
+ body.should have_tag('html > body > p' )
356
+ end
357
+
358
+ it "should create a cached file at [../public/system/cache#{file_url_str}]" do
359
+ test(?f, @cache_file).should == true
360
+ end
361
+
362
+ end #/ GET 'url' -
363
+
364
+ end #/ end loop
365
+
366
+ end #/ URLs with multiple levels and/or with ?params attached
367
+
368
+ describe "with :cache => false, :layout => false " do
369
+
370
+ before(:each) do
371
+ @cache_file = "#{test_cache_path('system/cache')}/uncached/erb/no/layout.html"
372
+ get('/uncached/erb/no/layout')
373
+ end
374
+
375
+ it "should output the correct HTML as expected" do
376
+ body.should have_tag('h1', 'HOME')
377
+ end
378
+
379
+ it "should NOT cache the output" do
380
+ test(?d, File.dirname(@cache_file) ).should == false # testing for directory
381
+ test(?f, @cache_file).should == false
382
+ end
383
+
384
+ end #/ with :cache => false, :layout => false
385
+
386
+ describe "with :cache => false" do
387
+
388
+ before(:each) do
389
+ @cache_file = "#{test_cache_path('system/cache')}/uncached/erb.html"
390
+ get('/uncached/erb')
391
+ end
392
+
393
+ it "should output the correct HTML as expected" do
394
+ body.should have_tag('h1', 'HOME')
395
+ end
396
+
397
+ it "should NOT cache the output" do
398
+ test(?d, File.dirname(@cache_file) ).should == false # testing for directory
399
+ test(?f, @cache_file).should == false
400
+ end
401
+
402
+ end #/ with :cache => false
403
+
404
+ describe "URLs with custom erb helpers, like :admin_erb().." do
405
+
406
+ before(:each) do
407
+ @cache_file = "#{test_cache_path('system/cache')}/uncached/uncached_erb.html"
408
+ get('/uncached/uncached_erb')
409
+ end
410
+
411
+ it "should output the correct HTML as expected" do
412
+ body.should have_tag('html > body > h1', 'HOME')
413
+ end
414
+
415
+ it "should NOT cache the output" do
416
+ test(?d, File.dirname(@cache_file) ).should == false # testing for directory
417
+ test(?f, @cache_file).should == false
418
+ end
419
+
420
+ end #/ URLs with custom erb helpers, like :admin_erb()..
421
+
422
+ describe "CSS URLs with dynamic .sass files" do
423
+
424
+ describe "the URL ['/css/screen.css' => /css/screen.css]" do
425
+
426
+ before(:each) do
427
+ @cache_file = "#{test_cache_path('system/cache')}/css/screen.css"
428
+ FileUtils.remove_dir(File.dirname(@cache_file), :force => true )
429
+ get('/css/screen.css')
430
+ end
431
+
432
+ after(:each) do
433
+ FileUtils.rm(@cache_file) if @delete_cached_test_files
434
+ FileUtils.remove_dir(File.dirname(@cache_file), :force => true ) if @delete_cached_test_files
435
+ end
436
+
437
+ it "should output the correct CSS as expected" do
438
+ body.should == "body { color: red; }\n"
439
+ end
440
+
441
+ it "should automatically create the cache /css output directory" do
442
+ test(?d, File.dirname(@cache_file) ).should == true
443
+ end
444
+
445
+ it "should create a cached file at [../public/system/cache/css/screen.css]" do
446
+ test(?f, @cache_file).should == true
447
+ end
448
+
449
+ end #/ GET ['/css/screen.css' => /css/screen.css]
450
+
451
+ describe "URLs with ':cache => false' - ['/css/no/cache.css' => /css/no/cache.css]" do
452
+
453
+ before(:each) do
454
+ @cache_file = "#{test_cache_path('system/cache')}/css/no/cache.css"
455
+ get('/css/no/cache.css')
456
+ end
457
+
458
+ it "should output the correct CSS as expected" do
459
+ body.should == "body { color: red; }\n"
460
+ end
461
+
462
+ it "should NOT cache the output" do
463
+ test(?d, File.dirname(@cache_file) ).should == false # testing for directory
464
+ test(?f, @cache_file).should == false
465
+ end
466
+
467
+ end #/ GET ['/css/no/cache.css' => /css/no/cache.css]
468
+
469
+ end #/ CSS URLs with dynamic .sass files
470
+
471
+ end #/ GET requests
472
+
473
+ describe "POST, PUT, DELETE requests" do
474
+
475
+ describe "POST '/post'" do
476
+
477
+ before(:each) do
478
+ @cache_file = "#{test_cache_path('system/cache')}/post.html"
479
+ post('/post', :test => {:name => "test-#{Time.now.strftime("%Y-%d-%m %H:%M:%S")}", :content => "with content" })
480
+ end
481
+
482
+ it "should render any output as normal" do
483
+ body.should == 'POST'
484
+ end
485
+
486
+ it "should NOT cache the output" do
487
+ test(?f, @cache_file).should == false
488
+ end
489
+
490
+ end #/ POST '/post'
491
+
492
+ describe "PUT '/put'" do
493
+
494
+ before(:each) do
495
+ @cache_file = "#{test_cache_path('system/cache')}/put.html"
496
+ put('/put', { :test => { :name => "test" }, :_method => "put" })
497
+ end
498
+
499
+ it "should render any output as normal" do
500
+ body.should == 'PUT'
501
+ end
502
+
503
+ it "should NOT cache the output" do
504
+ test(?f, @cache_file).should == false
505
+ end
506
+
507
+ end #/ PUT '/put'
508
+
509
+ describe "DELETE '/delete'" do
510
+
511
+ before(:each) do
512
+ @cache_file = "#{test_cache_path('system/cache')}/delete.html"
513
+ delete('/delete') #, { :test => { :name => "test" }, :_method => "put" })
514
+ end
515
+
516
+ it "should render any output as normal" do
517
+ body.should == 'DELETE'
518
+ end
519
+
520
+ it "should NOT cache the output" do
521
+ test(?f, @cache_file).should == false
522
+ end
523
+
524
+ end #/ DELETE '/delete'
525
+
526
+ end #/ POST, PUT, DELETE requests
527
+
528
+ end #/ basic Page caching
529
+
530
+ describe "and Fragment caching" do
531
+
532
+ describe "with default settings" do
533
+
534
+ def app; ::MyTestApp.new ; end
535
+
536
+ describe "using NOT shared fragments" do
537
+
538
+ after(:all) do
539
+ FileUtils.rm_r("#{test_cache_path('system/cache')}_fragments/fragments") if @delete_cached_test_files
540
+ end
541
+
542
+ %w(
543
+ /fragments /fragments/a/b/ /fragments/with/param/?page=1
544
+ /fragments/dasherised-urls/works-as-well
545
+ ).each do |url|
546
+
547
+ params_free_url = url =~ /\?/ ? url.split('?').first.chomp('?') : url
548
+ dir_structure = params_free_url.gsub(/^\//,'').gsub(/\/$/,'')
549
+
550
+ it "should cache the fragments from the URL [#{url}] as [#{dir_structure}/test_fragment.html]" do
551
+ get(url)
552
+ # body.should have_tag(:debug)
553
+ body.should have_tag('h1','FRAGMENTS', :count => 1)
554
+ body.should_not match(/<!-- cache fragment:(.+)test_fragment -->/)
555
+ body.should_not match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
556
+
557
+ # render the page a 2nd time from cache
558
+ get(url)
559
+ body.should have_tag('h1','FRAGMENTS', :count => 1)
560
+ body.should_not match(/<!-- cache fragment:(.+)test_fragment -->/)
561
+ body.should_not match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
562
+
563
+ # the cached fragment has already been found if we get this far,
564
+ # but just for good measure do we check for the existence of the fragment file.
565
+ test(?f, "#{test_cache_path('system/cache')}_fragments/#{dir_structure}/test_fragment.html").should == true
566
+ end
567
+ end
568
+
569
+ end #/ using NOT shared fragments
570
+
571
+ describe "using shared fragments" do
572
+
573
+ after(:all) do
574
+ FileUtils.rm_r("#{test_cache_path('system/cache')}_fragments/sharedfragments") if @delete_cached_test_files
575
+ end
576
+
577
+ describe "when requesting the first URL" do
578
+
579
+ # FIXME:: work out some clever way to split all of these tests into single items instead of in one big blob
580
+
581
+ it "should cache the fragment based on the URL and use it on subsequent requests by URLs sharing the same root URL" do
582
+ url = '/sharedfragments/2010/02/some-article-01'
583
+ params_free_url = url =~ /\?/ ? url.split('?').first.chomp('?') : url
584
+ dir_structure = params_free_url.gsub(/^\//,'').gsub(/\/$/,'')
585
+ dirs = dir_structure.split('/')
586
+ dir_structure = dirs.first(dirs.length-1).join('/')
587
+
588
+ get(url)
589
+ # body.should have_tag(:debug)
590
+ body.should have_tag('h1','FRAGMENTS - SHARED', :count => 1)
591
+ body.should_not match(/<!-- cache fragment:(.+)test_fragment -->/)
592
+ body.should_not match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
593
+
594
+ get(url)
595
+ body.should have_tag('h1','FRAGMENTS - SHARED', :count => 1)
596
+ body.should_not match(/<!-- cache fragment:(.+)test_fragment -->/)
597
+ body.should_not match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
598
+
599
+ # the cached fragment has already been found if we get this far,
600
+ # but just for good measure do we check for the existence of the fragment file.
601
+ test(?f, "#{test_cache_path('system/cache')}_fragments/#{dir_structure}/test_fragment.html").should == true
602
+
603
+ # should use the cached fragment rather than cache a new fragment
604
+ url = '/sharedfragments/2010/02/another-article-02'
605
+ get(url)
606
+ body.should have_tag('h1','FRAGMENTS - SHARED', :count => 1)
607
+ body.should_not match(/<!-- cache fragment:(.+)test_fragment -->/)
608
+ body.should_not match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
609
+ end
610
+
611
+ end #/ when requesting the first URL
612
+
613
+ end #/ using shared fragments
614
+
615
+ end #/ with default settings
616
+
617
+ describe "with :cache_fragments_wrap_with_html_comments enabled" do
618
+
619
+ class MyWrapTestApp < Sinatra::Base
620
+
621
+ set :app_dir, "#{APP_ROOT}/apps/base"
622
+ set :public, "#{fixtures_path}/public"
623
+ set :views, "#{app_dir}/views"
624
+
625
+ register(Sinatra::Tests)
626
+
627
+ enable :raise_errors
628
+
629
+ register(Sinatra::Cache)
630
+
631
+ # need to set the root of the app for the default :cache_fragments_output_dir to work
632
+ set :root, ::APP_ROOT
633
+
634
+ set :cache_enabled, true
635
+ set :cache_environment, :test
636
+ set :cache_output_dir, "#{test_cache_path('system/cache')}"
637
+ set :cache_fragments_output_dir, "#{test_cache_path('system/cache')}_wrap_fragments"
638
+ set :cache_fragments_wrap_with_html_comments, true
639
+
640
+ get('/') { erb(:index) }
641
+
642
+ get %r{^/fragments/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
643
+ erb(:fragments, :layout => false, :cache => false)
644
+ end
645
+ get %r{^/sharedfragments/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
646
+ erb(:fragments_shared, :layout => false, :cache => false)
647
+ end
648
+
649
+ end
650
+
651
+ def app; ::MyWrapTestApp.new ; end
652
+
653
+ describe "using NOT shared fragments" do
654
+
655
+ after(:all) do
656
+ FileUtils.rm_r("#{test_cache_path('system/cache')}_wrap_fragments/fragments") if @delete_cached_test_files
657
+ end
658
+
659
+ %w(
660
+ /fragments /fragments/a/b/ /fragments/with/param/?page=1
661
+ /fragments/dasherised-urls/works-as-well
662
+ ).each do |url|
663
+
664
+ params_free_url = url =~ /\?/ ? url.split('?').first.chomp('?') : url
665
+ dir_structure = params_free_url.gsub(/^\//,'').gsub(/\/$/,'')
666
+
667
+ it "should cache the fragments from the URL [#{url}] as [#{dir_structure}/test_fragment.html]" do
668
+ get(url)
669
+ # body.should have_tag(:debug)
670
+ body.should have_tag('h1','FRAGMENTS', :count => 1)
671
+ body.should_not match(/<!-- cache fragment:(.+)test_fragment -->/)
672
+ body.should_not match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
673
+
674
+ # render the page a 2nd time from cache
675
+ get(url)
676
+ body.should have_tag('h1','FRAGMENTS', :count => 1)
677
+ body.should match(/<!-- cache fragment:(.+)test_fragment -->/)
678
+ body.should match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
679
+
680
+ # the cached fragment has already been found if we get this far,
681
+ # but just for good measure do we check for the existence of the fragment file.
682
+ test(?f, "#{test_cache_path('system/cache')}_wrap_fragments/#{dir_structure}/test_fragment.html").should == true
683
+ end
684
+ end
685
+
686
+ end #/ using NOT shared fragments
687
+
688
+ describe "using shared fragments" do
689
+
690
+ after(:all) do
691
+ FileUtils.rm_r("#{test_cache_path('system/cache')}_wrap_fragments/sharedfragments") if @delete_cached_test_files
692
+ end
693
+
694
+ describe "when requesting the first URL" do
695
+
696
+ # FIXME:: work out some clever way to split all of these tests into single items instead of in one big blob
697
+
698
+ it "should cache the fragment based on the URL and use it on subsequent requests by URLs sharing the same root URL" do
699
+ url = '/sharedfragments/2010/02/some-article-01'
700
+ params_free_url = url =~ /\?/ ? url.split('?').first.chomp('?') : url
701
+ dir_structure = params_free_url.gsub(/^\//,'').gsub(/\/$/,'')
702
+ dirs = dir_structure.split('/')
703
+ dir_structure = dirs.first(dirs.length-1).join('/')
704
+
705
+ get(url)
706
+ # body.should have_tag(:debug)
707
+ body.should have_tag('h1','FRAGMENTS - SHARED', :count => 1)
708
+ body.should_not match(/<!-- cache fragment:(.+)test_fragment -->/)
709
+ body.should_not match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
710
+
711
+ get(url)
712
+ body.should have_tag('h1','FRAGMENTS - SHARED', :count => 1)
713
+ body.should match(/<!-- cache fragment:(.+)test_fragment -->/)
714
+ body.should match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
715
+
716
+ # the cached fragment has already been found if we get this far,
717
+ # but just for good measure do we check for the existence of the fragment file.
718
+ test(?f, "#{test_cache_path('system/cache')}_wrap_fragments/#{dir_structure}/test_fragment.html").should == true
719
+
720
+ # should use the cached fragment rather than cache a new fragment
721
+ url = '/sharedfragments/2010/02/another-article-02'
722
+ get(url)
723
+ body.should have_tag('h1','FRAGMENTS - SHARED', :count => 1)
724
+ body.should match(/<!-- cache fragment:(.+)test_fragment -->/)
725
+ body.should match(/<!-- \/cache fragment: test_fragment cached at \[ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\] -->/)
726
+ end
727
+
728
+ end #/ when requesting the first URL
729
+
730
+ end #/ using shared fragments
731
+
732
+ end #/ with customs settings
733
+
734
+
735
+ end #/ and Fragment caching
736
+
737
+ describe "basic Cache Expiring" do
738
+
739
+ describe "Pages" do
740
+
741
+ it "should expire the page ['/params/cache/expire/' => ../cache/params/cache/expire/index.html]" do
742
+ get('/params/cache/expire/')
743
+ test(?f,"#{test_cache_path('system/cache')}/params/cache/expire/index.html" ).should == true
744
+ lambda {
745
+ erb_app "<% cache_expire('/params/cache/expire/') %>"
746
+ }.should_not raise_error(Exception)
747
+
748
+ test(?f,"#{test_cache_path('system/cache')}/params/cache/expire/index.html" ).should == false
749
+
750
+ end
751
+
752
+ it "should expire the page ['/params/cache/expired' => ../cache/params/cache/expired.html]" do
753
+ get('/params/cache/expired')
754
+ test(?f,"#{test_cache_path('system/cache')}/params/cache/expired.html" ).should == true
755
+ lambda {
756
+ erb_app "<% cache_expire('/params/cache/expired') %>"
757
+ }.should_not raise_error(Exception)
758
+
759
+ test(?f,"#{test_cache_path('system/cache')}/params/cache/expired.html" ).should == false
760
+
761
+ end
762
+
763
+ end #/ Pages
764
+
765
+ describe "Fragments" do
766
+
767
+ it "should expire the fragment ['/fragments/cache/expire/' => ../cache_fragments/fragments/cache/expire/test_fragment.html]" do
768
+ get('/fragments/cache/expire/')
769
+ test(?f,"#{test_cache_path('system/cache')}_fragments/fragments/cache/expire/test_fragment.html" ).should == true
770
+ lambda {
771
+ erb_app "<% cache_expire('/fragments/cache/expire/',:fragment => :test_fragment) %>"
772
+ }.should_not raise_error(Exception)
773
+ test(?f,"#{test_cache_path('system/cache')}/params/cache_fragments/expire/test_fragment.html" ).should == false
774
+ end
775
+
776
+ it "should expire the fragment ['/fragments/cache/expired' => ../cache_fragments/fragments/cache/expired/test_fragment.html]" do
777
+ get('/fragments/cache/expired')
778
+ test(?f,"#{test_cache_path('system/cache')}_fragments/fragments/cache/expired/test_fragment.html" ).should == true
779
+ lambda {
780
+ erb_app "<% cache_expire('/fragments/cache/expired',:fragment => :test_fragment) %>"
781
+ }.should_not raise_error(Exception)
782
+ test(?f,"#{test_cache_path('system/cache')}/params/cache_fragments/expired/test_fragment.html" ).should == false
783
+ end
784
+
785
+ end #/ Pages
786
+
787
+ end #/ basic Cache Expiring
788
+
789
+
790
+ end #/ with caching enabled
791
+
792
+ describe "EDGE cases" do
793
+
794
+ describe "Using nested buffers" do
795
+
796
+ require 'sinatra/outputbuffer'
797
+
798
+ class MyPartialsTestApp < Sinatra::Base
799
+ register(Sinatra::Tests)
800
+
801
+ enable :raise_errors
802
+
803
+ register(Sinatra::OutputBuffer)
804
+ register(Sinatra::Cache)
805
+
806
+ # need to set the root of the app for the default :cache_fragments_output_dir to work
807
+ set :root, ::APP_ROOT
808
+ set :app_dir, "#{APP_ROOT}/apps/partials"
809
+ set :public, "#{fixtures_path}/public"
810
+ set :views, "#{APP_ROOT}/apps/partials/views"
811
+
812
+ set :cache_enabled, true
813
+ set :cache_environment, :test
814
+ set :cache_output_dir, "#{test_cache_path('system/cache')}"
815
+ set :cache_fragments_output_dir, "#{test_cache_path('system/cache')}_fragments"
816
+
817
+ # NB! Although without tests, the positioning of the custom method in relation to other
818
+ # Cache related declaration has no effect.
819
+ helpers do
820
+ def uncached_erb(template, options={})
821
+ erb(template, options.merge(:cache => false ))
822
+ end
823
+
824
+ ##
825
+ # Renders ERB partials
826
+ #
827
+ # ==== Examples
828
+ #
829
+ # <%= partial('shared/header') %> => renders app/views/shared/_header.erb
830
+ #
831
+ # <%= partial('snippets/snippet') %> => renders app/views/snippets/_snippet.erb
832
+ #
833
+ #
834
+ # @api public
835
+ def partial(template, *args)
836
+ template_array = template.to_s.split('/')
837
+ template = template_array[0..-2].join('/') + "/_#{template_array[-1]}"
838
+ options = args.last.is_a?(Hash) ? args.pop : {}
839
+ options.merge!(:layout => false, :cache => false) # add support for Sinatra::Cache
840
+ # options[:outvar] = '@_out_buf'
841
+ if collection = options.delete(:collection) then
842
+ collection.inject([]) do |buffer, member|
843
+ buffer << erb(:"#{template}", options.merge(:layout => false, :locals => { template_array[-1].to_sym => member } ) )
844
+ end.join("\n")
845
+ else
846
+ erb(:"#{template}", options)
847
+ end
848
+ rescue Errno::ENOENT => e
849
+ out = "ERROR: The partial [views/#{template.to_s}] is missing."
850
+ out << " Please create it to remove this error. [ Exception: #{h e.inspect}]" if self.class.development? || self.class.test?
851
+ out
852
+ rescue Exception => e
853
+ if self.class.production?
854
+ # TODO:: must store the error in the log somehow.
855
+ "<!-- ERROR: rendering the partial [#{template.to_s}] -->"
856
+ else
857
+ throw e
858
+ end
859
+ end
860
+
861
+ end
862
+
863
+ get('/') { erb(:"home/index") }
864
+ # get('/erb/?') { erb(:index, :layout => false) }
865
+
866
+ # complex regex URL, matches
867
+ get %r{^/params/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
868
+ @captures = params[:captures].nil? ? [] : params[:captures].compact
869
+ erb(:params)
870
+ end
871
+ get('/file-extensions.*') do
872
+ @vars = { :framework => "Sinatra", :url => "www.sinatrarb.com" }
873
+ case params[:splat].first.to_sym
874
+ when :json
875
+ erb(@vars.to_json, :layout => false )
876
+ when :yaml
877
+ erb(@vars.to_yaml,:layout => false)
878
+ else
879
+ # direct output, should NOT be Cached
880
+ @vars.inspect
881
+ # @vars.inspect
882
+ end
883
+ end
884
+
885
+ ## NO CACHING
886
+ get('/uncached/erb'){ erb(:index, :cache => false) }
887
+ get('/uncached/erb/no/layout'){ erb(:index, :cache => false, :layout => false ) }
888
+ get('/uncached/uncached_erb'){ uncached_erb(:index) }
889
+
890
+ get '/css/screen.css' do
891
+ content_type 'text/css'
892
+ sass(:css, :style => :compact)
893
+ end
894
+ get '/css/no/cache.css' do
895
+ content_type 'text/css'
896
+ sass(:css, :style => :compact, :cache => false)
897
+ end
898
+
899
+ # enable :method_override
900
+ post('/post/?') { erb("POST", :layout => false) }
901
+ put('/put/?') { erb('PUT', :layout => false) }
902
+ delete('/delete/?') { erb('DELETE', :layout => false) }
903
+
904
+ get %r{^/fragments/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
905
+ erb(:fragments, :layout => false, :cache => false)
906
+ end
907
+ get %r{^/sharedfragments/?([\s\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?/?([\w-]+)?} do
908
+ erb(:fragments_shared, :layout => false, :cache => false)
909
+ end
910
+
911
+ end
912
+
913
+ def app; ::MyPartialsTestApp.new ; end
914
+
915
+ describe "basic Page caching" do
916
+
917
+ describe "GET requests for" do
918
+
919
+ describe "the Home page - ['/' => /index.html] (with layout)" do
920
+
921
+ before(:each) do
922
+ @cache_file = "#{test_cache_path('system/cache')}/index.html"
923
+ get('/')
924
+ end
925
+
926
+ after(:each) do
927
+ FileUtils.rm(@cache_file) if @delete_cached_test_files
928
+ end
929
+
930
+ it "should render the expected output" do
931
+ # <html>
932
+ # <head>
933
+ # <title>Sinatra::Cache</title>
934
+ # <!-- page cached: 2010-11-15 18:21:06 -->
935
+ # </head>
936
+ # <body>
937
+ # <div id="header">
938
+ # <h1>HELLO from [content_for(:section_header)]</h1>
939
+ # </div>
940
+ # <h2>HELLO FROM [FILE ../sinatra-cache/spec/fixtures/apps/partials/views/home/index.erb]</h2>
941
+ # </body>
942
+ # </html>
943
+ body.should have_tag('html > head > title', 'Sinatra::Cache')
944
+ body.should match(/<!-- page cached: \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d -->/)
945
+ body.should have_tag('html > body > div#header > h1', /HELLO from \[content_for\(:section_header\)\]/)
946
+ body.should have_tag('html > body > h2', /HELLO FROM \[/)
947
+ end
948
+
949
+ it "should create a cached file in the cache output directory" do
950
+ test(?f, @cache_file).should == true
951
+ end
952
+
953
+ end #/ GET ['/' => /index.html]
954
+
955
+ end #/ GET requests
956
+
957
+ end #/ basic Page caching
958
+
959
+
960
+ end #/ Using nested buffers
961
+
962
+ end #/ EDGE cases
963
+
964
+
965
+ end #/ Cache
966
+
967
+ end #/ Sinatra