fir 0.0.9 → 0.0.10
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.
- 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
|