spontaneous 0.2.0.beta5 → 0.2.0.beta6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (227) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +39 -0
  4. data/Gemfile +2 -0
  5. data/Readme.markdown +4 -4
  6. data/application/css/core.css.scss +144 -43
  7. data/application/css/definitions.css.scss +50 -16
  8. data/application/css/dialogue.css.scss +5 -2
  9. data/application/css/editing.css.scss +7 -7
  10. data/application/css/font.css.scss +1 -1
  11. data/application/css/meta.css.scss +6 -6
  12. data/application/css/popover.css.scss +6 -6
  13. data/application/css/top.css.scss +8 -1
  14. data/application/js/add_alias_dialogue.js +137 -36
  15. data/application/js/add_home_dialogue.js +10 -10
  16. data/application/js/ajax.js +26 -26
  17. data/application/js/authentication.js +2 -2
  18. data/application/js/box.js +21 -10
  19. data/application/js/box_container.js +13 -7
  20. data/application/js/compatibility.js +19 -17
  21. data/application/js/conflicted_field_dialogue.js +5 -5
  22. data/application/js/content.js +22 -16
  23. data/application/js/content_area.js +62 -33
  24. data/application/js/dialogue.js +16 -16
  25. data/application/js/dom.js +9 -10
  26. data/application/js/edit_panel.js +25 -20
  27. data/application/js/editing.js +21 -8
  28. data/application/js/entry.js +1 -1
  29. data/application/js/extensions.js +11 -11
  30. data/application/js/field/boolean.js +6 -6
  31. data/application/js/field/date.js +1 -1
  32. data/application/js/field/file.js +17 -17
  33. data/application/js/field/image.js +27 -27
  34. data/application/js/field/markdown.js +72 -71
  35. data/application/js/field/select.js +9 -9
  36. data/application/js/field/string.js +3 -3
  37. data/application/js/field/webvideo.js +2 -2
  38. data/application/js/field_preview.js +3 -0
  39. data/application/js/init.js +3 -2
  40. data/application/js/jquery-selection-position.js +13 -13
  41. data/application/js/location.js +17 -12
  42. data/application/js/login.js +2 -2
  43. data/application/js/meta_view/user_admin.js +101 -101
  44. data/application/js/metadata.js +1 -1
  45. data/application/js/page.js +2 -2
  46. data/application/js/page_browser.js +13 -13
  47. data/application/js/page_entry.js +1 -1
  48. data/application/js/panel/root_menu.js +10 -10
  49. data/application/js/popover.js +6 -5
  50. data/application/js/popover_view.js +5 -5
  51. data/application/js/preview.js +10 -4
  52. data/application/js/progress.js +6 -6
  53. data/application/js/properties.js +35 -6
  54. data/application/js/publish.js +43 -43
  55. data/application/js/require.js +14 -14
  56. data/application/js/services.js +3 -3
  57. data/application/js/sharded_upload.js +9 -8
  58. data/application/js/side_bar.js +5 -5
  59. data/application/js/state.js +2 -2
  60. data/application/js/status_bar.js +6 -6
  61. data/application/js/top_bar.js +97 -65
  62. data/application/js/types.js +9 -6
  63. data/application/js/upload.js +4 -4
  64. data/application/js/upload_manager.js +21 -21
  65. data/application/js/user.js +1 -1
  66. data/application/js/vendor/jquery.velocity.min.js +7 -0
  67. data/application/js/views.js +32 -8
  68. data/application/js/views/box_view.js +51 -31
  69. data/application/js/views/page_piece_view.js +17 -15
  70. data/application/js/views/page_view.js +54 -43
  71. data/application/js/views/piece_view.js +44 -37
  72. data/application/static/font/fontawesome-webfont-4f0022f25672c7f501c339cbf98d9117.ttf +0 -0
  73. data/application/views/index.erb +1 -0
  74. data/db/migrations/20130114120000_create_revision_tables.rb +2 -1
  75. data/db/migrations/20130813111009_increase_path_length.rb +11 -2
  76. data/db/migrations/20140506171823_add_index_to_target_id.rb +11 -0
  77. data/db/migrations/20140514090204_add_content_hash.rb +59 -0
  78. data/db/migrations/20140519150253_add_content_hash_timestamp.rb +20 -0
  79. data/lib/spontaneous.rb +0 -1
  80. data/lib/spontaneous/asset/environment.rb +77 -15
  81. data/lib/spontaneous/box.rb +21 -0
  82. data/lib/spontaneous/capistrano/deploy.rb +1 -1
  83. data/lib/spontaneous/capistrano/sync.rb +8 -7
  84. data/lib/spontaneous/change.rb +4 -2
  85. data/lib/spontaneous/cli/fields.rb +7 -3
  86. data/lib/spontaneous/cli/generate.rb +2 -0
  87. data/lib/spontaneous/cli/init.rb +24 -93
  88. data/lib/spontaneous/cli/init/db.rb +94 -0
  89. data/lib/spontaneous/cli/init/mysql.rb +17 -0
  90. data/lib/spontaneous/cli/init/postgresql.rb +33 -0
  91. data/lib/spontaneous/cli/init/sqlite.rb +14 -0
  92. data/lib/spontaneous/cli/site.rb +45 -20
  93. data/lib/spontaneous/collections/box_set.rb +3 -0
  94. data/lib/spontaneous/collections/entry_set.rb +43 -4
  95. data/lib/spontaneous/collections/field_set.rb +14 -2
  96. data/lib/spontaneous/data_mapper.rb +40 -7
  97. data/lib/spontaneous/data_mapper/content_model.rb +1 -1
  98. data/lib/spontaneous/data_mapper/content_model/associations.rb +63 -12
  99. data/lib/spontaneous/data_mapper/content_model/timestamps.rb +9 -14
  100. data/lib/spontaneous/data_mapper/content_table.rb +4 -2
  101. data/lib/spontaneous/data_mapper/dataset.rb +31 -2
  102. data/lib/spontaneous/data_mapper/scope.rb +37 -20
  103. data/lib/spontaneous/errors.rb +6 -0
  104. data/lib/spontaneous/facet.rb +20 -10
  105. data/lib/spontaneous/field/base.rb +8 -1
  106. data/lib/spontaneous/field/file.rb +28 -3
  107. data/lib/spontaneous/field/image.rb +2 -0
  108. data/lib/spontaneous/field/update.rb +6 -0
  109. data/lib/spontaneous/field/webvideo/vimeo.rb +6 -1
  110. data/lib/spontaneous/field/webvideo/vine.rb +1 -1
  111. data/lib/spontaneous/field/webvideo/youtube.rb +1 -1
  112. data/lib/spontaneous/generators/site.rb +6 -4
  113. data/lib/spontaneous/generators/site/.gitignore +1 -0
  114. data/lib/spontaneous/generators/site/Gemfile.tt +3 -3
  115. data/lib/spontaneous/generators/site/config/{indexes.rb.tt → initializers/indexes.rb.tt} +0 -0
  116. data/lib/spontaneous/generators/site/config/initializers/publishing.rb.tt +78 -0
  117. data/lib/spontaneous/generators/site/{config/database.yml.tt → db/mysql2.yml.tt} +7 -6
  118. data/lib/spontaneous/generators/site/db/postgres.yml.tt +25 -0
  119. data/lib/spontaneous/generators/site/db/sqlite3.yml.tt +18 -0
  120. data/lib/spontaneous/generators/site/public/humans.txt.tt +14 -0
  121. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +51 -0
  122. data/lib/spontaneous/loader.rb +1 -1
  123. data/lib/spontaneous/logger.rb +1 -1
  124. data/lib/spontaneous/media/image/optimizer.rb +1 -1
  125. data/lib/spontaneous/media/image/processor.rb +11 -2
  126. data/lib/spontaneous/media/image/renderable.rb +2 -0
  127. data/lib/spontaneous/model.rb +3 -0
  128. data/lib/spontaneous/model/box/allowed_types.rb +17 -4
  129. data/lib/spontaneous/model/core.rb +36 -3
  130. data/lib/spontaneous/model/core/aliases.rb +5 -2
  131. data/lib/spontaneous/model/core/boxes.rb +6 -0
  132. data/lib/spontaneous/model/core/cascading_change.rb +38 -0
  133. data/lib/spontaneous/model/core/content_hash.rb +171 -0
  134. data/lib/spontaneous/model/core/entries.rb +0 -19
  135. data/lib/spontaneous/model/core/fields.rb +11 -0
  136. data/lib/spontaneous/model/core/modifications.rb +22 -21
  137. data/lib/spontaneous/model/core/render.rb +3 -0
  138. data/lib/spontaneous/model/core/serialisation.rb +18 -17
  139. data/lib/spontaneous/model/page.rb +35 -8
  140. data/lib/spontaneous/model/page/page_tree.rb +20 -8
  141. data/lib/spontaneous/model/page/paths.rb +79 -50
  142. data/lib/spontaneous/model/page/singleton.rb +71 -0
  143. data/lib/spontaneous/model/page/site_map.rb +2 -1
  144. data/lib/spontaneous/model/page/site_timestamps.rb +2 -2
  145. data/lib/spontaneous/model/piece.rb +10 -0
  146. data/lib/spontaneous/output/context.rb +13 -6
  147. data/lib/spontaneous/output/format.rb +30 -5
  148. data/lib/spontaneous/output/helpers/script_helper.rb +8 -0
  149. data/lib/spontaneous/output/helpers/stylesheet_helper.rb +7 -0
  150. data/lib/spontaneous/output/renderable.rb +16 -0
  151. data/lib/spontaneous/output/store.rb +1 -1
  152. data/lib/spontaneous/output/template/renderer.rb +2 -2
  153. data/lib/spontaneous/page_piece.rb +25 -1
  154. data/lib/spontaneous/prototypes/box_prototype.rb +13 -0
  155. data/lib/spontaneous/prototypes/field_prototype.rb +7 -4
  156. data/lib/spontaneous/publishing.rb +10 -5
  157. data/lib/spontaneous/publishing/immediate.rb +32 -349
  158. data/lib/spontaneous/publishing/pipeline.rb +43 -0
  159. data/lib/spontaneous/publishing/progress.rb +186 -0
  160. data/lib/spontaneous/publishing/publish.rb +107 -0
  161. data/lib/spontaneous/publishing/rerender.rb +17 -0
  162. data/lib/spontaneous/publishing/revision.rb +53 -18
  163. data/lib/spontaneous/publishing/simultaneous.rb +1 -1
  164. data/lib/spontaneous/publishing/steps.rb +154 -0
  165. data/lib/spontaneous/publishing/steps/activate_revision.rb +45 -0
  166. data/lib/spontaneous/publishing/steps/archive_old_revisions.rb +22 -0
  167. data/lib/spontaneous/publishing/steps/base_step.rb +49 -0
  168. data/lib/spontaneous/publishing/steps/copy_static_files.rb +74 -0
  169. data/lib/spontaneous/publishing/steps/create_revision_directory.rb +24 -0
  170. data/lib/spontaneous/publishing/steps/generate_rackup_file.rb +51 -0
  171. data/lib/spontaneous/publishing/steps/generate_search_indexes.rb +24 -0
  172. data/lib/spontaneous/publishing/steps/render_revision.rb +69 -0
  173. data/lib/spontaneous/publishing/steps/write_revision_file.rb +43 -0
  174. data/lib/spontaneous/rack/back.rb +3 -1
  175. data/lib/spontaneous/rack/back/alias.rb +9 -8
  176. data/lib/spontaneous/rack/front.rb +1 -1
  177. data/lib/spontaneous/rack/middleware.rb +7 -4
  178. data/lib/spontaneous/rack/middleware/transaction.rb +14 -0
  179. data/lib/spontaneous/rack/page_controller.rb +23 -8
  180. data/lib/spontaneous/revision.rb +5 -10
  181. data/lib/spontaneous/schema.rb +5 -0
  182. data/lib/spontaneous/server.rb +3 -1
  183. data/lib/spontaneous/site.rb +17 -10
  184. data/lib/spontaneous/site/publishing.rb +25 -3
  185. data/lib/spontaneous/site/state.rb +7 -3
  186. data/lib/spontaneous/tasks/database.rake +5 -10
  187. data/lib/spontaneous/utils/database/mysql_dumper.rb +5 -1
  188. data/lib/spontaneous/version.rb +1 -1
  189. data/spontaneous.gemspec +4 -3
  190. data/test/fixtures/example_application/config/initializers/initializer1.rb +1 -0
  191. data/test/fixtures/example_application/config/initializers/initializer2.rb +1 -0
  192. data/test/fixtures/example_application/config/initializers/publishing.rb +13 -0
  193. data/test/fixtures/search/config/{indexes.rb → initializers/indexes.rb} +0 -0
  194. data/test/fixtures/serialisation/root_hash.yaml.erb +10 -4
  195. data/test/functional/test_application.rb +10 -0
  196. data/test/functional/test_back.rb +23 -5
  197. data/test/functional/test_cli.rb +98 -34
  198. data/test/functional/test_front.rb +7 -3
  199. data/test/test_helper.rb +35 -28
  200. data/test/unit/test_alias.rb +20 -3
  201. data/test/unit/test_assets.rb +58 -30
  202. data/test/unit/test_changesets.rb +20 -12
  203. data/test/unit/test_content_hash.rb +496 -0
  204. data/test/unit/test_context.rb +28 -1
  205. data/test/unit/test_controllers.rb +96 -61
  206. data/test/unit/test_crypt.rb +1 -8
  207. data/test/unit/test_datamapper.rb +95 -19
  208. data/test/unit/test_features.rb +1 -4
  209. data/test/unit/test_fields.rb +61 -12
  210. data/test/unit/test_generators.rb +39 -2
  211. data/test/unit/test_images.rb +3 -1
  212. data/test/unit/test_modifications.rb +224 -219
  213. data/test/unit/test_output_store.rb +10 -0
  214. data/test/unit/{test_formats.rb → test_outputs.rb} +75 -6
  215. data/test/unit/test_page.rb +61 -15
  216. data/test/unit/test_plugins.rb +2 -42
  217. data/test/unit/test_publishing_pipeline.rb +1050 -0
  218. data/test/unit/test_render.rb +30 -0
  219. data/test/unit/test_revisions.rb +110 -2
  220. data/test/unit/test_schema.rb +4 -0
  221. data/test/unit/test_search.rb +1 -1
  222. data/test/unit/test_serialisation.rb +6 -1
  223. data/test/unit/test_singletons.rb +159 -0
  224. data/test/unit/test_site.rb +71 -44
  225. metadata +140 -86
  226. data/application/static/font/fontawesome-webfont-1c66a4738b40ef0f6b1abca0ba9a796d.ttf +0 -0
  227. data/test/unit/test_publishing.rb +0 -330
@@ -147,6 +147,25 @@ describe "Render" do
147
147
  @content.render(:epub).must_equal "<EPUB><title>The Title</title><body>The Description</body></EPUB>\n"
148
148
  end
149
149
 
150
+ it "can specify an alternate object as the content source" do
151
+ class Page
152
+ layout(:html) { "=${title}"}
153
+ end
154
+ class DivertedPage < Page
155
+ layout(:html) { "!${title}"}
156
+ renders { sections1.first }
157
+ end
158
+ parent = DivertedPage.create(title: "parent")
159
+ child = Page.create(title: "child")
160
+ @root.sections1 << parent
161
+ @root.save
162
+ parent.sections1 << child
163
+ parent.save
164
+ child.save
165
+ expected = child.render(:html)
166
+ parent.render(:html).must_equal expected
167
+ end
168
+
150
169
  describe "piece trees" do
151
170
  before do
152
171
  @page = ::Page.create
@@ -412,6 +431,12 @@ describe "Render" do
412
431
  @parent.things.render.must_equal "Child\n"
413
432
  @parent.render.must_equal "<html>Child\n</html>\n"
414
433
  end
434
+
435
+ it "renders using the inline style when loaded directly" do
436
+ id = @page.id
437
+ PageClass.layout(:html) { "=${::PageClass.get(#{id})}" }
438
+ @parent.render.must_equal "=Child\n"
439
+ end
415
440
  end
416
441
 
417
442
  describe "params in templates" do
@@ -572,6 +597,7 @@ describe "Render" do
572
597
  @site.background_mode = :immediate
573
598
  @site.output_store :Memory
574
599
 
600
+ ::Spontaneous::State.delete
575
601
  ::Content.delete
576
602
  ::Content.delete_revision(1) rescue nil
577
603
  @renderer = Spontaneous::Output::Template::PublishedRenderer.new(@site, 1)
@@ -593,6 +619,10 @@ describe "Render" do
593
619
 
594
620
  [@root, @dynamic, @static].each(&:save)
595
621
 
622
+ @site.publish do
623
+ run :render_revision
624
+ run :activate_revision
625
+ end
596
626
  @site.publish_all
597
627
  end
598
628
 
@@ -228,10 +228,21 @@ describe "Revisions" do
228
228
  Content.archive_dataset.select(:revision).group(:revision).all.must_equal [{:revision => 1}]
229
229
  end
230
230
 
231
+ it "adds an index for the primary key" do
232
+ Revision.create(Content, @revision)
233
+ pk = Content.primary_key
234
+ published_indexes = DB.indexes(Content.revision_table(@revision))
235
+ pk_index = published_indexes.detect { |name, index| index[:columns] == [pk] }
236
+ pk_index.wont_equal nil
237
+ name, options = pk_index
238
+ options[:unique].must_equal true
239
+ end
240
+
231
241
  it "have the correct indexes" do
232
242
  Revision.create(Content, @revision)
233
243
  content_indexes = DB.indexes(:content)
234
- published_indexes = DB.indexes(Content.revision_table(@revision))
244
+ # filter out the pk index as the DB::indexes call doesn't include it
245
+ published_indexes = DB.indexes(Content.revision_table(@revision)).reject { |name, index| index[:columns] == [:id] }
235
246
  # made slightly complex by the fact that the index names depend on the table names
236
247
  # (which are different)
237
248
  assert_has_elements published_indexes.values, content_indexes.values
@@ -360,7 +371,7 @@ describe "Revisions" do
360
371
  end
361
372
  end
362
373
 
363
- it "insert an entry value into the parent of a newly added page when that page is published aaa" do
374
+ it "insert an entry value into the parent of a newly added page when that page is published" do
364
375
  editable1 = Content.first(:uid => '0')
365
376
  new_page = Page.new(:uid => "new")
366
377
  editable1.things << new_page
@@ -442,6 +453,103 @@ describe "Revisions" do
442
453
  end
443
454
  end
444
455
 
456
+ describe "reverting changes" do
457
+ it "reverts page fields"
458
+ it "reverts piece fields"
459
+ it "removes added pieces"
460
+ it "restores removed pieces"
461
+ it "restores deleted pages"
462
+ end
463
+
464
+ describe "content hashes" do
465
+ before do
466
+ @revision = 1
467
+ Revision.delete(Content, @revision+1)
468
+ end
469
+ it "starts with a published_content_hash of nil" do
470
+ first = Content.first
471
+ first.published_content_hash.must_equal nil
472
+ first.content_hash.wont_equal nil
473
+ first.content_hash.length.must_equal 32
474
+ end
475
+
476
+ it "sets the published_content_hash on first publish" do
477
+ first = Content.first
478
+ content_hash = first.content_hash
479
+ first.reload.published_content_hash.must_be_nil
480
+ first.content_hash_changed.must_equal true
481
+ Revision.create(Content, @revision)
482
+ first.reload.published_content_hash.must_equal content_hash
483
+ first.content_hash_changed.must_equal false
484
+ Content.with_editable do
485
+ first.reload.published_content_hash.must_equal content_hash
486
+ first.content_hash_changed.must_equal false
487
+ end
488
+ Content.with_revision(@revision) do
489
+ first.reload.published_content_hash.must_equal content_hash
490
+ end
491
+ end
492
+
493
+ it "updates the published_content_hash on later publishes" do
494
+ first = Page.first
495
+ content_hash = first.content_hash
496
+ Revision.create(Content, @revision)
497
+ first.reload.published_content_hash.must_equal content_hash
498
+ first.update(title: "not the same")
499
+ content_hash2 = first.content_hash
500
+ content_hash2.wont_equal content_hash
501
+ added = Page.create
502
+ added.published_content_hash.must_be_nil
503
+ added.content_hash_changed.must_equal true
504
+ content_hash_added = added.content_hash
505
+ Revision.create(Content, @revision+1)
506
+ first.reload.published_content_hash.must_equal content_hash2
507
+ first.content_hash_changed.must_equal false
508
+ Content.with_editable do
509
+ c = Page.first :id => added.id
510
+ c.published_content_hash.must_equal content_hash_added
511
+ c.content_hash_changed.must_equal false
512
+ c = Page.first :id => first.id
513
+ c.published_content_hash.must_equal content_hash2
514
+ c.content_hash_changed.must_equal false
515
+ end
516
+ Content.with_revision(@revision+1) do
517
+ c = Page.first :id => added.id
518
+ c.published_content_hash.must_equal content_hash_added
519
+ c.content_hash_changed.must_equal false
520
+ c = Page.first :id => first.id
521
+ c.published_content_hash.must_equal content_hash2
522
+ c.content_hash_changed.must_equal false
523
+ end
524
+ end
525
+
526
+ it "doesn't set published_content_hash for items not published" do
527
+ Revision.create(Content, @revision)
528
+ page = Page.first
529
+ page.title = "changed"
530
+ page.save
531
+ page.content_hash_changed.must_equal true
532
+ content_hash = page.content_hash
533
+ added = Page.create
534
+ added.published_content_hash.must_be_nil
535
+ added.reload.content_hash_changed.must_equal true
536
+ Revision.patch(Content, @revision+1, [page])
537
+ page.reload.published_content_hash.must_equal content_hash
538
+ page.content_hash_changed.must_equal false
539
+ added.reload.published_content_hash.must_be_nil
540
+ added.reload.content_hash_changed.must_equal true
541
+ end
542
+
543
+ it "doesn't set published_content_hash if exception raised in passed block" do
544
+ Content.first.published_content_hash.must_be_nil
545
+ begin
546
+ Revision.create(Content, @revision) do
547
+ raise "Fail"
548
+ end
549
+ rescue Exception; end
550
+ Content.first.published_content_hash.must_be_nil
551
+ end
552
+ end
445
553
 
446
554
  describe "publication timestamps" do
447
555
  before do
@@ -605,6 +605,10 @@ describe "Schema" do
605
605
  A.boxes.cgroup.allowed_types(nil).must_equal [A, C]
606
606
  C.boxes.bgroup.allowed_types(nil).must_equal [A, B]
607
607
  end
608
+
609
+ it "exports the list of allowed types to the ui" do
610
+ B.boxes.agroup.export(nil)[:allowed_types].must_equal [{type:"A"}, {type:"B"}, {type:"C"}]
611
+ end
608
612
  end
609
613
 
610
614
 
@@ -686,7 +686,7 @@ describe "Search" do
686
686
  FileUtils.cp_r(File.expand_path("../../fixtures/search/config", __FILE__), @site.root)
687
687
  end
688
688
 
689
- it "load the config/indexes.rb file" do
689
+ it "load the search initializer file" do
690
690
  @site.expects(:connect_to_database!)
691
691
  @site.initialize!
692
692
  index = @site.indexes[:fast]
@@ -97,7 +97,7 @@ describe "Serialisation" do
97
97
  end
98
98
  end
99
99
 
100
- describe "pieces" do
100
+ describe "pages" do
101
101
  before do
102
102
 
103
103
  date = "2011-07-07"
@@ -161,6 +161,11 @@ describe "Serialisation" do
161
161
  assert_hashes_equal(@root_hash, @root.export)
162
162
  end
163
163
 
164
+ it "exports the private root state" do
165
+ page = SerialisedPage.create_root('#private')
166
+ page.export[:private].must_equal true
167
+ end
168
+
164
169
  it "serialise to JSON" do
165
170
  # hard to test this as the serialisation order appears to change
166
171
  Spot.deserialise_http(@root.serialise_http).must_equal @root.export
@@ -0,0 +1,159 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../test_helper', __FILE__)
4
+
5
+ describe "Singletons" do
6
+ before do
7
+ @site = setup_site
8
+ class ::SingletonPage < ::Page
9
+ group :singletons
10
+ singleton :singleton, :malcom
11
+ end
12
+ end
13
+
14
+ after do
15
+ Object.send :remove_const, :SingletonPage rescue nil
16
+ teardown_site
17
+ end
18
+
19
+ it "prevents creation of two instances of a singleton type" do
20
+ a = SingletonPage.create
21
+ b = nil
22
+ proc {
23
+ b = SingletonPage.create
24
+ }.must_raise(Spontaneous::SingletonException)
25
+ b.must_equal nil
26
+ proc {
27
+ b = SingletonPage.new
28
+ b.save
29
+ }.must_raise(Spontaneous::SingletonException)
30
+ SingletonPage.count.must_equal 1
31
+ end
32
+
33
+ it "allows saving of existing singleton instances" do
34
+ a = SingletonPage.create
35
+ a.uid = "changed"
36
+ a.save
37
+ end
38
+
39
+ it "::singleton?" do
40
+ ::Piece.singleton?.must_equal false
41
+ ::Page.singleton?.must_equal false
42
+ ::SingletonPage.singleton?.must_equal true
43
+ end
44
+
45
+ it "::exists?" do
46
+ SingletonPage.exists?.must_equal false
47
+ end
48
+
49
+ it "exports singleton status to UI" do
50
+ SingletonPage.export[:is_singleton].must_equal true
51
+ end
52
+
53
+ describe "instances" do
54
+ before do
55
+ @page = SingletonPage.create
56
+ @page.reload
57
+ end
58
+
59
+ it "::exists?" do
60
+ SingletonPage.exists?.must_equal true
61
+ end
62
+
63
+ it "provides access to singletons through the site" do
64
+ @site.singleton_page.must_equal @page
65
+ end
66
+
67
+ it "gives a ::instance method on the type" do
68
+ SingletonPage.instance.must_equal @page
69
+ end
70
+
71
+ it "allows for multiple aliases to the instance" do
72
+ @site.singleton.must_equal @page
73
+ @site.malcom.must_equal @page
74
+ end
75
+
76
+ it "allows for testing if a singleton exists" do
77
+ @site.singleton?(:singleton_page).must_equal true
78
+ @site.singleton?(:not_singleton_page).must_equal false
79
+ end
80
+
81
+ it "uses a cache within a mapper scope" do
82
+ instance1 = instance2 = instance3 = nil
83
+ Content.scope do
84
+ instance1 = @site.singleton
85
+ instance2 = @site.singleton
86
+ Content.scope(nil, true) do
87
+ instance3 = @site.singleton
88
+ end
89
+ end
90
+ instance1.object_id.must_equal instance2.object_id
91
+ instance1.object_id.wont_equal instance3.object_id
92
+ end
93
+ end
94
+
95
+ describe "sub-classes" do
96
+ before do
97
+ class ::SingletonSubclassPage < SingletonPage
98
+ end
99
+ end
100
+ after do
101
+ Object.send :remove_const, :SingletonSubclassPage rescue nil
102
+ end
103
+
104
+ it "aren't singletons" do
105
+ SingletonSubclassPage.singleton?.must_equal false
106
+ end
107
+ end
108
+
109
+ describe "boxes" do
110
+ describe "allow" do
111
+ before do
112
+ ::Page.box(:stuff) { allow :Piece } # Make sure allowing pieces doesn't raise an exception
113
+ @box = ::Page.box :sub do
114
+ allow :SingletonPage
115
+ end
116
+ end
117
+
118
+ it "allows the addition of singletons without an instance" do
119
+ @box.allowed_types(nil).must_equal [SingletonPage]
120
+ end
121
+ it "restricts the addition of singletons with an instance" do
122
+ SingletonPage.create
123
+ @box.allowed_types(nil).must_equal []
124
+ end
125
+ end
126
+
127
+ describe "allow_group" do
128
+ before do
129
+ @box = ::Page.box :sub do
130
+ allow_group :singletons
131
+ end
132
+ end
133
+
134
+ it "allows the addition of singletons without an instance" do
135
+ @box.allowed_types(nil).must_equal [SingletonPage]
136
+ end
137
+ it "restricts the addition of singletons with an instance" do
138
+ SingletonPage.create
139
+ @box.allowed_types(nil).must_equal []
140
+ end
141
+ end
142
+
143
+ describe "allow_subclasses" do
144
+ before do
145
+ @box = ::Page.box :sub do
146
+ allow_subclasses :Page
147
+ end
148
+ end
149
+
150
+ it "allows the addition of singletons without an instance" do
151
+ @box.allowed_types(nil).must_equal [SingletonPage]
152
+ end
153
+ it "restricts the addition of singletons with an instance" do
154
+ SingletonPage.create
155
+ @box.allowed_types(nil).must_equal []
156
+ end
157
+ end
158
+ end
159
+ end
@@ -19,6 +19,10 @@ describe "Site" do
19
19
  teardown_site
20
20
  end
21
21
 
22
+ def assert_site_modified(time)
23
+ (@site.modified_at - time).must_be :<=, 1
24
+ end
25
+
22
26
  describe "contents" do
23
27
  before do
24
28
  @root = ::Page.new
@@ -77,15 +81,16 @@ describe "Site" do
77
81
  :type => 'Page',
78
82
  :type_id => ::Page.schema_id,
79
83
  :depth => 3,
84
+ :private => false,
80
85
  :children => {},
81
86
  :ancestors => [
82
- { :id => @root.id, :title => "Homepage", :path => '/', :slug => '', :type => 'Page', :type_id => ::Page.schema_id, :depth => 0, :children => 2 },
83
- { :id => @page1_1.id, :title => "Page 1 1", :path => '/page1-1', :slug => 'page1-1', :type => 'Page', :type_id => ::Page.schema_id, :depth => 1, :children => 1 },
84
- { :id => @page2_1.id, :title => "Page 2 1", :path => '/page1-1/page2-1', :slug => 'page2-1', :type => 'Page', :type_id => ::Page.schema_id, :depth => 2, :children => 2 }
87
+ { :id => @root.id, :title => "Homepage", :path => '/', :slug => '', :type => 'Page', :type_id => ::Page.schema_id, :depth => 0, :children => 2, :private => false},
88
+ { :id => @page1_1.id, :title => "Page 1 1", :path => '/page1-1', :slug => 'page1-1', :type => 'Page', :type_id => ::Page.schema_id, :depth => 1, :children => 1, :private => false},
89
+ { :id => @page2_1.id, :title => "Page 2 1", :path => '/page1-1/page2-1', :slug => 'page2-1', :type => 'Page', :type_id => ::Page.schema_id, :depth => 2, :children => 2, :private => false}
85
90
  ],
86
91
  :generation => { "Subpages" => [
87
- { :id => @page3_1.id, :title => "Page 3 1", :path => '/page1-1/page2-1/page3-1', :slug => 'page3-1', :type => 'Page', :type_id => ::Page.schema_id, :depth => 3, :children => 0 },
88
- { :id => @page3_2.id, :title => "Page 3 2", :path => '/page1-1/page2-1/page3-2', :slug => 'page3-2', :type => 'Page', :type_id => ::Page.schema_id, :depth => 3, :children => 0 }
92
+ { :id => @page3_1.id, :title => "Page 3 1", :path => '/page1-1/page2-1/page3-1', :slug => 'page3-1', :type => 'Page', :type_id => ::Page.schema_id, :depth => 3, :children => 0, :private => false},
93
+ { :id => @page3_2.id, :title => "Page 3 2", :path => '/page1-1/page2-1/page3-2', :slug => 'page3-2', :type => 'Page', :type_id => ::Page.schema_id, :depth => 3, :children => 0, :private => false}
89
94
  ] }
90
95
  })
91
96
 
@@ -97,12 +102,14 @@ describe "Site" do
97
102
  :type => 'Page',
98
103
  :type_id => ::Page.schema_id,
99
104
  :depth => 2,
105
+ :private => false,
100
106
  :children => {
101
107
  "Subpages" => [
102
108
  {:depth=>3,
103
109
  :type=>"Page",
104
110
  :type_id => ::Page.schema_id,
105
111
  :children=>0,
112
+ :private => false,
106
113
  :path=>"/page1-1/page2-1/page3-1",
107
114
  :slug => 'page3-1',
108
115
  :title=>"Page 3 1",
@@ -111,20 +118,26 @@ describe "Site" do
111
118
  :type=>"Page",
112
119
  :type_id => ::Page.schema_id,
113
120
  :children=>0,
121
+ :private => false,
114
122
  :path=>"/page1-1/page2-1/page3-2",
115
123
  :slug => 'page3-2',
116
124
  :title=>"Page 3 2",
117
125
  :id=>@page3_2.id}]},
118
126
  :ancestors => [
119
- { :id => @root.id, :title => "Homepage", :path => '/', :slug => '',:type => 'Page', :type_id => ::Page.schema_id, :depth => 0, :children => 2 },
120
- { :id => @page1_1.id, :title => "Page 1 1", :path => '/page1-1', :slug => 'page1-1',:type => 'Page', :type_id => ::Page.schema_id, :depth => 1, :children => 1 }
127
+ { :id => @root.id, :title => "Homepage", :path => '/', :slug => '',:type => 'Page', :type_id => ::Page.schema_id, :depth => 0, :children => 2, :private => false },
128
+ { :id => @page1_1.id, :title => "Page 1 1", :path => '/page1-1', :slug => 'page1-1',:type => 'Page', :type_id => ::Page.schema_id, :depth => 1, :children => 1, :private => false}
121
129
  ],
122
130
  :generation => {"Subpages" => [
123
- { :id => @page2_1.id, :title => "Page 2 1", :path => '/page1-1/page2-1', :slug => 'page2-1',:type => 'Page', :type_id => ::Page.schema_id, :depth => 2, :children => 2 }
131
+ { :id => @page2_1.id, :title => "Page 2 1", :path => '/page1-1/page2-1', :slug => 'page2-1',:type => 'Page', :type_id => ::Page.schema_id, :depth => 2, :children => 2, :private => false}
124
132
  ]}
125
133
  })
126
134
  end
127
135
 
136
+ it "returns the private status of a page in the map" do
137
+ page = Page.create_root("#private")
138
+ page.map_entry[:private].must_equal true
139
+ end
140
+
128
141
 
129
142
  it "retrieve details of the root by default" do
130
143
  @site.map.must_equal Page.root.map_entry
@@ -206,73 +219,87 @@ describe "Site" do
206
219
 
207
220
  it "just return the current time if no modifications have been made" do
208
221
  now = @now + 12
209
- Time.stubs(:now).returns(now)
210
- @site.modified_at.must_equal now
222
+ Timecop.travel(now) do
223
+ assert_site_modified(now)
224
+ end
211
225
  end
212
226
 
213
227
  it "be updated when a page is added" do
214
228
  now = @now + 24
215
- Time.stubs(:now).returns(now)
216
- root = ::Page.create
217
- Time.stubs(:now).returns(now + 200)
218
- @site.modified_at.must_equal now
229
+ Timecop.travel(now) do
230
+ root = ::Page.create
231
+ end
232
+ assert_site_modified(now)
219
233
  end
220
234
 
221
235
  it "be updated when a page's title changes" do
222
236
  root = ::Page.create
223
237
  now = @now + 98
224
- Time.stubs(:now).returns(now)
225
- root.update(:title => "Some Title")
226
- @site.modified_at.must_equal now
238
+ Timecop.travel(now) do
239
+ root.update(:title => "Some Title")
240
+ end
241
+ assert_site_modified(now)
227
242
  end
228
243
 
229
244
  it "be updated when a page's slug changes" do
230
245
  root = ::Page.create
231
246
  now = @now + 98
232
- Time.stubs(:now).returns(now)
233
- root.update(:slug => "updated-slug")
234
- @site.modified_at.must_equal now
247
+ Timecop.travel(now) do
248
+ root.update(:slug => "updated-slug")
249
+ end
250
+ assert_site_modified(now)
235
251
  end
236
252
 
237
253
  it "not be updated when a piece is added" do
238
254
  now1 = @now + 24
239
- Time.stubs(:now).returns(now1)
240
- root = ::Page.create
255
+ root = nil
256
+ Timecop.travel(now1) do
257
+ root = ::Page.create
258
+ end
241
259
  now2 = @now + 240
242
- Time.stubs(:now).returns(now2)
243
- root.subpages << Piece.create
244
- @site.modified_at.must_equal now1
260
+ Timecop.travel(now2) do
261
+ root.subpages << Piece.create
262
+ end
263
+ assert_site_modified(now1)
245
264
  end
246
265
 
247
266
  it "be updated when a page is deleted" do
248
267
  now1 = @now + 24
249
- Time.stubs(:now).returns(now1)
250
- root = ::Page.create
268
+ root = child = nil
269
+ Timecop.travel(now1) do
270
+ root = ::Page.create
271
+ end
251
272
  now2 = @now + 48
252
- Time.stubs(:now).returns(now2)
253
- child = ::Page.new
254
- root.subpages << child
255
- root.save
256
- @site.modified_at.must_equal now2
273
+ Timecop.travel(now2) do
274
+ child = ::Page.new
275
+ root.subpages << child
276
+ root.save
277
+ end
278
+ assert_site_modified(now2)
257
279
  now3 = @now + 128
258
- Time.stubs(:now).returns(now3)
259
- child.destroy
260
- @site.modified_at.must_equal now3
280
+ Timecop.travel(now3) do
281
+ child.destroy
282
+ end
283
+ assert_site_modified(now3)
261
284
  end
262
285
 
263
286
  it "not be updated when a piece is deleted" do
264
287
  now1 = @now + 24
265
- Time.stubs(:now).returns(now1)
266
- root = ::Page.create
288
+ root = piece = nil
289
+ Timecop.travel(now1) do
290
+ root = ::Page.create
291
+ end
267
292
  now2 = @now + 240
268
- Time.stubs(:now).returns(now2)
269
- piece = ::Piece.create
270
- root.subpages << piece
271
- @site.modified_at.must_equal now1
293
+ Timecop.travel(now2) do
294
+ piece = ::Piece.create
295
+ root.subpages << piece
296
+ end
297
+ assert_site_modified(now1)
272
298
  now3 = @now + 480
273
- Time.stubs(:now).returns(now3)
274
- piece.reload.destroy
275
- @site.modified_at.must_equal now1
299
+ Timecop.travel(now3) do
300
+ piece.reload.destroy
301
+ end
302
+ assert_site_modified(now1)
276
303
  end
277
304
  end
278
305