fivepointssolutions-serve 0.9.11

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.
@@ -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