fir 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +33 -8
- data/bin/fir +7 -12
- data/example/Rakefile +4 -1
- data/example/deploy.yml +3 -0
- data/example/public/cache/404.html +46 -0
- data/example/public/cache/contact-us.html +48 -0
- data/example/public/cache/index.html +52 -0
- data/example/public/cache/products/dynamite-plus.html +52 -0
- data/example/public/cache/products/dynamite.html +52 -0
- data/example/public/cache/products/index.html +58 -0
- data/example/public/cache/products/mouse-trap.html +52 -0
- data/example/users.yml +0 -1
- data/lib/fir/admin.rb +9 -6
- data/lib/fir/generator.rb +33 -0
- data/lib/fir/tasks.rb +58 -7
- data/skeleton/public/dispatch.cgi +34 -0
- data/skeleton/public/htaccess +3 -0
- data/spec/admin_spec.rb +172 -0
- data/spec/fir_spec.rb +75 -48
- data/spec/generator_spec.rb +132 -0
- data/spec/rack_integration_spec.rb +44 -0
- data/spec/spec_helper.rb +16 -2
- data/spec/support/cache_testing.rb +20 -0
- data/spec/support/file_matchers.rb +22 -0
- data/spec/support/server_matchers.rb +39 -0
- data/spec/support/server_testing.rb +35 -0
- data/spec/support/test_app_setup.rb +11 -0
- data/spec/tasks_spec.rb +132 -0
- metadata +85 -9
data/example/users.yml
CHANGED
data/lib/fir/admin.rb
CHANGED
@@ -53,12 +53,14 @@ module Fir
|
|
53
53
|
if match
|
54
54
|
page = Fir.sanitize_page(match[1])
|
55
55
|
page_path = File.join(FIR_ROOT, 'pages', page)
|
56
|
-
if
|
56
|
+
if env['REQUEST_METHOD'].downcase == 'post'
|
57
|
+
update_page(page_path, env)
|
58
|
+
elsif File.exists?(page_path)
|
57
59
|
case env['REQUEST_METHOD'].downcase
|
58
60
|
when 'get'
|
59
61
|
retrieve_page(page_path)
|
60
|
-
when '
|
61
|
-
|
62
|
+
when 'delete'
|
63
|
+
return method_not_allowed('DELETE not yet implemented')
|
62
64
|
else
|
63
65
|
return method_not_allowed('Only GET and POST are allowed')
|
64
66
|
end
|
@@ -109,7 +111,7 @@ module Fir
|
|
109
111
|
if File.readable?(page_path)
|
110
112
|
source = File.read(page_path)
|
111
113
|
directory, filename, ext = Fir.split_path(page_path)
|
112
|
-
filename_with_ext = filename +
|
114
|
+
filename_with_ext = filename + ext
|
113
115
|
[200, {'Content-Type' => 'text/plain', 'Content-Disposition' => "attachment; #{filename_with_ext}"}, source]
|
114
116
|
else
|
115
117
|
internal_server_error('File exists on disk, but is not readable. Are the permissions wrong?')
|
@@ -117,12 +119,13 @@ module Fir
|
|
117
119
|
end
|
118
120
|
|
119
121
|
def update_page(page_path, env)
|
120
|
-
if File.writable?(page_path)
|
122
|
+
if File.writable?(page_path) or (!File.exists?(page_path) and File.writable?(File.dirname(page_path)))
|
121
123
|
File.open(page_path, 'w') do |file|
|
122
124
|
request = ::Rack::Request.new(env)
|
123
125
|
file.write(request.POST['content'])
|
124
126
|
end
|
125
|
-
|
127
|
+
# page_path will be an absolute path at this point, but clear_cache expects it to be relative to the pages directory
|
128
|
+
Fir.clear_cache(page_path.sub(File.join(FIR_ROOT, 'pages'), ''))
|
126
129
|
[200, {'Content-Type' => 'text/plain'}, "#{page_path} has been saved."]
|
127
130
|
else
|
128
131
|
internal_server_error('File exists on disk, but is not writeable. Are the permissions wrong?')
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Fir
|
2
|
+
def self.generate!(args)
|
3
|
+
::Fir::Generator.new.generate!(args)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Generator
|
7
|
+
def generate!(args)
|
8
|
+
unless args.length >= 1
|
9
|
+
raise 'Usage: fir path'
|
10
|
+
end
|
11
|
+
|
12
|
+
fir_root = args.shift
|
13
|
+
|
14
|
+
if File.exists?(fir_root)
|
15
|
+
raise "#{fir_root} already exists! Aborting."
|
16
|
+
end
|
17
|
+
FileUtils.cp_r FIR_SKELETON_ROOT, fir_root
|
18
|
+
# What's the deal with .htaccess being renamed, you ask? Rubygems doesn't want to include .htaccess
|
19
|
+
# in the package, so we have to distribute it as htaccess and then rename it at the last moment.
|
20
|
+
FileUtils.mv File.join(fir_root, 'public', 'htaccess'), File.join(fir_root, 'public', '.htaccess')
|
21
|
+
puts "Created new Fir site in #{fir_root}"
|
22
|
+
|
23
|
+
[
|
24
|
+
['--with-dispatch-cgi', 'public/dispatch.cgi'],
|
25
|
+
['--with-htaccess', 'public/.htaccess']
|
26
|
+
].each do |option, file|
|
27
|
+
unless args.include?(option)
|
28
|
+
FileUtils.rm File.join(fir_root, file)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/fir/tasks.rb
CHANGED
@@ -8,7 +8,8 @@ module Fir
|
|
8
8
|
require 'fir'
|
9
9
|
require 'active_support/secure_random'
|
10
10
|
unless (ENV.has_key?('user') or ENV.has_key?('USER')) and (ENV.has_key?('pass') or ENV.has_key?('PASS'))
|
11
|
-
|
11
|
+
puts 'usage: rake users:add user=username pass=password'
|
12
|
+
exit
|
12
13
|
end
|
13
14
|
user = ENV['user'] || ENV['USER']
|
14
15
|
pass = ENV['pass'] || ENV['PASS']
|
@@ -27,13 +28,20 @@ module Fir
|
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
30
|
-
desc 'Export the site to static HTML. (For web hosts that
|
31
|
+
desc 'Export the site to static HTML. (For web hosts that don\'t support Rack. Creates an .htaccess file for pretty URLs. mod_rewrite must be enabled!) ' +
|
31
32
|
'Usage: rake export dir=path_to_export_to'
|
32
33
|
task :export do
|
33
34
|
require 'fileutils'
|
34
35
|
|
35
36
|
if !ENV.has_key?('dir') or ENV['dir'].empty?
|
36
|
-
|
37
|
+
puts 'Usage: rake export dir=path_to_export_to'
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
|
41
|
+
copy_to = ENV['dir']
|
42
|
+
unless ENV['force'] or !File.exists?(copy_to) or Dir.entries(copy_to).length == 2
|
43
|
+
puts 'Directory is not empty. Export aborted. Use "rake export dir=path force=1" to override. This will delete everything in the directory!'
|
44
|
+
exit
|
37
45
|
end
|
38
46
|
|
39
47
|
page_config = YAML.load(File.read(File.join(FIR_ROOT, 'pages.yml')))
|
@@ -54,7 +62,7 @@ module Fir
|
|
54
62
|
result
|
55
63
|
end + path_mappings.keys
|
56
64
|
|
57
|
-
puts
|
65
|
+
puts "Iniitalizing Fir site in #{copy_to}..."
|
58
66
|
|
59
67
|
Fir.config.force_caching = true
|
60
68
|
require File.expand_path(File.join(FIR_ROOT, 'config.rb'))
|
@@ -67,10 +75,10 @@ module Fir
|
|
67
75
|
end
|
68
76
|
|
69
77
|
copy_from = File.join(FIR_ROOT, 'public', '.')
|
70
|
-
copy_to
|
71
|
-
|
72
|
-
Dir.mkdir(copy_to)
|
78
|
+
if File.exists?(copy_to)
|
79
|
+
FileUtils.rm_rf(copy_to)
|
73
80
|
end
|
81
|
+
Dir.mkdir(copy_to)
|
74
82
|
FileUtils.cp_r(copy_from, copy_to)
|
75
83
|
FileUtils.cp_r(File.join(copy_to, 'cache', '.'), copy_to)
|
76
84
|
FileUtils.rm_rf(File.join(copy_to, 'cache'))
|
@@ -91,6 +99,49 @@ module Fir
|
|
91
99
|
f.write(htaccess)
|
92
100
|
end
|
93
101
|
end
|
102
|
+
|
103
|
+
desc "Export the site to static HTML and transfer it to the server using rsync and SSH. (For web hosts that don't support Rack. " +
|
104
|
+
"Creates an .htaccess file for pretty URLs. mod_rewrite must be enabled!) " +
|
105
|
+
"Example usage: rake export_to_server dest=username@example.com:/home/username/public_html\n\n" +
|
106
|
+
"Obviously, dest should be set to the correct rsync destination for your deploy environment. " +
|
107
|
+
"Note that dest is NOT a URI, even though it sort of looks like one. It's a destination string in the form that rsync accepts."
|
108
|
+
task :export_to_server do
|
109
|
+
begin
|
110
|
+
ENV['dir'] = '.server_export_tmp'
|
111
|
+
Rake::Task['export'].execute
|
112
|
+
if ENV['dest']
|
113
|
+
dest = ENV['dest']
|
114
|
+
elsif File.exists?(File.join(FIR_ROOT, 'deploy.yml'))
|
115
|
+
yaml = YAML.load(File.read(File.join(FIR_ROOT, 'deploy.yml')))
|
116
|
+
['host', 'user', 'path'].each do |param|
|
117
|
+
unless yaml.has_key?(param)
|
118
|
+
puts "deploy.yml must specify #{param}"
|
119
|
+
exit
|
120
|
+
end
|
121
|
+
end
|
122
|
+
dest = "#{yaml['user']}@#{yaml['host']}:#{yaml['path']}"
|
123
|
+
else
|
124
|
+
puts "Example usage: rake export_to_server dest=username@example.com/home:/username/public_html"
|
125
|
+
exit
|
126
|
+
end
|
127
|
+
Rsyncer.new.run(dest)
|
128
|
+
ensure
|
129
|
+
FileUtils.rm_rf '.server_export_tmp'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Rsyncer
|
135
|
+
def run(dest)
|
136
|
+
run_command("rsync -ave ssh .server_export_tmp/* #{dest}")
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
# Stupid indirection to make it testable with should_receive and/or stub. (We don't want to run the real rsync from our test suite.)
|
142
|
+
def run_command(command)
|
143
|
+
system command
|
144
|
+
end
|
94
145
|
end
|
95
146
|
end
|
96
147
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# This script is called when running Fir as a CGI, which isn't recommended.
|
4
|
+
# Still, here it is, in case you want to try.
|
5
|
+
#
|
6
|
+
# This script will not be operational unless the webserver is configured properly.
|
7
|
+
# In Apache, you need to enable mod_rewrite in .htaccess files for this directory.
|
8
|
+
# That usually means setting AllowOverride to All (http://httpd.apache.org/docs/1.3/mod/core.html#allowoverride).
|
9
|
+
# Apache must also be told that CGI is allowed in this directory. This is done with the ExecCGI option
|
10
|
+
# (http://httpd.apache.org/docs/1.3/mod/core.html#options).
|
11
|
+
#
|
12
|
+
# Learn more about CGI under Apache: http://httpd.apache.org/docs/1.3/howto/cgi.html
|
13
|
+
#
|
14
|
+
# You must also set up rewrite rules so the requests get sent to this script.
|
15
|
+
# See how it's done in .htaccess in this directory.
|
16
|
+
#
|
17
|
+
# Besides the performance issues, CGI has another disadvantage. Apache (and perhaps others) unescapes
|
18
|
+
# URLs before sending them to your CGI script. Passenger does not. Fir, being designed for Passenger,
|
19
|
+
# unescapes URLs. So, when running Fir as a CGI, all URLs will be escaped twice. This breaks certain URLs.
|
20
|
+
# For example:
|
21
|
+
#
|
22
|
+
# http://example.com/the%2Bsign
|
23
|
+
#
|
24
|
+
# ...will break. Apache will unescape the path to "the+sign." Fir will then unescape it again to "the sign."
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'fir'
|
28
|
+
|
29
|
+
FIR_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
30
|
+
require File.expand_path(File.join(FIR_ROOT, 'config.rb'))
|
31
|
+
|
32
|
+
builder = Rack::Builder.new(&Fir.boot_proc)
|
33
|
+
|
34
|
+
Rack::Handler::CGI.run builder
|
data/spec/admin_spec.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe 'admin interface' do
|
4
|
+
before :each do
|
5
|
+
load_config
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'returns 404 for bad URLs' do
|
9
|
+
authorize 'johndoe', 'foo'
|
10
|
+
get '/-admin/doesnt_exist'
|
11
|
+
last_response.should be_not_found
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'GET pages' do
|
15
|
+
it 'requires HTTP auth' do
|
16
|
+
get '/-admin/pages'
|
17
|
+
last_response.status.should == 401
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'does not accept bad user/pass combos' do
|
21
|
+
authorize 'johndoe', 'wrong'
|
22
|
+
get '/-admin/pages'
|
23
|
+
last_response.status.should == 401
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns an XML feed with each directory and page' do
|
27
|
+
require 'test/unit/xml'
|
28
|
+
include Test::Unit::XML
|
29
|
+
authorize 'johndoe', 'foo'
|
30
|
+
get '/-admin/pages'
|
31
|
+
expected =
|
32
|
+
'<?xml version="1.0" encoding="UTF-8"?>
|
33
|
+
<pages>
|
34
|
+
<page>
|
35
|
+
<name>404.html.erb</name>
|
36
|
+
</page>
|
37
|
+
<page>
|
38
|
+
<name>contact-us.markdown</name>
|
39
|
+
</page>
|
40
|
+
<page>
|
41
|
+
|
42
|
+
<name>index.markdown</name>
|
43
|
+
</page>
|
44
|
+
<folder>
|
45
|
+
<name>products</name>
|
46
|
+
<pages>
|
47
|
+
<page>
|
48
|
+
<name>dynamite-plus.html.erb</name>
|
49
|
+
|
50
|
+
</page>
|
51
|
+
<page>
|
52
|
+
<name>dynamite.html.erb</name>
|
53
|
+
</page>
|
54
|
+
<page>
|
55
|
+
<name>index.markdown</name>
|
56
|
+
</page>
|
57
|
+
<page>
|
58
|
+
|
59
|
+
<name>mouse-trap.html.erb</name>
|
60
|
+
</page>
|
61
|
+
</pages>
|
62
|
+
</folder>
|
63
|
+
</pages>'.gsub(/\s/, '')
|
64
|
+
|
65
|
+
last_response.body.gsub(/\s/, '').should == expected
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'GET page/pagename' do
|
70
|
+
it 'requires HTTP auth' do
|
71
|
+
get '/-admin/pages/index.markdown'
|
72
|
+
last_response.status.should == 401
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'does not accept bad user/pass combos' do
|
76
|
+
authorize 'johndoe', 'wrong'
|
77
|
+
get '/-admin/pages/index.markdown'
|
78
|
+
last_response.status.should == 401
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with balid login' do
|
82
|
+
before :each do
|
83
|
+
authorize 'johndoe', 'foo'
|
84
|
+
get '/-admin/pages/index.markdown'
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns the source code of the page' do
|
88
|
+
last_response.body.should == File.read(File.join(FIR_ROOT, 'pages', 'index.markdown'))
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'sets Content-Type to text/plain' do
|
92
|
+
last_response.content_type.should == 'text/plain'
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'sets Content-Disposition to attachment with the source filename' do
|
96
|
+
last_response.headers['Content-Disposition'].should == 'attachment; index.markdown'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'works for pages in sub-directories' do
|
100
|
+
get '/-admin/pages/products/index.markdown'
|
101
|
+
last_response.body.should == File.read(File.join(FIR_ROOT, 'pages', 'products', 'index.markdown'))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe 'POST page/pagename' do
|
107
|
+
it 'requires HTTP auth' do
|
108
|
+
post '/-admin/pages/index.markdown'
|
109
|
+
last_response.status.should == 401
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'does not accept bad user/pass combos' do
|
113
|
+
authorize 'johndoe', 'wrong'
|
114
|
+
post '/-admin/pages/index.markdown'
|
115
|
+
last_response.status.should == 401
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'with balid login' do
|
119
|
+
before :each do
|
120
|
+
authorize 'johndoe', 'foo'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'creates the page if none exists' do
|
124
|
+
path = File.join(FIR_ROOT, 'pages', 'new-page.markdown')
|
125
|
+
begin
|
126
|
+
post '/-admin/pages/new-page.markdown', 'content' => 'New page content'
|
127
|
+
File.read(path).should == 'New page content'
|
128
|
+
ensure
|
129
|
+
File.delete(path)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'overwrites the page if it exists' do
|
134
|
+
path = File.join(FIR_ROOT, 'pages', 'new-page.markdown')
|
135
|
+
begin
|
136
|
+
File.open(path, 'w') do |file|
|
137
|
+
file.write 'Old page content'
|
138
|
+
end
|
139
|
+
post '/-admin/pages/new-page.markdown', 'content' => 'New page content'
|
140
|
+
File.read(path).should == 'New page content'
|
141
|
+
ensure
|
142
|
+
File.delete(path)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def assert_cache_cleared_after_post
|
147
|
+
cached_path = File.join(FIR_ROOT, 'public', 'cache', 'cached-page.html')
|
148
|
+
page_path = File.join(FIR_ROOT, 'pages', 'cached-page.markdown')
|
149
|
+
begin
|
150
|
+
File.open(cached_path, 'w') do |file|
|
151
|
+
file.write 'This is a cached page'
|
152
|
+
end
|
153
|
+
post '/-admin/pages/cached-page.markdown', 'content' => 'New page content'
|
154
|
+
File.exists?(cached_path).should be_false
|
155
|
+
ensure
|
156
|
+
File.delete(cached_path) if File.exists?(cached_path)
|
157
|
+
File.delete(page_path) if File.exists?(page_path)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'clears the cache if caching is on' do
|
162
|
+
Fir.config.perform_caching = true
|
163
|
+
assert_cache_cleared_after_post
|
164
|
+
end
|
165
|
+
|
166
|
+
it "clears the cache even if caching is off (in case it's off temporarily)" do
|
167
|
+
Fir.config.perform_caching = false
|
168
|
+
assert_cache_cleared_after_post
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/spec/fir_spec.rb
CHANGED
@@ -1,71 +1,98 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe 'Fir' do
|
4
|
-
|
4
|
+
before :each do
|
5
|
+
load_config
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'allows access to the home page' do
|
9
|
+
# Basic sanity check
|
10
|
+
get '/'
|
11
|
+
last_response.should contain('Welcome to Acme Company!')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns static files in the public directory' do
|
15
|
+
get '/static.txt'
|
16
|
+
last_response.should contain('This is static.txt.')
|
17
|
+
end
|
5
18
|
|
6
|
-
it 'returns static files in subdirectories of the public directory'
|
19
|
+
it 'returns static files in subdirectories of the public directory' do
|
20
|
+
get '/stylesheets/style.css'
|
21
|
+
last_response.should contain('General typography')
|
22
|
+
end
|
7
23
|
|
8
|
-
it 'does not allow access to files above the pages directory'
|
24
|
+
it 'does not allow access to files above the pages directory' do
|
25
|
+
get '/../pages.yml'
|
26
|
+
last_response.should_not be_ok
|
27
|
+
last_response.should_not include('index')
|
28
|
+
end
|
9
29
|
|
10
|
-
it 'maps pretty URLs to files in the pages directory'
|
30
|
+
it 'maps pretty URLs to files in the pages directory' do
|
31
|
+
get '/contact-us'
|
32
|
+
last_response.should contain('Contact Us')
|
33
|
+
end
|
11
34
|
|
12
|
-
it 'maps pretty URLs to files in subdirectories of the pages directory'
|
35
|
+
it 'maps pretty URLs to files in subdirectories of the pages directory' do
|
36
|
+
get '/products/dynamite'
|
37
|
+
last_response.should contain('Dynamite')
|
38
|
+
end
|
13
39
|
|
14
|
-
it 'returns 404 for bad URLs'
|
40
|
+
it 'returns 404 for bad URLs' do
|
41
|
+
get '/doesnt-exist'
|
42
|
+
last_response.should be_not_found
|
43
|
+
last_response.should contain('Sorry, there is nothing at this address.')
|
44
|
+
end
|
15
45
|
|
16
46
|
context 'with caching on' do
|
17
|
-
|
47
|
+
before :each do
|
48
|
+
Fir.config.perform_caching = true
|
49
|
+
end
|
18
50
|
|
19
|
-
it 'uses the cached page for pages in
|
51
|
+
it 'uses the cached page for pages in the root directory' do
|
52
|
+
with_cache('index', '<p>This is the cached index.</p>') do
|
53
|
+
get '/'
|
54
|
+
last_response.should contain('This is the cached index.')
|
55
|
+
end
|
56
|
+
end
|
20
57
|
|
21
|
-
it '
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
58
|
+
it 'uses the cached page for pages in subdirectories' do
|
59
|
+
with_cache('products/dynamite', '<p>Dynamite is cached</p>') do
|
60
|
+
get '/products/dynamite'
|
61
|
+
last_response.should contain('Dynamite is cached')
|
62
|
+
end
|
63
|
+
end
|
26
64
|
|
27
|
-
it '
|
65
|
+
it 'writes to the cache when a page is accessed' do
|
66
|
+
get '/'
|
67
|
+
File.read(File.join(FIR_ROOT, 'public', 'cache', 'index.html')).should contain('Welcome to Acme Company!')
|
68
|
+
end
|
28
69
|
|
29
|
-
it '
|
70
|
+
it 'creates the cache directory if none exists' do
|
71
|
+
begin
|
72
|
+
cache_path = File.join(FIR_ROOT, 'public', 'cache')
|
73
|
+
FileUtils.rm_rf(cache_path)
|
74
|
+
get '/'
|
75
|
+
File.directory?(cache_path).should be_true
|
76
|
+
ensure
|
77
|
+
File.delete File.join(cache_path, 'index.html')
|
78
|
+
end
|
79
|
+
end
|
30
80
|
end
|
31
81
|
|
32
|
-
describe '
|
33
|
-
it '
|
34
|
-
|
35
|
-
|
36
|
-
it 'requires HTTP auth'
|
37
|
-
|
38
|
-
it 'does not accept bad user/pass combos'
|
39
|
-
|
40
|
-
it 'returns an XML feed with each directory and page'
|
82
|
+
describe 'page_config.yml' do
|
83
|
+
it 'can override URL mappings' do
|
84
|
+
get '/products/dynamite%2B'
|
85
|
+
last_response.should contain('Dynamite Plus!')
|
41
86
|
end
|
42
87
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
it 'does not accept bad user/pass combos'
|
47
|
-
|
48
|
-
it 'returns the source code of the page'
|
49
|
-
|
50
|
-
it 'sets Content-Type to text/plain'
|
51
|
-
|
52
|
-
it 'sets Content-Disposition to attachment'
|
53
|
-
|
54
|
-
it 'sets Content-Disposition filename to the source filename'
|
88
|
+
it 'can specify page titles (if implemented in the layout)' do
|
89
|
+
get '/products'
|
90
|
+
last_response.should have_selector('title', :content => 'Products')
|
55
91
|
end
|
56
92
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
it 'does not accept bad user/pass combos'
|
61
|
-
|
62
|
-
it 'creates the page if none exists'
|
63
|
-
|
64
|
-
it 'overwrites the page if it exists'
|
65
|
-
|
66
|
-
it 'clears the cache if caching is on'
|
67
|
-
|
68
|
-
it "clears the cache even if caching is off (in case it's off temporarily)"
|
93
|
+
it 'can specify meta descriptions (if implemented in the layout)' do
|
94
|
+
get '/products'
|
95
|
+
last_response.should have_selector('meta[content="Acme Company\'s products"]', :name => 'description')
|
69
96
|
end
|
70
97
|
end
|
71
98
|
end
|