fivepointssolutions-serve 0.9.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ class ServeController < ActionController::Base
2
+ def show
3
+ response.headers.delete('Cache-Control')
4
+ cache = Serve::Rails.cache(request)
5
+ if cache.response_cached?(request.path)
6
+ cache.update_response(request.path, response, request)
7
+ else
8
+ mount = Serve::Rails.configuration.mounts.detect {|m| m.route == params[:serve_route]}
9
+ if path = mount.resolve_path(params[:path] || '/')
10
+ handler_class = Serve::FileTypeHandler.find(path)
11
+ handler = handler_class.new(mount.root_path, path)
12
+ install_view_helpers(handler, mount.view_helpers) if handler_class == Serve::DynamicHandler
13
+ handler.process(request, response)
14
+ cache.cache_response(request.path, response)
15
+ else
16
+ render :text => 'not found', :status => 404
17
+ end
18
+ end
19
+ @performed_render = true
20
+ end
21
+
22
+ private
23
+
24
+ # This is a quick solution: We need to install the view helpers defined by
25
+ # the Rails application after those of the Serve'd app's view helpers, so
26
+ # that those of the Rails application override them.
27
+ #
28
+ # Ideally, we'll work toward moving some of this Rails stuff further up
29
+ # into Serve, so that a simple Serve'd directory uses almost all of the
30
+ # same code, like the Configuration.
31
+ #
32
+ def install_view_helpers(handler, view_helpers)
33
+ controller = self
34
+ handler.metaclass.module_eval do
35
+ define_method :install_view_helpers do |context|
36
+ super(context)
37
+ # Make available to view helpers
38
+ context.instance_variable_set('@controller', controller)
39
+ context.extend view_helpers
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module Serve
2
+ module Rails
3
+ class Mount # :nodoc:
4
+ attr_reader :root_path, :route, :view_helpers
5
+
6
+ def initialize(route, root)
7
+ @route, @root_path = route, root
8
+ @view_helpers = Module.new
9
+ end
10
+
11
+ # Appends to the collection of view helpers that will be made availabe
12
+ # to the DynamicHandler.
13
+ #
14
+ def append_view_helper(m)
15
+ @view_helpers.module_eval do
16
+ include m
17
+ end
18
+ end
19
+
20
+ def connection
21
+ @route == '/' ? '*path' : "#{@route}/*path"
22
+ end
23
+
24
+ def resolve_path(path)
25
+ Serve.resolve_file(@root_path, path)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ module Serve
2
+ module Rails
3
+ module Routing
4
+
5
+ module MapperExtensions
6
+ def serve
7
+ serve_mounts = Serve::Rails.configuration.mounts
8
+ default_site_path = File.join(::Rails.root, 'site')
9
+
10
+ if File.directory?(default_site_path) && !serve_mounts.detect {|m| m.route == '/'}
11
+ mount('/', default_site_path)
12
+ end
13
+
14
+ serve_mounts.each do |mount|
15
+ @set.add_route(mount.connection, {
16
+ :controller => 'serve', :action => 'show',
17
+ :serve_route => mount.route
18
+ })
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,172 @@
1
+ # This code and it's tests are adapted from the Radiant CMS project.
2
+ #
3
+ # Copyright (c) 2006-2007, John W. Long.
4
+
5
+ require 'uri'
6
+
7
+ module Serve
8
+ class ResponseCache
9
+ @@defaults = {
10
+ :expire_time => 5.minutes,
11
+ :default_extension => '.yml',
12
+ :perform_caching => true,
13
+ :use_x_sendfile => false
14
+ }
15
+ cattr_accessor :defaults
16
+
17
+ attr_accessor :directory, :expire_time, :default_extension, :perform_caching, :logger, :use_x_sendfile
18
+ alias :page_cache_directory :directory
19
+ alias :page_cache_extension :default_extension
20
+ private :page_cache_directory, :page_cache_extension
21
+
22
+ # Creates a ResponseCache object with the specified options.
23
+ #
24
+ # Options are as follows:
25
+ # :directory :: the path to the temporary cache directory
26
+ # :expire_time :: the number of seconds a cached response is considered valid (defaults to 5 min)
27
+ # :default_extension :: the extension cached files should use (defaults to '.yml')
28
+ # :perform_caching :: boolean value that turns caching on or off (defaults to true)
29
+ # :logger :: the application logging object
30
+ # :use_x_sendfile :: use X-Sendfile headers to speed up transfer of cached pages (not available on all web servers)
31
+ #
32
+ def initialize(options = {})
33
+ options = options.symbolize_keys.reverse_merge(defaults)
34
+ self.directory = options[:directory]
35
+ self.expire_time = options[:expire_time]
36
+ self.default_extension = options[:default_extension]
37
+ self.perform_caching = options[:perform_caching]
38
+ self.logger = options[:logger]
39
+ self.use_x_sendfile = options[:use_x_sendfile]
40
+ end
41
+
42
+ # Caches a response object for path to disk.
43
+ def cache_response(path, response)
44
+ if perform_caching
45
+ path = clean(path)
46
+ write_response(path, response)
47
+ end
48
+ response
49
+ end
50
+
51
+ # If perform_caching is set to true, updates a response object so that it mirrors the
52
+ # cached version. The request object is required to perform Last-Modified/If-Modified-Since
53
+ # checks--it is left optional to allow for backwards compatability.
54
+ def update_response(path, response, request=nil)
55
+ if perform_caching
56
+ path = clean(path)
57
+ read_response(path, response, request)
58
+ end
59
+ response
60
+ end
61
+
62
+ # Returns metadata for path.
63
+ def read_metadata(path)
64
+ path = clean(path)
65
+ name = "#{page_cache_path(path)}.yml"
66
+ if File.exists?(name) and not File.directory?(name)
67
+ content = File.open(name, "rb") { |f| f.read }
68
+ metadata = YAML::load(content)
69
+ metadata if metadata['expires'] >= Time.now
70
+ end
71
+ rescue
72
+ nil
73
+ end
74
+
75
+ # Returns true if a response is cached at the specified path.
76
+ def response_cached?(path)
77
+ perform_caching && !!read_metadata(path)
78
+ end
79
+
80
+ # Expires the cached response for the specified path.
81
+ def expire_response(path)
82
+ path = clean(path)
83
+ expire_page(path)
84
+ end
85
+
86
+ # Expires the entire cache.
87
+ def clear
88
+ Dir["#{directory}/*"].each do |f|
89
+ FileUtils.rm_rf f
90
+ end
91
+ end
92
+
93
+ private
94
+ # Ensures that path begins with a slash and remove extra slashes.
95
+ def clean(path)
96
+ path = path.gsub(%r{/+}, '/')
97
+ %r{^/?(.*?)/?$}.match(path)
98
+ "/#{$1}"
99
+ end
100
+
101
+ # Reads a cached response from disk and updates a response object.
102
+ def read_response(path, response, request)
103
+ file_path = page_cache_path(path)
104
+ if metadata = read_metadata(path)
105
+ response.headers.merge!(metadata['headers'] || {})
106
+ if client_has_cache?(metadata, request)
107
+ response.headers.merge!('Status' => '304 Not Modified')
108
+ elsif use_x_sendfile
109
+ response.headers.merge!('X-Sendfile' => "#{file_path}.data")
110
+ else
111
+ response.body = File.open("#{file_path}.data", "rb") {|f| f.read}
112
+ end
113
+ end
114
+ response
115
+ end
116
+
117
+ def client_has_cache?(metadata, request)
118
+ return false unless request
119
+ request_time = Time.httpdate(request.env["HTTP_IF_MODIFIED_SINCE"]) rescue nil
120
+ response_time = Time.httpdate(metadata['headers']['Last-Modified']) rescue nil
121
+ return request_time && response_time && response_time <= request_time
122
+ end
123
+
124
+ # Writes a response to disk.
125
+ def write_response(path, response)
126
+ if response.cache_timeout
127
+ if Time === response.cache_timeout
128
+ expires = response.cache_timeout
129
+ else
130
+ expires = Time.now + response.cache_timeout
131
+ end
132
+ else
133
+ expires = Time.now + self.expire_time
134
+ end
135
+ response.headers['Last-Modified'] ||= Time.now.httpdate
136
+ metadata = {
137
+ 'headers' => response.headers,
138
+ 'expires' => expires
139
+ }.to_yaml
140
+ cache_page(metadata, response.body, path)
141
+ end
142
+
143
+ def page_cache_path(path)
144
+ path = (path.empty? || path == "/") ? "/_site-root" : URI.unescape(path)
145
+ root_dir = File.expand_path(page_cache_directory)
146
+ cache_path = File.expand_path(File.join(root_dir, path), root_dir)
147
+ cache_path if cache_path.index(root_dir) == 0
148
+ end
149
+
150
+ def expire_page(path)
151
+ return unless perform_caching
152
+
153
+ if path = page_cache_path(path)
154
+ File.delete("#{path}.yml") if File.exists?("#{path}.yml")
155
+ File.delete("#{path}.data") if File.exists?("#{path}.data")
156
+ end
157
+ end
158
+
159
+ def cache_page(metadata, content, path)
160
+ return unless perform_caching
161
+
162
+ if path = page_cache_path(path)
163
+ FileUtils.makedirs(File.dirname(path))
164
+ #dont want yml without data
165
+ File.open("#{path}.data", "wb+") { |f| f.write(content) }
166
+ File.open("#{path}.yml", "wb+") { |f| f.write(metadata) }
167
+ end
168
+ end
169
+ end
170
+
171
+ NULL_CACHE = ResponseCache.new(:perform_caching => false)
172
+ end
@@ -0,0 +1,13 @@
1
+ module Serve #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 9
5
+ TINY = 11
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+
10
+ def self.version
11
+ VERSION::STRING
12
+ end
13
+ end
@@ -0,0 +1,98 @@
1
+ # Portions from Rails, Copyright (c) 2004-2008 David Heinemeier Hansson
2
+ require 'active_support/memoizable'
3
+
4
+ module Serve #:nodoc:
5
+ module WEBrick #:nodoc:
6
+
7
+ module FileHandlerExtensions #:nodoc:
8
+ def self.included(base)
9
+ base.extend(self)
10
+ base.class_eval do
11
+ alias :search_file_without_auto_appending :search_file
12
+ alias :search_file :search_file_with_auto_appending
13
+ end
14
+ end
15
+
16
+ def search_file_with_auto_appending(req, res, basename)
17
+ full_path = File.join(res.filename, basename)
18
+ return basename if File.file?(full_path)
19
+ return nil if File.directory?(full_path)
20
+ Serve.resolve_file(Dir.pwd, req.path)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+ WEBrick::HTTPRequest.module_eval do
28
+ extend ActiveSupport::Memoizable
29
+
30
+ alias headers header
31
+
32
+ # Returns the \host for this request, such as "example.com".
33
+ def raw_host_with_port
34
+ @host + ':' + @port.to_s
35
+ end
36
+
37
+ # Returns 'https://' if this is an SSL request and 'http://' otherwise.
38
+ def protocol
39
+ ssl? ? 'https://' : 'http://'
40
+ end
41
+ memoize :protocol
42
+
43
+ # Is this an SSL request?
44
+ def ssl?
45
+ meta_vars['HTTPS'] == 'on'
46
+ end
47
+
48
+ def params
49
+ query.inject({}) {|m, (k,v)| m[k.to_s.to_sym] = v; m}
50
+ end
51
+
52
+ # Returns the host for this request, such as example.com.
53
+ def host
54
+ raw_host_with_port.sub(/:\d+$/, '')
55
+ end
56
+ memoize :host
57
+
58
+ # Returns a \host:\port string for this request, such as "example.com" or
59
+ # "example.com:8080".
60
+ def host_with_port
61
+ "#{host}#{port_string}"
62
+ end
63
+ memoize :host_with_port
64
+
65
+ # Returns the port number of this request as an integer.
66
+ def port
67
+ if raw_host_with_port =~ /:(\d+)$/
68
+ $1.to_i
69
+ else
70
+ standard_port
71
+ end
72
+ end
73
+ memoize :port
74
+
75
+ # Returns the standard \port number for this request's protocol.
76
+ def standard_port
77
+ case protocol
78
+ when 'https://' then 443
79
+ else 80
80
+ end
81
+ end
82
+
83
+ # Returns a \port suffix like ":8080" if the \port number of this request
84
+ # is not the default HTTP \port 80 or HTTPS \port 443.
85
+ def port_string
86
+ port == standard_port ? '' : ":#{port}"
87
+ end
88
+ end
89
+
90
+ WEBrick::HTTPResponse.module_eval do
91
+ alias headers header
92
+
93
+ def redirect(url, status)
94
+ set_redirect(::WEBrick::HTTPStatus[status.to_i], url)
95
+ end
96
+ end
97
+
98
+ WEBrick::HTTPServlet::FileHandler.class_eval { include Serve::WEBrick::FileHandlerExtensions }
@@ -0,0 +1,17 @@
1
+ module Serve #:nodoc:
2
+ module WEBrick
3
+ class Server < ::WEBrick::HTTPServer #:nodoc:
4
+ def self.register_handlers
5
+ extensions = []
6
+ Serve::FileTypeHandler.handlers.each do |ext, handler|
7
+ extensions << ext
8
+ handler_servlet = Class.new(Serve::WEBrick::Servlet) do
9
+ define_method(:handler) { handler }
10
+ end
11
+ ::WEBrick::HTTPServlet::FileHandler.add_handler(ext, handler_servlet)
12
+ end
13
+ extensions
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ module Serve #:nodoc:
2
+ module WEBrick
3
+ class Servlet < ::WEBrick::HTTPServlet::AbstractServlet #:nodoc:
4
+ def do_GET(req, res)
5
+ begin
6
+ path = Serve.resolve_file(Dir.pwd, req.path)
7
+ handler.new(Dir.pwd, path).process(req, res)
8
+ rescue StandardError => ex
9
+ raise
10
+ rescue Exception => ex
11
+ @logger.error(ex)
12
+ raise ::WEBrick::HTTPStatus::InternalServerError, ex.message
13
+ end
14
+ end
15
+
16
+ alias do_POST do_GET
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,248 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Serve::ResponseCache do
4
+ class SilentLogger
5
+ def method_missing(*args); end
6
+ end
7
+
8
+ class TestRequest < Struct.new(:body, :env)
9
+ end
10
+
11
+ class TestResponse < Struct.new(:body, :headers, :cache_timeout)
12
+ def initialize(body = '', headers = {})
13
+ self.body = body
14
+ self.headers = headers
15
+ end
16
+ end
17
+
18
+ before :all do
19
+ @dir = File.expand_path("#{File.expand_path(File.dirname(__FILE__))}/tmp/cache")
20
+ @baddir = File.expand_path("#{File.expand_path(File.dirname(__FILE__))}/tmp/badcache")
21
+ end
22
+
23
+ before :each do
24
+ FileUtils.rm_rf @baddir
25
+ @cache = Serve::ResponseCache.new(
26
+ :directory => @dir,
27
+ :perform_caching => true
28
+ )
29
+ @cache.clear
30
+ end
31
+
32
+ after :each do
33
+ FileUtils.rm_rf @dir if File.exists? @dir
34
+ end
35
+
36
+ it 'should initialize with defaults' do
37
+ @cache = Serve::ResponseCache.new
38
+ @cache.expire_time.should == 5.minutes
39
+ @cache.default_extension.should == '.yml'
40
+ end
41
+
42
+ it 'should initialize with options' do
43
+ @cache = Serve::ResponseCache.new(
44
+ :directory => "test",
45
+ :expire_time => 5,
46
+ :default_extension => ".xhtml",
47
+ :perform_caching => false,
48
+ :logger => SilentLogger.new
49
+ )
50
+ @cache.directory.should == "test"
51
+ @cache.expire_time.should == 5
52
+ @cache.default_extension.should == ".xhtml"
53
+ @cache.perform_caching.should == false
54
+ @cache.logger.should be_kind_of(SilentLogger)
55
+ end
56
+
57
+ it 'should cache response' do
58
+ ['test/me', '/test/me', 'test/me/', '/test/me/', 'test//me'].each do |url|
59
+ @cache.clear
60
+ response = response('content', 'Last-Modified' => 'Tue, 27 Feb 2007 06:13:43 GMT')
61
+ response.cache_timeout = Time.gm(2007, 2, 8, 17, 37, 9)
62
+ @cache.cache_response(url, response)
63
+ name = "#{@dir}/test/me.yml"
64
+ File.exists?(name).should == true
65
+ file(name).should == "--- \nexpires: 2007-02-08 17:37:09 Z\nheaders: \n Last-Modified: Tue, 27 Feb 2007 06:13:43 GMT\n"
66
+ data_name = "#{@dir}/test/me.data"
67
+ file(data_name).should == "content"
68
+ end
69
+ end
70
+
71
+ it 'cache response with extension' do
72
+ @cache.cache_response("styles.css", response('content'))
73
+ File.exists?("#{@dir}/styles.css.yml").should == true
74
+ end
75
+
76
+ it 'cache response without caching' do
77
+ @cache.perform_caching = false
78
+ @cache.cache_response('test', response('content'))
79
+ File.exists?("#{@dir}/test.yml").should == false
80
+ end
81
+
82
+ it 'update response' do
83
+ @cache.cache_response('/test/me', response('content'))
84
+ ['test/me', '/test/me', 'test/me/', '/test/me/', 'test//me'].each do |url|
85
+ @cache.update_response(url, response, TestRequest).body.should == 'content'
86
+ end
87
+ end
88
+
89
+ it 'update response nonexistant' do
90
+ @cache.update_response('nothing/here', response, TestRequest).body.should == ''
91
+ end
92
+
93
+ it 'update response without caching' do
94
+ @cache.cache_response('/test/me', response('content'))
95
+ @cache.perform_caching = false
96
+ @cache.update_response('/test/me', response, TestRequest).body.should == ''
97
+ end
98
+
99
+ it 'cache' do
100
+ result = @cache.cache_response('test', response('content', 'Content-Type' => 'text/plain'))
101
+ cached = @cache.update_response('test', response, TestRequest)
102
+ cached.body.should == 'content'
103
+ cached.headers['Content-Type'].should == 'text/plain'
104
+ result.should be_kind_of(TestResponse)
105
+ end
106
+
107
+ it 'expire response' do
108
+ @cache.cache_response('test', response('content'))
109
+ @cache.expire_response('test')
110
+ @cache.update_response('test', response, TestRequest).body.should == ''
111
+ end
112
+
113
+ it 'clear' do
114
+ @cache.cache_response('test1', response('content'))
115
+ @cache.cache_response('test2', response('content'))
116
+ Dir["#{@dir}/*"].size.should == 4
117
+
118
+ @cache.clear
119
+ Dir["#{@dir}/*"].size.should == 0
120
+ end
121
+
122
+ it 'response_cached?' do
123
+ @cache.response_cached?('test').should == false
124
+ result = @cache.cache_response('test', response('content'))
125
+ @cache.response_cached?('test').should == true
126
+ end
127
+
128
+ it 'response_cached? should not answer true when response is cached but preform_caching option is false' do
129
+ @cache.cache_response('test', response('content'))
130
+ @cache.perform_caching = false
131
+ @cache.response_cached?('test').should == false
132
+ end
133
+
134
+ it 'response_cached? with timeout' do
135
+ @cache.expire_time = 1
136
+ result = @cache.cache_response('test', response('content'))
137
+ sleep 1.5
138
+ @cache.response_cached?('test').should == false
139
+ end
140
+
141
+ it 'response_cached? timeout with response setting' do
142
+ @cache.expire_time = 1
143
+ response = response('content')
144
+ response.cache_timeout = 3.seconds
145
+ result = @cache.cache_response('test', response)
146
+ sleep 1.5
147
+ @cache.response_cached?('test').should == true
148
+ sleep 2
149
+ @cache.response_cached?('test').should == false
150
+ end
151
+
152
+ it 'send using x_sendfile header' do
153
+ @cache.use_x_sendfile = true
154
+ result = @cache.cache_response('test', response('content', 'Content-Type' => 'text/plain'))
155
+ cached = @cache.update_response('test', response, TestRequest)
156
+ cached.body.should == ''
157
+ cached.headers['X-Sendfile'].should == "#{@dir}/test.data"
158
+ cached.headers['Content-Type'].should == 'text/plain'
159
+ result.should be_kind_of(TestResponse)
160
+ end
161
+
162
+ it 'send cached page with last modified' do
163
+ last_modified = Time.now.httpdate
164
+ result = @cache.cache_response('test', response('content', 'Last-Modified' => last_modified))
165
+ request = TestRequest.new
166
+ request.env = { 'HTTP_IF_MODIFIED_SINCE' => last_modified }
167
+ second_call = @cache.update_response('test', response, request)
168
+ second_call.headers['Status'].should match(/^304/)
169
+ second_call.body.should == ''
170
+ result.should be_kind_of(TestResponse)
171
+ end
172
+
173
+ it 'send cached page with old last modified' do
174
+ last_modified = Time.now.httpdate
175
+ result = @cache.cache_response('test', response('content', 'Last-Modified' => last_modified))
176
+ request = TestRequest.new
177
+ request.env = { 'HTTP_IF_MODIFIED_SINCE' => 5.minutes.ago.httpdate }
178
+ second_call = @cache.update_response('test', response, request)
179
+ second_call.body.should == 'content'
180
+ result.should be_kind_of(TestResponse)
181
+ end
182
+
183
+ it 'not cached if metadata empty' do
184
+ FileUtils.makedirs(@dir)
185
+ File.open("#{@dir}/test_me.yml", 'w') { }
186
+ @cache.response_cached?('/test_me').should == false
187
+ end
188
+
189
+ it 'not cached if metadata broken' do
190
+ FileUtils.makedirs(@dir)
191
+ File.open("#{@dir}/test_me.yml", 'w') {|f| f.puts '::: bad yaml file:::' }
192
+ @cache.response_cached?('/test_me').should == false
193
+ end
194
+
195
+ it 'not cached if metadata not hash' do
196
+ FileUtils.makedirs(@dir)
197
+ File.open("#{@dir}/test_me.yml", 'w') {|f| f.puts ':symbol' }
198
+ @cache.response_cached?('/test_me').should == false
199
+ end
200
+
201
+ it 'not cached if metadata has no expire' do
202
+ FileUtils.makedirs(@dir)
203
+ File.open("#{@dir}/test_me.yml", 'w') { |f| f.puts "--- \nheaders: \n Last-Modified: Tue, 27 Feb 2007 06:13:43 GMT\n" }
204
+ @cache.response_cached?('/test_me').should == false
205
+ end
206
+
207
+ it 'cache cant write outside dir' do
208
+ @cache.cache_response('../badcache/cache_cant_write_outside_dir', response('content'))
209
+ File.exist?("#{@baddir}/cache_cant_write_outside_dir.yml").should == false
210
+ end
211
+
212
+ it 'cache cannot read outside dir' do
213
+ FileUtils.makedirs(@baddir)
214
+ @cache.cache_response('/test_me', response('content'))
215
+ File.rename "#{@dir}/test_me.yml", "#{@baddir}/test_me.yml"
216
+ File.rename "#{@dir}/test_me.data", "#{@baddir}/test_me.data"
217
+ @cache.response_cached?('/../badcache/test_me').should == false
218
+ end
219
+
220
+ it 'cache cannot expire outside dir' do
221
+ FileUtils.makedirs(@baddir)
222
+ @cache.cache_response('/test_me', response('content'))
223
+ File.rename "#{@dir}/test_me.yml", "#{@baddir}/test_me.yml"
224
+ File.rename "#{@dir}/test_me.data", "#{@baddir}/test_me.data"
225
+ @cache.expire_response('/../badcache/test_me')
226
+ File.exist?("#{@baddir}/test_me.yml").should == true
227
+ File.exist?("#{@baddir}/test_me.data").should == true
228
+ end
229
+
230
+ it 'should store indexes correctly' do
231
+ @cache.cache_response('/', response('content'))
232
+ @cache.response_cached?('_site-root').should == true
233
+ @cache.response_cached?('/') .should == true
234
+ File.exist?("#{@dir}/../cache.yml").should == false
235
+ File.exist?("#{@dir}/../cache.data").should == false
236
+ end
237
+
238
+ private
239
+
240
+ def file(filename)
241
+ open(filename) { |f| f.read } rescue ''
242
+ end
243
+
244
+ def response(*args)
245
+ TestResponse.new(*args)
246
+ end
247
+
248
+ end