mars-nesta 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +13 -0
  2. data/CHANGES +97 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +44 -0
  5. data/LICENSE +19 -0
  6. data/README.md +42 -0
  7. data/Rakefile +12 -0
  8. data/bin/nesta +79 -0
  9. data/config.ru +9 -0
  10. data/config/deploy.rb.sample +62 -0
  11. data/lib/nesta/app.rb +167 -0
  12. data/lib/nesta/cache.rb +139 -0
  13. data/lib/nesta/commands.rb +188 -0
  14. data/lib/nesta/config.rb +86 -0
  15. data/lib/nesta/models.rb +364 -0
  16. data/lib/nesta/navigation.rb +60 -0
  17. data/lib/nesta/nesta.rb +7 -0
  18. data/lib/nesta/overrides.rb +59 -0
  19. data/lib/nesta/path.rb +11 -0
  20. data/lib/nesta/plugins.rb +15 -0
  21. data/lib/nesta/version.rb +3 -0
  22. data/nesta.gemspec +48 -0
  23. data/scripts/import-from-mephisto +207 -0
  24. data/spec/atom_spec.rb +138 -0
  25. data/spec/commands_spec.rb +292 -0
  26. data/spec/config_spec.rb +69 -0
  27. data/spec/model_factory.rb +94 -0
  28. data/spec/models_spec.rb +554 -0
  29. data/spec/overrides_spec.rb +114 -0
  30. data/spec/page_spec.rb +458 -0
  31. data/spec/path_spec.rb +28 -0
  32. data/spec/sitemap_spec.rb +102 -0
  33. data/spec/spec.opts +1 -0
  34. data/spec/spec_helper.rb +70 -0
  35. data/templates/Gemfile +8 -0
  36. data/templates/Rakefile +11 -0
  37. data/templates/config.ru +9 -0
  38. data/templates/config/config.yml +67 -0
  39. data/templates/config/deploy.rb +47 -0
  40. data/templates/index.haml +1 -0
  41. data/templates/themes/README.md +7 -0
  42. data/templates/themes/app.rb +19 -0
  43. data/views/analytics.haml +12 -0
  44. data/views/atom.haml +28 -0
  45. data/views/categories.haml +4 -0
  46. data/views/colors.sass +10 -0
  47. data/views/comments.haml +8 -0
  48. data/views/error.haml +12 -0
  49. data/views/feed.haml +3 -0
  50. data/views/footer.haml +5 -0
  51. data/views/header.haml +4 -0
  52. data/views/layout.haml +24 -0
  53. data/views/master.sass +246 -0
  54. data/views/mixins.sass +39 -0
  55. data/views/not_found.haml +12 -0
  56. data/views/page.haml +21 -0
  57. data/views/page_meta.haml +16 -0
  58. data/views/sidebar.haml +3 -0
  59. data/views/sitemap.haml +11 -0
  60. data/views/summaries.haml +15 -0
  61. metadata +235 -0
@@ -0,0 +1,554 @@
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 find index page by path" do
58
+ create_page(:heading => 'Banana', :path => 'banana/index')
59
+ Nesta::Page.find_by_path('banana').heading.should == 'Banana'
60
+ end
61
+
62
+ describe "for home page" do
63
+ it "should set title to heading and site title" do
64
+ create_page(:heading => 'Home', :path => 'index')
65
+ Nesta::Page.find_by_path('/').title.should == 'Home - My blog'
66
+ end
67
+
68
+ it "should respect title metadata" do
69
+ create_page(:path => 'index', :metadata => { 'title' => 'Specific title' })
70
+ Nesta::Page.find_by_path('/').title.should == 'Specific title'
71
+ end
72
+
73
+ it "should set title to site title by default" do
74
+ create_page(:path => 'index')
75
+ Nesta::Page.find_by_path('/').title.should == 'My blog'
76
+ end
77
+
78
+ it "should set permalink to empty string" do
79
+ create_page(:path => 'index')
80
+ Nesta::Page.find_by_path('/').permalink.should == ''
81
+ end
82
+
83
+ it "should set abspath to /" do
84
+ create_page(:path => 'index')
85
+ Nesta::Page.find_by_path('/').abspath.should == '/'
86
+ end
87
+ end
88
+
89
+ it "should not find nonexistent page" do
90
+ Nesta::Page.find_by_path("no-such-page").should be_nil
91
+ end
92
+
93
+ it "should ensure file exists on instantiation" do
94
+ lambda {
95
+ Nesta::Page.new("no-such-file")
96
+ }.should raise_error(Sinatra::NotFound)
97
+ end
98
+
99
+ it "should reload cached files when modified" do
100
+ create_page(:path => "a-page", :heading => "Version 1")
101
+ File.stub!(:mtime).and_return(Time.new - 1)
102
+ Nesta::Page.find_by_path("a-page")
103
+ create_page(:path => "a-page", :heading => "Version 2")
104
+ File.stub!(:mtime).and_return(Time.new)
105
+ Nesta::Page.find_by_path("a-page").heading.should == "Version 2"
106
+ end
107
+
108
+ it "should have default priority of 0 in category" do
109
+ page = create_page(:metadata => { 'categories' => 'some-page' })
110
+ page.priority('some-page').should == 0
111
+ page.priority('another-page').should be_nil
112
+ end
113
+
114
+ it "should read priority from category metadata" do
115
+ page = create_page(:metadata => {
116
+ 'categories' => ' some-page:1, another-page , and-another :-1 '
117
+ })
118
+ page.priority('some-page').should == 1
119
+ page.priority('another-page').should == 0
120
+ page.priority('and-another').should == -1
121
+ end
122
+
123
+ describe "with assigned pages" do
124
+ before(:each) do
125
+ @category = create_category
126
+ create_article(:heading => 'Article 1', :path => 'article-1')
127
+ create_article(
128
+ :heading => 'Article 2',
129
+ :path => 'article-2',
130
+ :metadata => {
131
+ 'date' => '30 December 2008',
132
+ 'categories' => @category.path
133
+ }
134
+ )
135
+ @article = create_article(
136
+ :heading => 'Article 3',
137
+ :path => 'article-3',
138
+ :metadata => {
139
+ 'date' => '31 December 2008',
140
+ 'categories' => @category.path
141
+ }
142
+ )
143
+ @category1 = create_category(
144
+ :path => 'category-1',
145
+ :heading => 'Category 1',
146
+ :metadata => { 'categories' => @category.path }
147
+ )
148
+ @category2 = create_category(
149
+ :path => 'category-2',
150
+ :heading => 'Category 2',
151
+ :metadata => { 'categories' => @category.path }
152
+ )
153
+ @category3 = create_category(
154
+ :path => 'category-3',
155
+ :heading => 'Category 3',
156
+ :metadata => { 'categories' => "#{@category.path}:1" }
157
+ )
158
+ end
159
+
160
+ it "should find articles" do
161
+ @category.articles.should have(2).items
162
+ end
163
+
164
+ it "should order articles by reverse chronological order" do
165
+ @category.articles.first.path.should == @article.path
166
+ end
167
+
168
+ it "should find pages" do
169
+ @category.pages.should have(3).items
170
+ end
171
+
172
+ it "should sort pages by priority" do
173
+ @category.pages.index(@category3).should == 0
174
+ end
175
+
176
+ it "should order pages by heading if priority not set" do
177
+ pages = @category.pages
178
+ pages.index(@category1).should < pages.index(@category2)
179
+ end
180
+
181
+ it "should not find pages scheduled in the future" do
182
+ future_date = (Time.now + 86400).strftime("%d %B %Y")
183
+ article = create_article(:heading => "Article 4",
184
+ :path => "foo/article-4",
185
+ :metadata => { "date" => future_date })
186
+ Nesta::Page.find_articles.detect{|a| a == article}.should be_nil
187
+ end
188
+ end
189
+
190
+ describe "when finding articles" do
191
+ before(:each) do
192
+ create_article(:heading => "Article 1", :path => "article-1")
193
+ create_article(:heading => "Article 2",
194
+ :path => "article-2",
195
+ :metadata => { "date" => "31 December 2008" })
196
+ create_article(:heading => "Article 3",
197
+ :path => "foo/article-3",
198
+ :metadata => { "date" => "30 December 2008" })
199
+ end
200
+
201
+ it "should only find pages with dates" do
202
+ articles = Nesta::Page.find_articles
203
+ articles.size.should > 0
204
+ Nesta::Page.find_articles.each { |page| page.date.should_not be_nil }
205
+ end
206
+
207
+ it "should return articles in reverse chronological order" do
208
+ article1, article2 = Nesta::Page.find_articles[0..1]
209
+ article1.date.should > article2.date
210
+ end
211
+ end
212
+
213
+ it "should be able to find parent page" do
214
+ category = create_category(:path => 'parent')
215
+ article = create_article(:path => 'parent/child')
216
+ article.parent.should == category
217
+ end
218
+
219
+ describe "(with deep index page)" do
220
+ it "should be able to find index parent" do
221
+ home = create_category(:path => 'index', :heading => 'Home')
222
+ category = create_category(:path => 'parent')
223
+ category.parent.should == home
224
+ home.parent.should be_nil
225
+ end
226
+
227
+ it "should be able to find parent of index" do
228
+ category = create_category(:path => "parent")
229
+ index = create_category(:path => "parent/child/index")
230
+ index.parent.should == category
231
+ end
232
+
233
+ it "should be able to find permalink of index" do
234
+ index = create_category(:path => "parent/child/index")
235
+ index.permalink.should == 'child'
236
+ end
237
+ end
238
+
239
+ describe "(with missing nested page)" do
240
+ it "should consider grandparent to be parent" do
241
+ grandparent = create_category(:path => 'grandparent')
242
+ child = create_category(:path => 'grandparent/parent/child')
243
+ child.parent.should == grandparent
244
+ end
245
+
246
+ it "should consider grandparent home page to be parent" do
247
+ home = create_category(:path => 'index')
248
+ child = create_category(:path => 'parent/child')
249
+ child.parent.should == home
250
+ end
251
+ end
252
+
253
+ describe "when assigned to categories" do
254
+ before(:each) do
255
+ create_category(:heading => "Apple", :path => "the-apple")
256
+ create_category(:heading => "Banana", :path => "banana")
257
+ @article = create_article(
258
+ :metadata => { "categories" => "banana, the-apple" })
259
+ end
260
+
261
+ it "should be possible to list the categories" do
262
+ @article.categories.should have(2).items
263
+ @article.should be_in_category("the-apple")
264
+ @article.should be_in_category("banana")
265
+ end
266
+
267
+ it "should sort categories by heading" do
268
+ @article.categories.first.heading.should == "Apple"
269
+ end
270
+
271
+ it "should not be assigned to non-existant category" do
272
+ delete_page(:category, "banana", @extension)
273
+ @article.should_not be_in_category("banana")
274
+ end
275
+ end
276
+
277
+ it "should set parent to nil when at root" do
278
+ create_category(:path => "top-level").parent.should be_nil
279
+ end
280
+
281
+ describe "when not assigned to category" do
282
+ it "should have empty category list" do
283
+ article = create_article
284
+ Nesta::Page.find_by_path(article.path).categories.should be_empty
285
+ end
286
+ end
287
+
288
+ describe "without metadata" do
289
+ before(:each) do
290
+ create_article
291
+ @article = Nesta::Page.find_all.first
292
+ end
293
+
294
+ it "should use default layout" do
295
+ @article.layout.should == :layout
296
+ end
297
+
298
+ it "should use default template" do
299
+ @article.template.should == :page
300
+ end
301
+
302
+ it "should parse heading correctly" do
303
+ @article.to_html.should have_tag("h1", "My article")
304
+ end
305
+
306
+ it "should have default read more link text" do
307
+ @article.read_more.should == "Continue reading"
308
+ end
309
+
310
+ it "should not have description" do
311
+ @article.description.should be_nil
312
+ end
313
+
314
+ it "should not have keywords" do
315
+ @article.keywords.should be_nil
316
+ end
317
+ end
318
+
319
+ describe "with metadata" do
320
+ before(:each) do
321
+ @layout = 'my_layout'
322
+ @template = 'my_template'
323
+ @date = '07 September 2009'
324
+ @keywords = 'things, stuff'
325
+ @description = 'Page about stuff'
326
+ @summary = 'Multiline\n\nsummary'
327
+ @read_more = 'Continue at your leisure'
328
+ @skillz = 'ruby, guitar, bowstaff'
329
+ @article = create_article(:metadata => {
330
+ 'layout' => @layout,
331
+ 'template' => @template,
332
+ 'date' => @date.gsub('September', 'Sep'),
333
+ 'description' => @description,
334
+ 'keywords' => @keywords,
335
+ 'summary' => @summary,
336
+ 'read more' => @read_more,
337
+ 'skillz' => @skillz
338
+ })
339
+ end
340
+
341
+ it "should override default layout" do
342
+ @article.layout.should == @layout.to_sym
343
+ end
344
+
345
+ it "should override default template" do
346
+ @article.template.should == @template.to_sym
347
+ end
348
+
349
+ it "should set permalink to basename of filename" do
350
+ @article.permalink.should == 'my-article'
351
+ end
352
+
353
+ it "should set path from filename" do
354
+ @article.path.should == 'article-prefix/my-article'
355
+ end
356
+
357
+ it "should retrieve heading" do
358
+ @article.heading.should == 'My article'
359
+ end
360
+
361
+ it "should be possible to convert an article to HTML" do
362
+ @article.to_html.should have_tag("h1", "My article")
363
+ end
364
+
365
+ it "should not include metadata in the HTML" do
366
+ @article.to_html.should_not have_tag("p", /^Date/)
367
+ end
368
+
369
+ it "should not include heading in body" do
370
+ @article.body.should_not have_tag("h1", "My article")
371
+ end
372
+
373
+ it "should retrieve description from metadata" do
374
+ @article.description.should == @description
375
+ end
376
+
377
+ it "should retrieve keywords from metadata" do
378
+ @article.keywords.should == @keywords
379
+ end
380
+
381
+ it "should retrieve date published from metadata" do
382
+ @article.date.strftime("%d %B %Y").should == @date
383
+ end
384
+
385
+ it "should retrieve read more link from metadata" do
386
+ @article.read_more.should == @read_more
387
+ end
388
+
389
+ it "should retrieve summary text from metadata" do
390
+ @article.summary.should match(/#{@summary.split('\n\n').first}/)
391
+ end
392
+
393
+ it "should treat double newline chars as paragraph break in summary" do
394
+ @article.summary.should match(/#{@summary.split('\n\n').last}/)
395
+ end
396
+
397
+ it "should allow arbitrary access to metadata" do
398
+ @article.metadata('skillz').should == @skillz
399
+ end
400
+ end
401
+
402
+ describe "when checking last modification time" do
403
+ before(:each) do
404
+ create_article
405
+ @article = Nesta::Page.find_all.first
406
+ end
407
+
408
+ it "should check filesystem" do
409
+ mock_file_stat(:should_receive, @article.filename, "3 January 2009")
410
+ @article.last_modified.should == Time.parse("3 January 2009")
411
+ end
412
+ end
413
+ end
414
+
415
+ describe "All types of page" do
416
+ include ConfigSpecHelper
417
+ include ModelFactory
418
+
419
+ before(:each) do
420
+ stub_configuration
421
+ end
422
+
423
+ after(:each) do
424
+ remove_fixtures
425
+ Nesta::FileModel.purge_cache
426
+ end
427
+
428
+ it "should still return top level menu items" do
429
+ # Page.menu_items is deprecated; we're keeping it for the moment so
430
+ # that we don't break themes or code in a local app.rb (just yet).
431
+ page1 = create_category(:path => "page-1")
432
+ page2 = create_category(:path => "page-2")
433
+ create_menu([page1.path, page2.path].join("\n"))
434
+ Nesta::Page.menu_items.should == [page1, page2]
435
+ end
436
+ end
437
+
438
+ describe "Markdown page" do
439
+ include ConfigSpecHelper
440
+
441
+ before(:each) do
442
+ @extension = :mdown
443
+ end
444
+
445
+ it_should_behave_like "Page"
446
+
447
+ it "should set heading from first h1 tag" do
448
+ create_page(
449
+ :path => "a-page",
450
+ :heading => "First heading",
451
+ :content => "# Second heading"
452
+ )
453
+ Nesta::Page.find_by_path("a-page").heading.should == "First heading"
454
+ end
455
+ end
456
+
457
+ describe "Haml page" do
458
+ include ConfigSpecHelper
459
+
460
+ before(:each) do
461
+ @extension = :haml
462
+ end
463
+
464
+ it_should_behave_like "Page"
465
+
466
+ it "should set heading from first h1 tag" do
467
+ create_page(
468
+ :path => "a-page",
469
+ :heading => "First heading",
470
+ :content => "%h1 Second heading"
471
+ )
472
+ Nesta::Page.find_by_path("a-page").heading.should == "First heading"
473
+ end
474
+ end
475
+
476
+ describe "Textile page" do
477
+ include ConfigSpecHelper
478
+
479
+ before(:each) do
480
+ @extension = :textile
481
+ end
482
+
483
+ it_should_behave_like "Page"
484
+
485
+ it "should set heading from first h1 tag" do
486
+ create_page(
487
+ :path => "a-page",
488
+ :heading => "First heading",
489
+ :content => "h1. Second heading"
490
+ )
491
+ Nesta::Page.find_by_path("a-page").heading.should == "First heading"
492
+ end
493
+ end
494
+
495
+ describe "Menu" do
496
+ include ConfigSpecHelper
497
+ include ModelFactory
498
+
499
+ before(:each) do
500
+ stub_configuration
501
+ @page = create_page(:path => "page-1")
502
+ end
503
+
504
+ after(:each) do
505
+ remove_fixtures
506
+ Nesta::FileModel.purge_cache
507
+ end
508
+
509
+ it "should find top level menu items" do
510
+ text = [@page.path, "no-such-page"].join("\n")
511
+ create_menu(text)
512
+ Nesta::Menu.top_level.should == [@page]
513
+ end
514
+
515
+ it "should find all items in the menu" do
516
+ create_menu(@page.path)
517
+ Nesta::Menu.full_menu.should == [@page]
518
+ Nesta::Menu.for_path('/').should == [@page]
519
+ end
520
+
521
+ describe "with nested sub menus" do
522
+ before(:each) do
523
+ (2..6).each do |i|
524
+ instance_variable_set("@page#{i}", create_page(:path => "page-#{i}"))
525
+ end
526
+ text = <<-EOF
527
+ #{@page.path}
528
+ #{@page2.path}
529
+ #{@page3.path}
530
+ #{@page4.path}
531
+ #{@page5.path}
532
+ #{@page6.path}
533
+ EOF
534
+ create_menu(text)
535
+ end
536
+
537
+ it "should return top level menu items" do
538
+ Nesta::Menu.top_level.should == [@page, @page5]
539
+ end
540
+
541
+ it "should return full tree of menu items" do
542
+ Nesta::Menu.full_menu.should ==
543
+ [@page, [@page2, [@page3, @page4]], @page5, [@page6]]
544
+ end
545
+
546
+ it "should return part of the tree of menu items" do
547
+ Nesta::Menu.for_path(@page2.path).should == [@page2, [@page3, @page4]]
548
+ end
549
+
550
+ it "should deem menu for path that isn't in menu to be nil" do
551
+ Nesta::Menu.for_path('wibble').should be_nil
552
+ end
553
+ end
554
+ end