nesta 0.9.0
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/.gitignore +13 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +58 -0
- data/LICENSE +19 -0
- data/README.md +45 -0
- data/Rakefile +12 -0
- data/bin/nesta +67 -0
- data/config.ru +9 -0
- data/config/config.yml.sample +73 -0
- data/config/deploy.rb.sample +62 -0
- data/lib/nesta/app.rb +199 -0
- data/lib/nesta/cache.rb +139 -0
- data/lib/nesta/commands.rb +135 -0
- data/lib/nesta/config.rb +87 -0
- data/lib/nesta/models.rb +313 -0
- data/lib/nesta/nesta.rb +0 -0
- data/lib/nesta/overrides.rb +59 -0
- data/lib/nesta/path.rb +11 -0
- data/lib/nesta/plugins.rb +15 -0
- data/lib/nesta/version.rb +3 -0
- data/nesta.gemspec +49 -0
- data/scripts/import-from-mephisto +207 -0
- data/spec/atom_spec.rb +138 -0
- data/spec/commands_spec.rb +220 -0
- data/spec/config_spec.rb +69 -0
- data/spec/model_factory.rb +94 -0
- data/spec/models_spec.rb +445 -0
- data/spec/overrides_spec.rb +113 -0
- data/spec/page_spec.rb +428 -0
- data/spec/path_spec.rb +28 -0
- data/spec/sitemap_spec.rb +102 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +72 -0
- data/templates/Gemfile +8 -0
- data/templates/Rakefile +35 -0
- data/templates/config.ru +9 -0
- data/templates/config/config.yml +73 -0
- data/templates/config/deploy.rb +47 -0
- data/views/analytics.haml +12 -0
- data/views/atom.builder +28 -0
- data/views/categories.haml +3 -0
- data/views/comments.haml +8 -0
- data/views/error.haml +13 -0
- data/views/feed.haml +3 -0
- data/views/index.haml +5 -0
- data/views/layout.haml +27 -0
- data/views/master.sass +246 -0
- data/views/not_found.haml +13 -0
- data/views/page.haml +29 -0
- data/views/sidebar.haml +3 -0
- data/views/sitemap.builder +15 -0
- data/views/summaries.haml +14 -0
- metadata +302 -0
@@ -0,0 +1,220 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
+
require File.expand_path("../lib/nesta/commands", File.dirname(__FILE__))
|
3
|
+
|
4
|
+
describe "nesta" do
|
5
|
+
include FixtureHelper
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
create_fixtures_directory
|
9
|
+
@project_path = File.join(FixtureHelper::FIXTURE_DIR, 'mysite.com')
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:each) do
|
13
|
+
remove_fixtures
|
14
|
+
end
|
15
|
+
|
16
|
+
def project_path(path)
|
17
|
+
File.join(@project_path, path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def should_exist(file)
|
21
|
+
File.exist?(project_path(file)).should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "new" do
|
25
|
+
def gemfile_source
|
26
|
+
File.read(project_path('Gemfile'))
|
27
|
+
end
|
28
|
+
|
29
|
+
def rakefile_source
|
30
|
+
File.read(project_path('Rakefile'))
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "without options" do
|
34
|
+
before(:each) do
|
35
|
+
Nesta::Commands::New.new(@project_path).execute
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should create the content directories" do
|
39
|
+
should_exist('content/attachments')
|
40
|
+
should_exist('content/pages')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should create the rackup file" do
|
44
|
+
should_exist('config.ru')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should create the config.yml file" do
|
48
|
+
should_exist('config/config.yml')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should add a Gemfile" do
|
52
|
+
should_exist('Gemfile')
|
53
|
+
gemfile_source.should match(/gem 'nesta', '#{Nesta::VERSION}'/)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "--heroku" do
|
58
|
+
before(:each) do
|
59
|
+
Nesta::Commands::New.new(@project_path, 'heroku' => '').execute
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should add heroku to Gemfile" do
|
63
|
+
gemfile_source.should match(/gem 'heroku'/)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should add the heroku:config Rake task" do
|
67
|
+
should_exist('Rakefile')
|
68
|
+
rakefile_source.should match(/namespace :heroku/)
|
69
|
+
rakefile_source.should match(/task :config/)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "--vlad" do
|
74
|
+
before(:each) do
|
75
|
+
Nesta::Commands::New.new(@project_path, 'vlad' => '').execute
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should add vlad to Gemfile" do
|
79
|
+
gemfile_source.should match(/gem 'vlad', '2.1.0'/)
|
80
|
+
gemfile_source.should match(/gem 'vlad-git', '2.2.0'/)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should configure the vlad rake tasks" do
|
84
|
+
should_exist('Rakefile')
|
85
|
+
rakefile_source.should match(/require 'vlad'/)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should create deploy.rb" do
|
89
|
+
should_exist('config/deploy.rb')
|
90
|
+
deploy_source = File.read(project_path('config/deploy.rb'))
|
91
|
+
deploy_source.should match(/set :application, 'mysite.com'/)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "theme:install" do
|
97
|
+
before(:each) do
|
98
|
+
@repo_url = 'git://github.com/gma/nesta-theme-mine.git'
|
99
|
+
@theme_dir = 'themes/mine'
|
100
|
+
FileUtils.mkdir_p(File.join(@theme_dir, '.git'))
|
101
|
+
@command = Nesta::Commands::Theme::Install.new(@repo_url)
|
102
|
+
@command.stub!(:system)
|
103
|
+
end
|
104
|
+
|
105
|
+
after(:each) do
|
106
|
+
FileUtils.rm_r(@theme_dir)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should clone the repository" do
|
110
|
+
@command.should_receive(:system).with(
|
111
|
+
'git', 'clone', @repo_url, @theme_dir)
|
112
|
+
@command.execute
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should remove the theme's .git directory" do
|
116
|
+
@command.execute
|
117
|
+
File.exist?(@theme_dir).should be_true
|
118
|
+
File.exist?(File.join(@theme_dir, '.git')).should be_false
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should enable the freshly installed theme" do
|
122
|
+
@command.should_receive(:enable).with('mine')
|
123
|
+
@command.execute
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "when theme URL doesn't match recommendation" do
|
127
|
+
before(:each) do
|
128
|
+
@repo_url = 'git://foobar.com/path/to/mytheme.git'
|
129
|
+
@other_theme_dir = 'themes/mytheme'
|
130
|
+
FileUtils.mkdir_p(File.join(@other_theme_dir, '.git'))
|
131
|
+
@command = Nesta::Commands::Theme::Install.new(@repo_url)
|
132
|
+
end
|
133
|
+
|
134
|
+
after(:each) do
|
135
|
+
FileUtils.rm_r(@other_theme_dir)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should use the basename as theme dir" do
|
139
|
+
@command.should_receive(:system).with(
|
140
|
+
'git', 'clone', @repo_url, @other_theme_dir)
|
141
|
+
@command.execute
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "theme:enable" do
|
147
|
+
before(:each) do
|
148
|
+
config = File.join(FixtureHelper::FIXTURE_DIR, 'config.yml')
|
149
|
+
Nesta::Config.stub!(:yaml_path).and_return(config)
|
150
|
+
@name = 'mytheme'
|
151
|
+
@command = Nesta::Commands::Theme::Enable.new(@name)
|
152
|
+
end
|
153
|
+
|
154
|
+
def create_config_yaml(text)
|
155
|
+
File.open(Nesta::Config.yaml_path, 'w') { |f| f.puts(text) }
|
156
|
+
end
|
157
|
+
|
158
|
+
shared_examples_for "command that configures the theme" do
|
159
|
+
it "should enable the theme" do
|
160
|
+
@command.execute
|
161
|
+
File.read(Nesta::Config.yaml_path).should match(/^theme: #{@name}/)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "when theme config is commented out" do
|
166
|
+
before(:each) do
|
167
|
+
create_config_yaml(' # theme: blah')
|
168
|
+
end
|
169
|
+
|
170
|
+
it_should_behave_like "command that configures the theme"
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "when another theme is configured" do
|
174
|
+
before(:each) do
|
175
|
+
create_config_yaml('theme: another')
|
176
|
+
end
|
177
|
+
|
178
|
+
it_should_behave_like "command that configures the theme"
|
179
|
+
end
|
180
|
+
|
181
|
+
describe "when no theme config exists" do
|
182
|
+
before(:each) do
|
183
|
+
create_config_yaml('# I have no theme config')
|
184
|
+
end
|
185
|
+
|
186
|
+
it_should_behave_like "command that configures the theme"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "theme:create" do
|
191
|
+
def should_exist(file)
|
192
|
+
File.exist?(Nesta::Path.themes(@name, file)).should be_true
|
193
|
+
end
|
194
|
+
|
195
|
+
before(:each) do
|
196
|
+
Nesta::App.stub!(:root).and_return(FixtureHelper::FIXTURE_DIR)
|
197
|
+
@name = 'my-new-theme'
|
198
|
+
Nesta::Commands::Theme::Create.new(@name).execute
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should create the theme directory" do
|
202
|
+
File.directory?(Nesta::Path.themes(@name)).should be_true
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should create a dummy README file" do
|
206
|
+
should_exist('README.md')
|
207
|
+
text = File.read(Nesta::Path.themes(@name, 'README.md'))
|
208
|
+
text.should match(/#{@name} is a theme/)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should create a default app.rb file" do
|
212
|
+
should_exist('app.rb')
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should create public and view directories" do
|
216
|
+
should_exist("public/#{@name}")
|
217
|
+
should_exist('views')
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe "Config" do
|
4
|
+
include ConfigSpecHelper
|
5
|
+
|
6
|
+
after(:each) do
|
7
|
+
ENV.keys.each { |variable| ENV.delete(variable) if variable =~ /NESTA_/ }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "when settings defined in ENV" do
|
11
|
+
before(:each) do
|
12
|
+
@title = "Title from ENV"
|
13
|
+
ENV["NESTA_TITLE"] = @title
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should never try and access config.yml" do
|
17
|
+
stub_config_key("subtitle", "Subtitle in YAML file")
|
18
|
+
Nesta::Config.subtitle.should be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should override config.yml" do
|
22
|
+
stub_config_key("title", "Title in YAML file")
|
23
|
+
Nesta::Config.title.should == @title
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should know how to cope with boolean values" do
|
27
|
+
ENV["NESTA_CACHE"] = "true"
|
28
|
+
Nesta::Config.cache.should be_true
|
29
|
+
ENV["NESTA_CACHE"] = "false"
|
30
|
+
Nesta::Config.cache.should be_false
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should set author hash from ENV" do
|
34
|
+
name = "Name from ENV"
|
35
|
+
uri = "URI from ENV"
|
36
|
+
ENV["NESTA_AUTHOR__NAME"] = name
|
37
|
+
ENV["NESTA_AUTHOR__URI"] = uri
|
38
|
+
Nesta::Config.author["name"].should == name
|
39
|
+
Nesta::Config.author["uri"].should == uri
|
40
|
+
Nesta::Config.author["email"].should be_nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "when settings only defined in config.yml" do
|
45
|
+
before(:each) do
|
46
|
+
@title = "Title in YAML file"
|
47
|
+
stub_config_key("subtitle", @title)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should read configuration from YAML" do
|
51
|
+
Nesta::Config.subtitle.should == @title
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should set author hash from YAML" do
|
55
|
+
name = "Name from YAML"
|
56
|
+
uri = "URI from YAML"
|
57
|
+
stub_config_key("author", { "name" => name, "uri" => uri })
|
58
|
+
Nesta::Config.author["name"].should == name
|
59
|
+
Nesta::Config.author["uri"].should == uri
|
60
|
+
Nesta::Config.author["email"].should be_nil
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should override top level settings with RACK_ENV specific settings" do
|
64
|
+
stub_config_key('content', 'general/path')
|
65
|
+
stub_config_key('content', 'rack_env/path', :rack_env => true)
|
66
|
+
Nesta::Config.content.should == 'rack_env/path'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module ModelFactory
|
2
|
+
include FixtureHelper
|
3
|
+
|
4
|
+
def create_page(options)
|
5
|
+
extension = options[:ext] || :mdown
|
6
|
+
path = filename(Nesta::Config.page_path, options[:path], extension)
|
7
|
+
create_file(path, options)
|
8
|
+
yield(path) if block_given?
|
9
|
+
Nesta::Page.new(path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_article(options = {}, &block)
|
13
|
+
o = {
|
14
|
+
:path => 'article-prefix/my-article',
|
15
|
+
:heading => 'My article',
|
16
|
+
:content => 'Content goes here',
|
17
|
+
:metadata => {
|
18
|
+
'date' => '29 December 2008'
|
19
|
+
}.merge(options.delete(:metadata) || {})
|
20
|
+
}.merge(options)
|
21
|
+
create_page(o, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_category(options = {}, &block)
|
25
|
+
o = {
|
26
|
+
:path => 'category-prefix/my-category',
|
27
|
+
:heading => 'My category',
|
28
|
+
:content => 'Content goes here'
|
29
|
+
}.merge(options)
|
30
|
+
create_page(o, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def write_menu_item(indent, file, menu_item)
|
34
|
+
if menu_item.is_a?(Array)
|
35
|
+
indent.sub!(/^/, ' ')
|
36
|
+
menu_item.each { |path| write_menu_item(indent, file, path) }
|
37
|
+
indent.sub!(/^ /, '')
|
38
|
+
else
|
39
|
+
file.write("#{indent}#{menu_item}\n")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_menu(menu_text)
|
44
|
+
file = filename(Nesta::Config.content_path, 'menu', :txt)
|
45
|
+
File.open(file, 'w') { |file| file.write(menu_text) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete_page(type, permalink, extension)
|
49
|
+
file = filename(Nesta::Config.page_path, permalink, extension)
|
50
|
+
FileUtils.rm(file)
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_content_directories
|
54
|
+
FileUtils.mkdir_p(Nesta::Config.page_path)
|
55
|
+
FileUtils.mkdir_p(Nesta::Config.attachment_path)
|
56
|
+
end
|
57
|
+
|
58
|
+
def mock_file_stat(method, filename, time)
|
59
|
+
stat = mock(:stat)
|
60
|
+
stat.stub!(:mtime).and_return(Time.parse(time))
|
61
|
+
File.send(method, :stat).with(filename).and_return(stat)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def filename(directory, basename, extension = :mdown)
|
66
|
+
File.join(directory, "#{basename}.#{extension}")
|
67
|
+
end
|
68
|
+
|
69
|
+
def heading(options)
|
70
|
+
prefix = case options[:ext]
|
71
|
+
when :haml
|
72
|
+
"%div\n %h1"
|
73
|
+
when :textile
|
74
|
+
"<div>\nh1."
|
75
|
+
else
|
76
|
+
'# '
|
77
|
+
end
|
78
|
+
"#{prefix} #{options[:heading]}\n\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_file(path, options = {})
|
82
|
+
create_content_directories
|
83
|
+
metadata = options[:metadata] || {}
|
84
|
+
metatext = metadata.map { |key, value| "#{key}: #{value}" }.join("\n")
|
85
|
+
heading = options[:heading] ? heading(options) : ''
|
86
|
+
contents =<<-EOF
|
87
|
+
#{metatext}
|
88
|
+
|
89
|
+
#{heading}#{options[:content]}
|
90
|
+
EOF
|
91
|
+
FileUtils.mkdir_p(File.dirname(path))
|
92
|
+
File.open(path, 'w') { |file| file.write(contents) }
|
93
|
+
end
|
94
|
+
end
|
data/spec/models_spec.rb
ADDED
@@ -0,0 +1,445 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
require File.expand_path('model_factory', File.dirname(__FILE__))
|
3
|
+
|
4
|
+
module ModelMatchers
|
5
|
+
class HavePage
|
6
|
+
def initialize(path)
|
7
|
+
@category = path
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(article)
|
11
|
+
@article = article
|
12
|
+
article.categories.map { |c| c.path }.include?(@category)
|
13
|
+
end
|
14
|
+
|
15
|
+
def failure_message
|
16
|
+
"expected '#{@article.path}' to be assigned to #{@category}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def negative_failure_message
|
20
|
+
"'#{@article.path}' should not be assigned to #{@category}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def be_in_category(path)
|
25
|
+
HavePage.new(path)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "Page", :shared => true do
|
30
|
+
include ConfigSpecHelper
|
31
|
+
include ModelFactory
|
32
|
+
include ModelMatchers
|
33
|
+
|
34
|
+
def create_page(options)
|
35
|
+
super(options.merge(:ext => @extension))
|
36
|
+
end
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
stub_configuration
|
40
|
+
end
|
41
|
+
|
42
|
+
after(:each) do
|
43
|
+
remove_fixtures
|
44
|
+
Nesta::FileModel.purge_cache
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be findable" do
|
48
|
+
create_page(:heading => "Apple", :path => "the-apple")
|
49
|
+
Nesta::Page.find_all.should have(1).item
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should find by path" do
|
53
|
+
create_page(:heading => "Banana", :path => "banana")
|
54
|
+
Nesta::Page.find_by_path("banana").heading.should == "Banana"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should not find nonexistent page" do
|
58
|
+
Nesta::Page.find_by_path("no-such-page").should be_nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should ensure file exists on instantiation" do
|
62
|
+
lambda {
|
63
|
+
Nesta::Page.new("no-such-file")
|
64
|
+
}.should raise_error(Sinatra::NotFound)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should reload cached files when modified" do
|
68
|
+
create_page(:path => "a-page", :heading => "Version 1")
|
69
|
+
File.stub!(:mtime).and_return(Time.new - 1)
|
70
|
+
Nesta::Page.find_by_path("a-page")
|
71
|
+
create_page(:path => "a-page", :heading => "Version 2")
|
72
|
+
File.stub!(:mtime).and_return(Time.new)
|
73
|
+
Nesta::Page.find_by_path("a-page").heading.should == "Version 2"
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "with assigned pages" do
|
77
|
+
before(:each) do
|
78
|
+
@category = create_category
|
79
|
+
create_article(:heading => "Article 1", :path => "article-1")
|
80
|
+
create_article(
|
81
|
+
:heading => "Article 2",
|
82
|
+
:path => "article-2",
|
83
|
+
:metadata => {
|
84
|
+
"date" => "30 December 2008",
|
85
|
+
"categories" => @category.path
|
86
|
+
}
|
87
|
+
)
|
88
|
+
@article = create_article(
|
89
|
+
:heading => "Article 3",
|
90
|
+
:path => "article-3",
|
91
|
+
:metadata => {
|
92
|
+
"date" => "31 December 2008",
|
93
|
+
"categories" => @category.path
|
94
|
+
}
|
95
|
+
)
|
96
|
+
create_category(:path => "category-2",
|
97
|
+
:metadata => { "categories" => @category.path })
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should find articles" do
|
101
|
+
@category.articles.should have(2).items
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should list most recent articles first" do
|
105
|
+
@category.articles.first.path.should == @article.path
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should find pages" do
|
109
|
+
@category.pages.should have(1).item
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should not find pages scheduled in the future" do
|
113
|
+
future_date = (Time.now + 86400).strftime("%d %B %Y")
|
114
|
+
article = create_article(:heading => "Article 4",
|
115
|
+
:path => "foo/article-4",
|
116
|
+
:metadata => { "date" => future_date })
|
117
|
+
Nesta::Page.find_articles.detect{|a| a == article}.should be_nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "when finding articles" do
|
122
|
+
before(:each) do
|
123
|
+
create_article(:heading => "Article 1", :path => "article-1")
|
124
|
+
create_article(:heading => "Article 2",
|
125
|
+
:path => "article-2",
|
126
|
+
:metadata => { "date" => "31 December 2008" })
|
127
|
+
create_article(:heading => "Article 3",
|
128
|
+
:path => "foo/article-3",
|
129
|
+
:metadata => { "date" => "30 December 2008" })
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should only find pages with dates" do
|
133
|
+
articles = Nesta::Page.find_articles
|
134
|
+
articles.size.should > 0
|
135
|
+
Nesta::Page.find_articles.each { |page| page.date.should_not be_nil }
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should return articles in reverse chronological order" do
|
139
|
+
article1, article2 = Nesta::Page.find_articles[0..1]
|
140
|
+
article1.date.should > article2.date
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "when assigned to categories" do
|
145
|
+
before(:each) do
|
146
|
+
create_category(:heading => "Apple", :path => "the-apple")
|
147
|
+
create_category(:heading => "Banana", :path => "banana")
|
148
|
+
@article = create_article(
|
149
|
+
:metadata => { "categories" => "banana, the-apple" })
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should be possible to list the categories" do
|
153
|
+
@article.categories.should have(2).items
|
154
|
+
@article.should be_in_category("the-apple")
|
155
|
+
@article.should be_in_category("banana")
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should sort categories by heading" do
|
159
|
+
@article.categories.first.heading.should == "Apple"
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should not be assigned to non-existant category" do
|
163
|
+
delete_page(:category, "banana", @extension)
|
164
|
+
@article.should_not be_in_category("banana")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should be able to find parent page" do
|
169
|
+
category = create_category(:path => "parent")
|
170
|
+
article = create_article(:path => "parent/child")
|
171
|
+
article.parent.should == category
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should set parent to nil when at root" do
|
175
|
+
create_category(:path => "top-level").parent.should be_nil
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "when not assigned to category" do
|
179
|
+
it "should have empty category list" do
|
180
|
+
article = create_article
|
181
|
+
Nesta::Page.find_by_path(article.path).categories.should be_empty
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "without metadata" do
|
186
|
+
before(:each) do
|
187
|
+
create_article
|
188
|
+
@article = Nesta::Page.find_all.first
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should use default layout" do
|
192
|
+
@article.layout.should == :layout
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should use default template" do
|
196
|
+
@article.template.should == :page
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should parse heading correctly" do
|
200
|
+
@article.to_html.should have_tag("h1", "My article")
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should have default read more link text" do
|
204
|
+
@article.read_more.should == "Continue reading"
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should not have description" do
|
208
|
+
@article.description.should be_nil
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should not have keywords" do
|
212
|
+
@article.keywords.should be_nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "with metadata" do
|
217
|
+
before(:each) do
|
218
|
+
@layout = "my_layout"
|
219
|
+
@template = "my_template"
|
220
|
+
@date = "07 September 2009"
|
221
|
+
@keywords = "things, stuff"
|
222
|
+
@description = "Page about stuff"
|
223
|
+
@summary = 'Multiline\n\nsummary'
|
224
|
+
@read_more = "Continue at your leisure"
|
225
|
+
@article = create_article(:metadata => {
|
226
|
+
"layout" => @layout,
|
227
|
+
"template" => @template,
|
228
|
+
"date" => @date.gsub("September", "Sep"),
|
229
|
+
"description" => @description,
|
230
|
+
"keywords" => @keywords,
|
231
|
+
"summary" => @summary,
|
232
|
+
"read more" => @read_more
|
233
|
+
})
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should override default layout" do
|
237
|
+
@article.layout.should == @layout.to_sym
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should override default template" do
|
241
|
+
@article.template.should == @template.to_sym
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should set permalink from filename" do
|
245
|
+
@article.permalink.should == "my-article"
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should set path from filename" do
|
249
|
+
@article.path.should == "article-prefix/my-article"
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should retrieve heading" do
|
253
|
+
@article.heading.should == "My article"
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should be possible to convert an article to HTML" do
|
257
|
+
@article.to_html.should have_tag("h1", "My article")
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should not include metadata in the HTML" do
|
261
|
+
@article.to_html.should_not have_tag("p", /^Date/)
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should not include heading in body" do
|
265
|
+
@article.body.should_not have_tag("h1", "My article")
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should retrieve description from metadata" do
|
269
|
+
@article.description.should == @description
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should retrieve keywords from metadata" do
|
273
|
+
@article.keywords.should == @keywords
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should retrieve date published from metadata" do
|
277
|
+
@article.date.strftime("%d %B %Y").should == @date
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should retrieve read more link from metadata" do
|
281
|
+
@article.read_more.should == @read_more
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should retrieve summary text from metadata" do
|
285
|
+
@article.summary.should match(/#{@summary.split('\n\n').first}/)
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should treat double newline chars as paragraph break in summary" do
|
289
|
+
@article.summary.should match(/#{@summary.split('\n\n').last}/)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe "when checking last modification time" do
|
294
|
+
before(:each) do
|
295
|
+
create_article
|
296
|
+
@article = Nesta::Page.find_all.first
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should check filesystem" do
|
300
|
+
mock_file_stat(:should_receive, @article.filename, "3 January 2009")
|
301
|
+
@article.last_modified.should == Time.parse("3 January 2009")
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "All types of page" do
|
307
|
+
include ConfigSpecHelper
|
308
|
+
include ModelFactory
|
309
|
+
|
310
|
+
before(:each) do
|
311
|
+
stub_configuration
|
312
|
+
end
|
313
|
+
|
314
|
+
after(:each) do
|
315
|
+
remove_fixtures
|
316
|
+
Nesta::FileModel.purge_cache
|
317
|
+
end
|
318
|
+
|
319
|
+
it "should still return top level menu items" do
|
320
|
+
# Page.menu_items is deprecated; we're keeping it for the moment so
|
321
|
+
# that we don't break themes or code in a local app.rb (just yet).
|
322
|
+
page1 = create_category(:path => "page-1")
|
323
|
+
page2 = create_category(:path => "page-2")
|
324
|
+
create_menu([page1.path, page2.path].join("\n"))
|
325
|
+
Nesta::Page.menu_items.should == [page1, page2]
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
describe "Markdown page" do
|
330
|
+
include ConfigSpecHelper
|
331
|
+
|
332
|
+
before(:each) do
|
333
|
+
@extension = :mdown
|
334
|
+
end
|
335
|
+
|
336
|
+
it_should_behave_like "Page"
|
337
|
+
|
338
|
+
it "should set heading from first h1 tag" do
|
339
|
+
create_page(
|
340
|
+
:path => "a-page",
|
341
|
+
:heading => "First heading",
|
342
|
+
:content => "# Second heading"
|
343
|
+
)
|
344
|
+
Nesta::Page.find_by_path("a-page").heading.should == "First heading"
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
describe "Haml page" do
|
349
|
+
include ConfigSpecHelper
|
350
|
+
|
351
|
+
before(:each) do
|
352
|
+
@extension = :haml
|
353
|
+
end
|
354
|
+
|
355
|
+
it_should_behave_like "Page"
|
356
|
+
|
357
|
+
it "should set heading from first h1 tag" do
|
358
|
+
create_page(
|
359
|
+
:path => "a-page",
|
360
|
+
:heading => "First heading",
|
361
|
+
:content => "%h1 Second heading"
|
362
|
+
)
|
363
|
+
Nesta::Page.find_by_path("a-page").heading.should == "First heading"
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
describe "Textile page" do
|
368
|
+
include ConfigSpecHelper
|
369
|
+
|
370
|
+
before(:each) do
|
371
|
+
@extension = :textile
|
372
|
+
end
|
373
|
+
|
374
|
+
it_should_behave_like "Page"
|
375
|
+
|
376
|
+
it "should set heading from first h1 tag" do
|
377
|
+
create_page(
|
378
|
+
:path => "a-page",
|
379
|
+
:heading => "First heading",
|
380
|
+
:content => "h1. Second heading"
|
381
|
+
)
|
382
|
+
Nesta::Page.find_by_path("a-page").heading.should == "First heading"
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
describe "Menu" do
|
387
|
+
include ConfigSpecHelper
|
388
|
+
include ModelFactory
|
389
|
+
|
390
|
+
before(:each) do
|
391
|
+
stub_configuration
|
392
|
+
@page = create_page(:path => "page-1")
|
393
|
+
end
|
394
|
+
|
395
|
+
after(:each) do
|
396
|
+
remove_fixtures
|
397
|
+
Nesta::FileModel.purge_cache
|
398
|
+
end
|
399
|
+
|
400
|
+
it "should find top level menu items" do
|
401
|
+
text = [@page.path, "no-such-page"].join("\n")
|
402
|
+
create_menu(text)
|
403
|
+
Nesta::Menu.top_level.should == [@page]
|
404
|
+
end
|
405
|
+
|
406
|
+
it "should find all items in the menu" do
|
407
|
+
create_menu(@page.path)
|
408
|
+
Nesta::Menu.full_menu.should == [@page]
|
409
|
+
Nesta::Menu.for_path('/').should == [@page]
|
410
|
+
end
|
411
|
+
|
412
|
+
describe "with nested sub menus" do
|
413
|
+
before(:each) do
|
414
|
+
(2..6).each do |i|
|
415
|
+
instance_variable_set("@page#{i}", create_page(:path => "page-#{i}"))
|
416
|
+
end
|
417
|
+
text = <<-EOF
|
418
|
+
#{@page.path}
|
419
|
+
#{@page2.path}
|
420
|
+
#{@page3.path}
|
421
|
+
#{@page4.path}
|
422
|
+
#{@page5.path}
|
423
|
+
#{@page6.path}
|
424
|
+
EOF
|
425
|
+
create_menu(text)
|
426
|
+
end
|
427
|
+
|
428
|
+
it "should return top level menu items" do
|
429
|
+
Nesta::Menu.top_level.should == [@page, @page5]
|
430
|
+
end
|
431
|
+
|
432
|
+
it "should return full tree of menu items" do
|
433
|
+
Nesta::Menu.full_menu.should ==
|
434
|
+
[@page, [@page2, [@page3, @page4]], @page5, [@page6]]
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should return part of the tree of menu items" do
|
438
|
+
Nesta::Menu.for_path(@page2.path).should == [@page2, [@page3, @page4]]
|
439
|
+
end
|
440
|
+
|
441
|
+
it "should deem menu for path that isn't in menu to be nil" do
|
442
|
+
Nesta::Menu.for_path('wibble').should be_nil
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|