mars-nesta 0.9.4

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