spontaneous 0.1.0.alpha1

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 (556) hide show
  1. data/Gemfile +49 -0
  2. data/Gemfile.lock +146 -0
  3. data/LICENSE +0 -0
  4. data/README +0 -0
  5. data/Rakefile +284 -0
  6. data/Readme.markdown +7 -0
  7. data/application/css/add_alias_dialogue.scss +27 -0
  8. data/application/css/definitions.scss +249 -0
  9. data/application/css/developer.scss +9 -0
  10. data/application/css/editing.scss +649 -0
  11. data/application/css/login.scss +91 -0
  12. data/application/css/min/54ee0ed3c7fac7632bd5c020d69e9a2503e0c88c.css +1 -0
  13. data/application/css/min/c256adc144e2bdd0b0539356b04eb62db01e1dc3.css +1 -0
  14. data/application/css/popover.scss +335 -0
  15. data/application/css/schema_error.scss +90 -0
  16. data/application/css/spontaneous.scss +111 -0
  17. data/application/css/unsupported.scss +16 -0
  18. data/application/css/v2.scss +1606 -0
  19. data/application/css/variables.scss +80 -0
  20. data/application/js/add_alias_dialogue.js +59 -0
  21. data/application/js/add_home_dialogue.js +59 -0
  22. data/application/js/ajax.js +99 -0
  23. data/application/js/authentication.js +22 -0
  24. data/application/js/box.js +104 -0
  25. data/application/js/box_container.js +82 -0
  26. data/application/js/compatibility.js +132 -0
  27. data/application/js/conflicted_field_dialogue.js +92 -0
  28. data/application/js/content.js +224 -0
  29. data/application/js/content_area.js +44 -0
  30. data/application/js/dialogue.js +196 -0
  31. data/application/js/dom.js +71 -0
  32. data/application/js/edit_dialogue.js +137 -0
  33. data/application/js/edit_panel.js +232 -0
  34. data/application/js/editing.js +42 -0
  35. data/application/js/entry.js +13 -0
  36. data/application/js/extensions.js +104 -0
  37. data/application/js/field.js +4 -0
  38. data/application/js/field_preview.js +55 -0
  39. data/application/js/field_types/date_field.js +16 -0
  40. data/application/js/field_types/file_field.js +71 -0
  41. data/application/js/field_types/image_field.js +358 -0
  42. data/application/js/field_types/markdown_field.js +656 -0
  43. data/application/js/field_types/string_field.js +185 -0
  44. data/application/js/image.js +72 -0
  45. data/application/js/init.js +34 -0
  46. data/application/js/load.js +4 -0
  47. data/application/js/location.js +157 -0
  48. data/application/js/login.js +53 -0
  49. data/application/js/min/492a209de8ee955fa9c729a765377495001e11b1.js +17 -0
  50. data/application/js/min/80f684d77c940887a1d4a63e3a96102e993baa98.js +88 -0
  51. data/application/js/min/b8abf302a824c35385ff517b34111e1710ff3b37.js +2 -0
  52. data/application/js/min/c7140ec9475e5bf868b901e0621338d7d162358b.js +3 -0
  53. data/application/js/min/f07f2bd6630ee31e1c2288ec223383d8f0658ba6.js +2 -0
  54. data/application/js/page.js +43 -0
  55. data/application/js/page_browser.js +147 -0
  56. data/application/js/page_entry.js +47 -0
  57. data/application/js/popover.js +99 -0
  58. data/application/js/popover_view.js +56 -0
  59. data/application/js/preview.js +64 -0
  60. data/application/js/progress.js +358 -0
  61. data/application/js/properties.js +90 -0
  62. data/application/js/publish.js +187 -0
  63. data/application/js/require.js +129 -0
  64. data/application/js/sharded_upload.js +206 -0
  65. data/application/js/side_bar.js +30 -0
  66. data/application/js/spontaneous.js +6 -0
  67. data/application/js/state.js +64 -0
  68. data/application/js/status_bar.js +47 -0
  69. data/application/js/top_bar.js +368 -0
  70. data/application/js/types.js +98 -0
  71. data/application/js/upload.js +88 -0
  72. data/application/js/upload_manager.js +319 -0
  73. data/application/js/user.js +37 -0
  74. data/application/js/vendor/.DS_Store +0 -0
  75. data/application/js/vendor/JS.Class-2.1.5/CHANGELOG +283 -0
  76. data/application/js/vendor/JS.Class-2.1.5/MIT-LICENSE +30 -0
  77. data/application/js/vendor/JS.Class-2.1.5/README +30 -0
  78. data/application/js/vendor/JS.Class-2.1.5/min/command.js +1 -0
  79. data/application/js/vendor/JS.Class-2.1.5/min/comparable.js +1 -0
  80. data/application/js/vendor/JS.Class-2.1.5/min/constant_scope.js +1 -0
  81. data/application/js/vendor/JS.Class-2.1.5/min/core.js +1 -0
  82. data/application/js/vendor/JS.Class-2.1.5/min/decorator.js +1 -0
  83. data/application/js/vendor/JS.Class-2.1.5/min/enumerable.js +1 -0
  84. data/application/js/vendor/JS.Class-2.1.5/min/forwardable.js +1 -0
  85. data/application/js/vendor/JS.Class-2.1.5/min/hash.js +1 -0
  86. data/application/js/vendor/JS.Class-2.1.5/min/linked_list.js +1 -0
  87. data/application/js/vendor/JS.Class-2.1.5/min/loader.js +1 -0
  88. data/application/js/vendor/JS.Class-2.1.5/min/method_chain.js +1 -0
  89. data/application/js/vendor/JS.Class-2.1.5/min/observable.js +1 -0
  90. data/application/js/vendor/JS.Class-2.1.5/min/package.js +1 -0
  91. data/application/js/vendor/JS.Class-2.1.5/min/proxy.js +1 -0
  92. data/application/js/vendor/JS.Class-2.1.5/min/ruby.js +1 -0
  93. data/application/js/vendor/JS.Class-2.1.5/min/set.js +1 -0
  94. data/application/js/vendor/JS.Class-2.1.5/min/stack_trace.js +1 -0
  95. data/application/js/vendor/JS.Class-2.1.5/min/state.js +1 -0
  96. data/application/js/vendor/JS.Class-2.1.5/min/stdlib.js +16 -0
  97. data/application/js/vendor/JS.Class-2.1.5/src/command.js +93 -0
  98. data/application/js/vendor/JS.Class-2.1.5/src/comparable.js +37 -0
  99. data/application/js/vendor/JS.Class-2.1.5/src/constant_scope.js +48 -0
  100. data/application/js/vendor/JS.Class-2.1.5/src/core.js +1060 -0
  101. data/application/js/vendor/JS.Class-2.1.5/src/decorator.js +50 -0
  102. data/application/js/vendor/JS.Class-2.1.5/src/enumerable.js +505 -0
  103. data/application/js/vendor/JS.Class-2.1.5/src/forwardable.js +22 -0
  104. data/application/js/vendor/JS.Class-2.1.5/src/hash.js +334 -0
  105. data/application/js/vendor/JS.Class-2.1.5/src/linked_list.js +114 -0
  106. data/application/js/vendor/JS.Class-2.1.5/src/loader.js +553 -0
  107. data/application/js/vendor/JS.Class-2.1.5/src/method_chain.js +172 -0
  108. data/application/js/vendor/JS.Class-2.1.5/src/observable.js +55 -0
  109. data/application/js/vendor/JS.Class-2.1.5/src/package.js +472 -0
  110. data/application/js/vendor/JS.Class-2.1.5/src/proxy.js +58 -0
  111. data/application/js/vendor/JS.Class-2.1.5/src/ruby.js +44 -0
  112. data/application/js/vendor/JS.Class-2.1.5/src/set.js +332 -0
  113. data/application/js/vendor/JS.Class-2.1.5/src/stack_trace.js +151 -0
  114. data/application/js/vendor/JS.Class-2.1.5/src/state.js +95 -0
  115. data/application/js/vendor/JS.Class-2.1.5/src/stdlib.js +2612 -0
  116. data/application/js/vendor/crypto-2.3.0-crypto.js +160 -0
  117. data/application/js/vendor/crypto-2.3.0-sha1.js +91 -0
  118. data/application/js/vendor/diff_match_patch.js +2153 -0
  119. data/application/js/vendor/jquery-1.4.2.min.js +154 -0
  120. data/application/js/vendor/jquery-1.4.3.min.js +166 -0
  121. data/application/js/vendor/jquery-1.5.1.min.js +16 -0
  122. data/application/js/vendor/jquery-1.5.1rc1.min.js +24 -0
  123. data/application/js/vendor/jquery-1.6.2.min.js +18 -0
  124. data/application/js/vendor/jquery-ui-1.8.6.custom.min.js +265 -0
  125. data/application/js/vendor/jquery-ui-1.8.9.custom.min.js +415 -0
  126. data/application/js/vendor/jquery-ui-1.8.custom.min.js +106 -0
  127. data/application/js/vendor/jquery.hotkeys-0.7.9.js +248 -0
  128. data/application/js/vendor/jquery.hotkeys-0.7.9.min.js +19 -0
  129. data/application/js/vendor/jsdiff.js +169 -0
  130. data/application/js/views/box_view.js +229 -0
  131. data/application/js/views/page_piece_view.js +45 -0
  132. data/application/js/views/page_view.js +238 -0
  133. data/application/js/views/piece_view.js +178 -0
  134. data/application/js/views.js +110 -0
  135. data/application/static/editing-0-noise.png +0 -0
  136. data/application/static/editing-1-noise.png +0 -0
  137. data/application/static/editing-texture-1.png +0 -0
  138. data/application/static/editing-texture.png +0 -0
  139. data/application/static/editing-toolbar-shadow-bottom.png +0 -0
  140. data/application/static/editing-toolbar-shadow-top.png +0 -0
  141. data/application/static/favicon.ico +0 -0
  142. data/application/static/inner-glow.png +0 -0
  143. data/application/static/item-buttons.png +0 -0
  144. data/application/static/location-arrow.png +0 -0
  145. data/application/static/logo-400px-transparent.png +0 -0
  146. data/application/static/missing.png +0 -0
  147. data/application/static/orange-down-arrow.png +0 -0
  148. data/application/static/page-browser-next.png +0 -0
  149. data/application/static/paper-texture-dark.png +0 -0
  150. data/application/static/px.gif +0 -0
  151. data/application/static/select-arrow-root.png +0 -0
  152. data/application/static/select-arrow.png +0 -0
  153. data/application/static/slot-down-arrow.png +0 -0
  154. data/application/static/splash.png +0 -0
  155. data/application/static/spontaneous.png +0 -0
  156. data/application/static/spot.png +0 -0
  157. data/application/static/spot.svg +40 -0
  158. data/application/static/texture.png +0 -0
  159. data/application/views/index.erubis +46 -0
  160. data/application/views/login.erubis +69 -0
  161. data/application/views/schema_modification_error.html.erb +61 -0
  162. data/application/views/unsupported.erubis +23 -0
  163. data/bin/limit-upload +5 -0
  164. data/bin/spot +10 -0
  165. data/bin/unlimit-upload +3 -0
  166. data/config/nginx.conf +60 -0
  167. data/db/migrations/20100610142136_init.rb +66 -0
  168. data/db/migrations/20101130104334_timestamps.rb +44 -0
  169. data/db/migrations/20101202113205_site_publishing_flags.rb +12 -0
  170. data/db/migrations/20101206124543_aliases.rb +16 -0
  171. data/db/migrations/20110201133550_visibility.rb +27 -0
  172. data/db/migrations/20110209152710_users_and_groups.rb +58 -0
  173. data/db/migrations/20110215133910_boxes.rb +25 -0
  174. data/db/migrations/20110521114145_remove_slots_and_entries.rb +21 -0
  175. data/db/migrations/20110604192145_rename_schema_id_columns.rb +22 -0
  176. data/db/migrations/20110805141925_rename_site_to_state.rb +11 -0
  177. data/lib/cutaneous/context_helper.rb +82 -0
  178. data/lib/cutaneous/first_pass_parser.rb +23 -0
  179. data/lib/cutaneous/first_pass_renderer.rb +18 -0
  180. data/lib/cutaneous/parser_core.rb +18 -0
  181. data/lib/cutaneous/preview_context.rb +31 -0
  182. data/lib/cutaneous/preview_renderer.rb +15 -0
  183. data/lib/cutaneous/publish_context.rb +9 -0
  184. data/lib/cutaneous/renderer.rb +122 -0
  185. data/lib/cutaneous/request_context.rb +8 -0
  186. data/lib/cutaneous/second_pass_parser.rb +23 -0
  187. data/lib/cutaneous/second_pass_renderer.rb +18 -0
  188. data/lib/cutaneous.rb +47 -0
  189. data/lib/sequel/plugins/content_table_inheritance.rb +196 -0
  190. data/lib/sequel/plugins/yajl_serialization.rb +154 -0
  191. data/lib/spontaneous/application/feature.rb +9 -0
  192. data/lib/spontaneous/application/plugin.rb +13 -0
  193. data/lib/spontaneous/application.rb +8 -0
  194. data/lib/spontaneous/box.rb +232 -0
  195. data/lib/spontaneous/box_style.rb +64 -0
  196. data/lib/spontaneous/change.rb +107 -0
  197. data/lib/spontaneous/cli/adapter.rb +13 -0
  198. data/lib/spontaneous/cli/base.rb +184 -0
  199. data/lib/spontaneous/cli/console.rb +0 -0
  200. data/lib/spontaneous/cli/media.rb +13 -0
  201. data/lib/spontaneous/cli/server.rb +50 -0
  202. data/lib/spontaneous/cli/site.rb +46 -0
  203. data/lib/spontaneous/cli/sync.rb +42 -0
  204. data/lib/spontaneous/cli/tasks.rb +9 -0
  205. data/lib/spontaneous/cli.rb +83 -0
  206. data/lib/spontaneous/collections/box_set.rb +56 -0
  207. data/lib/spontaneous/collections/change_set.rb +43 -0
  208. data/lib/spontaneous/collections/entry_set.rb +83 -0
  209. data/lib/spontaneous/collections/field_set.rb +53 -0
  210. data/lib/spontaneous/collections/prototype_set.rb +131 -0
  211. data/lib/spontaneous/collections/style_set.rb +13 -0
  212. data/lib/spontaneous/config.rb +156 -0
  213. data/lib/spontaneous/constants.rb +24 -0
  214. data/lib/spontaneous/content.rb +113 -0
  215. data/lib/spontaneous/content_query.rb +17 -0
  216. data/lib/spontaneous/errors.rb +48 -0
  217. data/lib/spontaneous/extensions/array.rb +18 -0
  218. data/lib/spontaneous/extensions/class.rb +17 -0
  219. data/lib/spontaneous/extensions/hash.rb +18 -0
  220. data/lib/spontaneous/extensions/json.rb +26 -0
  221. data/lib/spontaneous/extensions/kernel.rb +7 -0
  222. data/lib/spontaneous/extensions/object.rb +30 -0
  223. data/lib/spontaneous/extensions/object_space.rb +12 -0
  224. data/lib/spontaneous/extensions/string.rb +44 -0
  225. data/lib/spontaneous/facet.rb +47 -0
  226. data/lib/spontaneous/field_types/date_field.rb +12 -0
  227. data/lib/spontaneous/field_types/field.rb +252 -0
  228. data/lib/spontaneous/field_types/image_field.rb +329 -0
  229. data/lib/spontaneous/field_types/markdown_field.rb +37 -0
  230. data/lib/spontaneous/field_types/string_field.rb +14 -0
  231. data/lib/spontaneous/field_types.rb +40 -0
  232. data/lib/spontaneous/generators/page/inline.html.cut +1 -0
  233. data/lib/spontaneous/generators/page/page.html.cut.tt +4 -0
  234. data/lib/spontaneous/generators/page/page.rb.tt +9 -0
  235. data/lib/spontaneous/generators/page.rb +38 -0
  236. data/lib/spontaneous/generators/site/.gitignore +4 -0
  237. data/lib/spontaneous/generators/site/Gemfile.tt +31 -0
  238. data/lib/spontaneous/generators/site/Rakefile.tt +6 -0
  239. data/lib/spontaneous/generators/site/config/back.ru +7 -0
  240. data/lib/spontaneous/generators/site/config/boot.rb +19 -0
  241. data/lib/spontaneous/generators/site/config/database.yml.tt +21 -0
  242. data/lib/spontaneous/generators/site/config/deploy.rb.tt +0 -0
  243. data/lib/spontaneous/generators/site/config/environment.rb.tt +8 -0
  244. data/lib/spontaneous/generators/site/config/environments/development.rb.tt +15 -0
  245. data/lib/spontaneous/generators/site/config/environments/production.rb.tt +5 -0
  246. data/lib/spontaneous/generators/site/config/front.ru +8 -0
  247. data/lib/spontaneous/generators/site/config/user_levels.yml +22 -0
  248. data/lib/spontaneous/generators/site/lib/site.rb.tt +4 -0
  249. data/lib/spontaneous/generators/site/lib/tasks/site.rake.tt +8 -0
  250. data/lib/spontaneous/generators/site/public/css/site.css +0 -0
  251. data/lib/spontaneous/generators/site/public/favicon.ico +0 -0
  252. data/lib/spontaneous/generators/site/public/js/.empty_directory +0 -0
  253. data/lib/spontaneous/generators/site/public/js/site.js +0 -0
  254. data/lib/spontaneous/generators/site/public/robots.txt +0 -0
  255. data/lib/spontaneous/generators/site/schema/.map +1 -0
  256. data/lib/spontaneous/generators/site/schema/page.rb.tt +8 -0
  257. data/lib/spontaneous/generators/site/schema/piece.rb.tt +4 -0
  258. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +13 -0
  259. data/lib/spontaneous/generators/site.rb +77 -0
  260. data/lib/spontaneous/generators.rb +23 -0
  261. data/lib/spontaneous/image_size.rb +117 -0
  262. data/lib/spontaneous/json.rb +33 -0
  263. data/lib/spontaneous/layout.rb +15 -0
  264. data/lib/spontaneous/loader.rb +280 -0
  265. data/lib/spontaneous/logger.rb +369 -0
  266. data/lib/spontaneous/media.rb +84 -0
  267. data/lib/spontaneous/page.rb +92 -0
  268. data/lib/spontaneous/page_controller.rb +18 -0
  269. data/lib/spontaneous/page_piece.rb +77 -0
  270. data/lib/spontaneous/paths.rb +30 -0
  271. data/lib/spontaneous/permissions/access_group.rb +50 -0
  272. data/lib/spontaneous/permissions/access_key.rb +35 -0
  273. data/lib/spontaneous/permissions/user.rb +167 -0
  274. data/lib/spontaneous/permissions/user_level.rb +177 -0
  275. data/lib/spontaneous/permissions.rb +55 -0
  276. data/lib/spontaneous/piece.rb +30 -0
  277. data/lib/spontaneous/plugins/aliases.rb +128 -0
  278. data/lib/spontaneous/plugins/allowed_types.rb +173 -0
  279. data/lib/spontaneous/plugins/application/facets.rb +25 -0
  280. data/lib/spontaneous/plugins/application/paths.rb +137 -0
  281. data/lib/spontaneous/plugins/application/render.rb +29 -0
  282. data/lib/spontaneous/plugins/application/serialisation.rb +16 -0
  283. data/lib/spontaneous/plugins/application/state.rb +86 -0
  284. data/lib/spontaneous/plugins/boxes.rb +84 -0
  285. data/lib/spontaneous/plugins/controllers.rb +52 -0
  286. data/lib/spontaneous/plugins/entries.rb +193 -0
  287. data/lib/spontaneous/plugins/entry.rb +51 -0
  288. data/lib/spontaneous/plugins/fields.rb +103 -0
  289. data/lib/spontaneous/plugins/instance_code.rb +18 -0
  290. data/lib/spontaneous/plugins/layouts.rb +87 -0
  291. data/lib/spontaneous/plugins/media.rb +41 -0
  292. data/lib/spontaneous/plugins/page/formats.rb +67 -0
  293. data/lib/spontaneous/plugins/page/request.rb +89 -0
  294. data/lib/spontaneous/plugins/page_search.rb +64 -0
  295. data/lib/spontaneous/plugins/page_tree.rb +25 -0
  296. data/lib/spontaneous/plugins/paths.rb +125 -0
  297. data/lib/spontaneous/plugins/permissions.rb +63 -0
  298. data/lib/spontaneous/plugins/prototypes.rb +84 -0
  299. data/lib/spontaneous/plugins/publishing.rb +255 -0
  300. data/lib/spontaneous/plugins/render.rb +24 -0
  301. data/lib/spontaneous/plugins/schema_hierarchy.rb +76 -0
  302. data/lib/spontaneous/plugins/schema_id.rb +60 -0
  303. data/lib/spontaneous/plugins/schema_title.rb +33 -0
  304. data/lib/spontaneous/plugins/serialisation.rb +67 -0
  305. data/lib/spontaneous/plugins/site/instance.rb +22 -0
  306. data/lib/spontaneous/plugins/site/map.rb +19 -0
  307. data/lib/spontaneous/plugins/site/publishing.rb +74 -0
  308. data/lib/spontaneous/plugins/site/revisions.rb +28 -0
  309. data/lib/spontaneous/plugins/site/selectors.rb +41 -0
  310. data/lib/spontaneous/plugins/site_map.rb +34 -0
  311. data/lib/spontaneous/plugins/styles.rb +119 -0
  312. data/lib/spontaneous/plugins/supertype.rb +11 -0
  313. data/lib/spontaneous/plugins/visibility.rb +151 -0
  314. data/lib/spontaneous/plugins.rb +20 -0
  315. data/lib/spontaneous/prototypes/box_prototype.rb +168 -0
  316. data/lib/spontaneous/prototypes/field_prototype.rb +112 -0
  317. data/lib/spontaneous/prototypes/layout_prototype.rb +17 -0
  318. data/lib/spontaneous/prototypes/style_prototype.rb +42 -0
  319. data/lib/spontaneous/proxy_object.rb +12 -0
  320. data/lib/spontaneous/publishing/fire_and_forget.rb +57 -0
  321. data/lib/spontaneous/publishing/immediate.rb +197 -0
  322. data/lib/spontaneous/publishing/threaded.rb +25 -0
  323. data/lib/spontaneous/publishing.rb +10 -0
  324. data/lib/spontaneous/rack/around_back.rb +44 -0
  325. data/lib/spontaneous/rack/around_front.rb +29 -0
  326. data/lib/spontaneous/rack/around_preview.rb +26 -0
  327. data/lib/spontaneous/rack/assets.rb +98 -0
  328. data/lib/spontaneous/rack/back.rb +729 -0
  329. data/lib/spontaneous/rack/front.rb +41 -0
  330. data/lib/spontaneous/rack/http.rb +18 -0
  331. data/lib/spontaneous/rack/media.rb +29 -0
  332. data/lib/spontaneous/rack/public.rb +232 -0
  333. data/lib/spontaneous/rack/reloader.rb +42 -0
  334. data/lib/spontaneous/rack/static.rb +25 -0
  335. data/lib/spontaneous/rack.rb +55 -0
  336. data/lib/spontaneous/render/context.rb +100 -0
  337. data/lib/spontaneous/render/development_renderer.rb +14 -0
  338. data/lib/spontaneous/render/engine.rb +19 -0
  339. data/lib/spontaneous/render/format/html.rb +5 -0
  340. data/lib/spontaneous/render/format.rb +70 -0
  341. data/lib/spontaneous/render/preview_renderer.rb +18 -0
  342. data/lib/spontaneous/render/published_renderer.rb +54 -0
  343. data/lib/spontaneous/render/publishing_renderer.rb +13 -0
  344. data/lib/spontaneous/render/renderer.rb +46 -0
  345. data/lib/spontaneous/render.rb +173 -0
  346. data/lib/spontaneous/revision.rb +7 -0
  347. data/lib/spontaneous/schema/schema_modification.rb +260 -0
  348. data/lib/spontaneous/schema/uid.rb +221 -0
  349. data/lib/spontaneous/schema.rb +295 -0
  350. data/lib/spontaneous/server.rb +65 -0
  351. data/lib/spontaneous/site.rb +87 -0
  352. data/lib/spontaneous/state.rb +53 -0
  353. data/lib/spontaneous/style.rb +144 -0
  354. data/lib/spontaneous/tasks/database.rake +9 -0
  355. data/lib/spontaneous/tasks.rb +5 -0
  356. data/lib/spontaneous/version.rb +6 -0
  357. data/lib/spontaneous.rb +179 -0
  358. data/spontaneous.gemspec.tmpl +66 -0
  359. data/test/disabled/test_slots.rb +287 -0
  360. data/test/experimental/test_formats.rb +92 -0
  361. data/test/experimental/test_plugins.rb +64 -0
  362. data/test/fixtures/application/css/test.less +5 -0
  363. data/test/fixtures/application/js/test.js +1 -0
  364. data/test/fixtures/application/static/favicon.ico +1 -0
  365. data/test/fixtures/application/static/test.html +1 -0
  366. data/test/fixtures/application/views/index.erubis +1 -0
  367. data/test/fixtures/back/public/test.html +1 -0
  368. data/test/fixtures/back/templates/layouts/standard.html.cut +1 -0
  369. data/test/fixtures/config/config/environment.rb +4 -0
  370. data/test/fixtures/config/config/environments/development.rb +13 -0
  371. data/test/fixtures/config/config/environments/production.rb +22 -0
  372. data/test/fixtures/config/config/environments/staging.rb +2 -0
  373. data/test/fixtures/example_application/Gemfile +6 -0
  374. data/test/fixtures/example_application/Gemfile.lock +76 -0
  375. data/test/fixtures/example_application/Rakefile +6 -0
  376. data/test/fixtures/example_application/config/back.rb +15 -0
  377. data/test/fixtures/example_application/config/back.ru +8 -0
  378. data/test/fixtures/example_application/config/back.yml +8 -0
  379. data/test/fixtures/example_application/config/boot.rb +16 -0
  380. data/test/fixtures/example_application/config/database.yml +24 -0
  381. data/test/fixtures/example_application/config/environment.rb +4 -0
  382. data/test/fixtures/example_application/config/environments/development.rb +16 -0
  383. data/test/fixtures/example_application/config/environments/production.rb +21 -0
  384. data/test/fixtures/example_application/config/environments/staging.rb +1 -0
  385. data/test/fixtures/example_application/config/front.rb +8 -0
  386. data/test/fixtures/example_application/config/front.ru +8 -0
  387. data/test/fixtures/example_application/config/front.yml +8 -0
  388. data/test/fixtures/example_application/config/schema.yml +48 -0
  389. data/test/fixtures/example_application/config/unicorn.rb +1 -0
  390. data/test/fixtures/example_application/config/user_levels.yml +19 -0
  391. data/test/fixtures/example_application/public/css/test.css +0 -0
  392. data/test/fixtures/example_application/public/favicon.ico +1 -0
  393. data/test/fixtures/example_application/public/js/test.js +0 -0
  394. data/test/fixtures/example_application/public/test.html +1 -0
  395. data/test/fixtures/example_application/schema/client_project.rb +18 -0
  396. data/test/fixtures/example_application/schema/client_projects.rb +8 -0
  397. data/test/fixtures/example_application/schema/home_page.rb +22 -0
  398. data/test/fixtures/example_application/schema/info_page.rb +13 -0
  399. data/test/fixtures/example_application/schema/inline_image.rb +11 -0
  400. data/test/fixtures/example_application/schema/page.rb +4 -0
  401. data/test/fixtures/example_application/schema/piece.rb +3 -0
  402. data/test/fixtures/example_application/schema/project.rb +21 -0
  403. data/test/fixtures/example_application/schema/project_image.rb +18 -0
  404. data/test/fixtures/example_application/schema/projects_page.rb +12 -0
  405. data/test/fixtures/example_application/schema/text.rb +8 -0
  406. data/test/fixtures/example_application/templates/client_project/images.html.cut +1 -0
  407. data/test/fixtures/example_application/templates/client_project.html.cut +4 -0
  408. data/test/fixtures/example_application/templates/client_projects.html.cut +6 -0
  409. data/test/fixtures/example_application/templates/info_page/inline.html.cut +0 -0
  410. data/test/fixtures/example_application/templates/inline_image.html.cut +1 -0
  411. data/test/fixtures/example_application/templates/layouts/home.html.cut +15 -0
  412. data/test/fixtures/example_application/templates/layouts/info.html.cut +3 -0
  413. data/test/fixtures/example_application/templates/layouts/project.html.cut +13 -0
  414. data/test/fixtures/example_application/templates/layouts/projects.html.cut +11 -0
  415. data/test/fixtures/example_application/templates/layouts/standard.html.cut +0 -0
  416. data/test/fixtures/example_application/templates/project/inline.html.cut +5 -0
  417. data/test/fixtures/example_application/templates/project.html.cut +5 -0
  418. data/test/fixtures/example_application/templates/project_image.html.cut +1 -0
  419. data/test/fixtures/example_application/templates/text.html.cut +1 -0
  420. data/test/fixtures/images/rose.greyscale.jpg +0 -0
  421. data/test/fixtures/images/rose.jpg +0 -0
  422. data/test/fixtures/images/size.gif +0 -0
  423. data/test/fixtures/images/size.jpg +0 -0
  424. data/test/fixtures/images/size.png24 +0 -0
  425. data/test/fixtures/images/size.png8 +0 -0
  426. data/test/fixtures/layouts/layouts/custom1.html.cut +1 -0
  427. data/test/fixtures/layouts/layouts/custom1.pdf.cut +0 -0
  428. data/test/fixtures/layouts/layouts/custom1.xml.cut +0 -0
  429. data/test/fixtures/layouts/layouts/custom2.html.cut +1 -0
  430. data/test/fixtures/layouts/layouts/custom3.html.cut +0 -0
  431. data/test/fixtures/layouts/layouts/standard.html.cut +1 -0
  432. data/test/fixtures/media/101/003/rose.jpg +0 -0
  433. data/test/fixtures/permissions/config/user_levels.yml +9 -0
  434. data/test/fixtures/permissions/media/image.jpg +0 -0
  435. data/test/fixtures/plugins/schema_plugin/init.rb +1 -0
  436. data/test/fixtures/plugins/schema_plugin/schema/external.rb +5 -0
  437. data/test/fixtures/plugins/schema_plugin/templates/external.html.cut +1 -0
  438. data/test/fixtures/plugins/schema_plugin/templates/from_plugin.html.cut +0 -0
  439. data/test/fixtures/plugins/schema_plugin/templates/layouts/from_plugin.html.cut +0 -0
  440. data/test/fixtures/public/templates/layouts/default.html.cut +1 -0
  441. data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -0
  442. data/test/fixtures/public/templates/layouts/default.rss.cut +1 -0
  443. data/test/fixtures/public/templates/layouts/dynamic.html.cut +1 -0
  444. data/test/fixtures/public/templates/layouts/standard.html.cut +0 -0
  445. data/test/fixtures/schema/before.yml +24 -0
  446. data/test/fixtures/schema/resolvable.yml +12 -0
  447. data/test/fixtures/schema/schema.yml +7 -0
  448. data/test/fixtures/serialisation/class_hash.yaml.erb +53 -0
  449. data/test/fixtures/serialisation/root_hash.yaml.erb +184 -0
  450. data/test/fixtures/sharding/rose.jpg +0 -0
  451. data/test/fixtures/sharding/xaa +0 -0
  452. data/test/fixtures/sharding/xab +0 -0
  453. data/test/fixtures/sharding/xac +0 -0
  454. data/test/fixtures/sharding/xad +0 -0
  455. data/test/fixtures/sharding/xae +0 -0
  456. data/test/fixtures/sharding/xaf +0 -0
  457. data/test/fixtures/sharding/xag +0 -0
  458. data/test/fixtures/styles/box_a/runny.html.cut +0 -0
  459. data/test/fixtures/styles/box_a.html.cut +1 -0
  460. data/test/fixtures/styles/named2.html.cut +1 -0
  461. data/test/fixtures/styles/orange/apple.html.cut +1 -0
  462. data/test/fixtures/styles/template_class/named1.html.cut +1 -0
  463. data/test/fixtures/styles/template_class/results.html.cut +1 -0
  464. data/test/fixtures/styles/template_class/walky.html.cut +0 -0
  465. data/test/fixtures/styles/template_class.epub.cut +0 -0
  466. data/test/fixtures/styles/template_class.html.cut +1 -0
  467. data/test/fixtures/styles/template_class.pdf.cut +0 -0
  468. data/test/fixtures/styles/template_sub_class1.html.cut +1 -0
  469. data/test/fixtures/templates/aliases/a/a_style.html.cut +0 -0
  470. data/test/fixtures/templates/aliases/a/page.html.cut +0 -0
  471. data/test/fixtures/templates/aliases/a_alias/a_alias_style.html.cut +0 -0
  472. data/test/fixtures/templates/aliases/layouts/b.html.cut +1 -0
  473. data/test/fixtures/templates/aliases/layouts/b_alias.html.cut +1 -0
  474. data/test/fixtures/templates/aliases/layouts/c_alias.html.cut +1 -0
  475. data/test/fixtures/templates/boxes/blank_content/things.html.cut +1 -0
  476. data/test/fixtures/templates/boxes/my_box_class/christy.html.cut +1 -0
  477. data/test/fixtures/templates/boxes/thangs.html.cut +1 -0
  478. data/test/fixtures/templates/boxes/with_template_box.html.cut +1 -0
  479. data/test/fixtures/templates/content/include.html.cut +1 -0
  480. data/test/fixtures/templates/content/include_dir.html.cut +1 -0
  481. data/test/fixtures/templates/content/included.epub.cut +1 -0
  482. data/test/fixtures/templates/content/included.html.cut +1 -0
  483. data/test/fixtures/templates/content/partial/included.html.cut +1 -0
  484. data/test/fixtures/templates/content/preprocess.html.cut +1 -0
  485. data/test/fixtures/templates/content/second.html.cut +1 -0
  486. data/test/fixtures/templates/content/template.epub.cut +1 -0
  487. data/test/fixtures/templates/content/template.html.cut +1 -0
  488. data/test/fixtures/templates/default_style_class.html.cut +1 -0
  489. data/test/fixtures/templates/direct.html.cut +1 -0
  490. data/test/fixtures/templates/extended/grandparent.html.cut +10 -0
  491. data/test/fixtures/templates/extended/main.html.cut +6 -0
  492. data/test/fixtures/templates/extended/parent.html.cut +10 -0
  493. data/test/fixtures/templates/layouts/entries.html.cut +7 -0
  494. data/test/fixtures/templates/layouts/page_style.html.cut +1 -0
  495. data/test/fixtures/templates/layouts/params.html.cut +1 -0
  496. data/test/fixtures/templates/layouts/preview_render.html.cut +2 -0
  497. data/test/fixtures/templates/layouts/standard_page.html.cut +1 -0
  498. data/test/fixtures/templates/layouts/subdir_style.html.cut +1 -0
  499. data/test/fixtures/templates/layouts/template_params.html.cut +1 -0
  500. data/test/fixtures/templates/page_class/inline_style.html.cut +1 -0
  501. data/test/fixtures/templates/preview_render/inline.html.cut +0 -0
  502. data/test/fixtures/templates/publishing/layouts/dynamic.html.cut +1 -0
  503. data/test/fixtures/templates/publishing/layouts/static.html.cut +1 -0
  504. data/test/fixtures/templates/template_class/anonymous_style.html.cut +4 -0
  505. data/test/fixtures/templates/template_class/another_template.html.cut +0 -0
  506. data/test/fixtures/templates/template_class/complex_template.html.cut +6 -0
  507. data/test/fixtures/templates/template_class/complex_template.pdf.cut +6 -0
  508. data/test/fixtures/templates/template_class/default_template_style.html.cut +4 -0
  509. data/test/fixtures/templates/template_class/images_with_template.html.cut +5 -0
  510. data/test/fixtures/templates/template_class/slots_template.html.cut +5 -0
  511. data/test/fixtures/templates/template_class/slots_template.pdf.cut +5 -0
  512. data/test/fixtures/templates/template_class/this_template.epub.cut +1 -0
  513. data/test/fixtures/templates/template_class/this_template.html.cut +1 -0
  514. data/test/fixtures/templates/template_class/this_template.pdf.cut +1 -0
  515. data/test/fixtures/templates/with_default_style_class.html.cut +1 -0
  516. data/test/functional/test_application.rb +176 -0
  517. data/test/functional/test_back.rb +902 -0
  518. data/test/functional/test_front.rb +571 -0
  519. data/test/javascript/test_dom.rb +94 -0
  520. data/test/javascript/test_markdown.rb +97 -0
  521. data/test/slow/test_publishing.rb +987 -0
  522. data/test/slow/test_visibility.rb +250 -0
  523. data/test/support/custom_matchers.rb +77 -0
  524. data/test/support/timing.rb +23 -0
  525. data/test/test_helper.rb +164 -0
  526. data/test/test_javascript.rb +34 -0
  527. data/test/ui/test_page_editing.rb +167 -0
  528. data/test/ui_helper.rb +114 -0
  529. data/test/unit/test_alias.rb +254 -0
  530. data/test/unit/test_authentication.rb +510 -0
  531. data/test/unit/test_boxes.rb +497 -0
  532. data/test/unit/test_config.rb +156 -0
  533. data/test/unit/test_content.rb +221 -0
  534. data/test/unit/test_content_inheritance.rb +103 -0
  535. data/test/unit/test_extensions.rb +14 -0
  536. data/test/unit/test_fields.rb +392 -0
  537. data/test/unit/test_generators.rb +97 -0
  538. data/test/unit/test_image_size.rb +25 -0
  539. data/test/unit/test_images.rb +265 -0
  540. data/test/unit/test_layouts.rb +111 -0
  541. data/test/unit/test_logger.rb +80 -0
  542. data/test/unit/test_media.rb +70 -0
  543. data/test/unit/test_page.rb +244 -0
  544. data/test/unit/test_permissions.rb +834 -0
  545. data/test/unit/test_piece.rb +80 -0
  546. data/test/unit/test_prototype_set.rb +192 -0
  547. data/test/unit/test_prototypes.rb +102 -0
  548. data/test/unit/test_render.rb +359 -0
  549. data/test/unit/test_schema.rb +1009 -0
  550. data/test/unit/test_serialisation.rb +215 -0
  551. data/test/unit/test_site.rb +145 -0
  552. data/test/unit/test_structure.rb +85 -0
  553. data/test/unit/test_styles.rb +417 -0
  554. data/test/unit/test_templates.rb +224 -0
  555. data/test/unit/test_type_hierarchy.rb +28 -0
  556. metadata +1017 -0
@@ -0,0 +1,987 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+
6
+ class PublishingTest < MiniTest::Spec
7
+
8
+ def self.startup
9
+ root = File.expand_path("../../fixtures/example_application", __FILE__)
10
+ Spontaneous.root = root
11
+ instance = Spontaneous::Site.instantiate(root, :test, :back)
12
+ Site.config.publishing_delay = nil
13
+ Site.instance.database = DB
14
+ Content.delete_all_revisions!
15
+ end
16
+
17
+ def self.shutdown
18
+ # Spontaneous.database = DB
19
+ Content.delete_all_revisions!
20
+ end
21
+
22
+ @@now = Time.now
23
+
24
+ def setup
25
+ Site.publishing_method = :immediate
26
+ end
27
+
28
+ def assert_content_equal(result, compare)
29
+ serialised_columns = [:field_store, :entry_store]
30
+ columns = Content.columns - serialised_columns
31
+ columns.each do |col|
32
+ # result[col].should == compare[col]
33
+ assert_equal(result[col], compare[col], "Column '#{col}' should be equal")
34
+ end
35
+ serialised_columns.each do |col|
36
+ result.send(col).should == compare.send(col)
37
+ end
38
+ end
39
+
40
+ context "publishing" do
41
+
42
+ setup do
43
+ Spot::Schema.reset!
44
+
45
+ # DB.logger = Logger.new($stdout)
46
+ Content.delete
47
+
48
+ class Page < Spontaneous::Page
49
+ field :title, :string, :default => "New Page"
50
+ end
51
+
52
+ 2.times do |i|
53
+ c = Page.new(:uid => i)
54
+ 2.times do |j|
55
+ d = Content.new(:uid => "#{i}.#{j}")
56
+ c << d
57
+ 2.times do |k|
58
+ d << Page.new(:uid => "#{i}.#{j}.#{k}")
59
+ d.save
60
+ end
61
+ end
62
+ c.save
63
+ end
64
+ end
65
+
66
+ teardown do
67
+ # Content.delete_all_revisions!
68
+ PublishingTest.send(:remove_const, :Page) rescue nil
69
+ Content.delete
70
+ DB.logger = nil
71
+ end
72
+
73
+ context "data sources" do
74
+
75
+ should "have the right names" do
76
+ Content.revision_table(23).should == '__r00023_content'
77
+ Content.revision_table(nil).should == 'content'
78
+ end
79
+
80
+ should "be recognisable" do
81
+ Content.revision_table?('content').should be_false
82
+ Content.revision_table?('__r00023_content').should be_true
83
+ Content.revision_table?('__r00023_not').should be_false
84
+ Content.revision_table?('subscribers').should be_false
85
+ end
86
+
87
+ should "be switchable within blocks" do
88
+ Content.dataset.should be_content_revision
89
+ Content.with_revision(23) do
90
+ Content.revision.should ==23
91
+ Content.dataset.should be_content_revision(23)
92
+ end
93
+ Content.dataset.should be_content_revision
94
+ Content.revision.should be_nil
95
+ end
96
+
97
+ should "know which revision is active" do
98
+ Content.with_revision(23) do
99
+ Content.revision.should == 23
100
+ end
101
+ end
102
+
103
+ should "be switchable without blocks" do
104
+ Content.with_revision(23)
105
+ Content.dataset.should be_content_revision(23)
106
+ Content.reset_revision
107
+ Content.dataset.should be_content_revision
108
+ end
109
+
110
+ should "understand the with_editable" do
111
+ Content.with_revision(23) do
112
+ Content.dataset.should be_content_revision(23)
113
+ Content.with_editable do
114
+ Content.dataset.should be_content_revision
115
+ end
116
+ Content.dataset.should be_content_revision(23)
117
+ end
118
+ Content.dataset.should be_content_revision
119
+ end
120
+
121
+ should "understand with_published" do
122
+ Site.stubs(:published_revision).returns(99)
123
+ Content.with_published do
124
+ Content.dataset.should be_content_revision(99)
125
+ Content.with_editable do
126
+ Content.dataset.should be_content_revision
127
+ end
128
+ Content.dataset.should be_content_revision(99)
129
+ end
130
+ Content.dataset.should be_content_revision
131
+ end
132
+
133
+ should "be stackable" do
134
+ Content.dataset.should be_content_revision
135
+ Content.with_revision(23) do
136
+ Content.dataset.should be_content_revision(23)
137
+ Content.with_revision(24) do
138
+ Content.dataset.should be_content_revision(24)
139
+ end
140
+ Content.dataset.should be_content_revision(23)
141
+ end
142
+ Content.dataset.should be_content_revision
143
+ end
144
+
145
+ should "reset datasource after an exception" do
146
+ Content.dataset.should be_content_revision
147
+ begin
148
+ Content.with_revision(23) do
149
+ Content.dataset.should be_content_revision(23)
150
+ raise Exception.new
151
+ end
152
+ rescue Exception
153
+ end
154
+ Content.dataset.should be_content_revision
155
+ end
156
+
157
+ should "read revision from the environment if present" do
158
+ ENV["SPOT_REVISION"] = '1001'
159
+ Content.with_published do
160
+ Content.dataset.should be_content_revision(1001)
161
+ end
162
+ ENV.delete("SPOT_REVISION")
163
+ end
164
+
165
+ context "subclasses" do
166
+ setup do
167
+ class ::Subclass < Page; end
168
+ end
169
+
170
+ teardown do
171
+ Object.send(:remove_const, :Subclass)
172
+ end
173
+
174
+ should "set all subclasses to use the same dataset" do
175
+ Content.with_revision(23) do
176
+ Subclass.revision.should ==23
177
+ Subclass.dataset.should be_content_revision(23, Subclass.schema_id)
178
+ # piece wasn't loaded until this point
179
+ Piece.dataset.should be_content_revision(23)
180
+ Piece.revision.should == 23
181
+ end
182
+ Subclass.dataset.should be_content_revision(nil, Subclass.schema_id)
183
+ Piece.dataset.should be_content_revision(nil)
184
+ end
185
+ end
186
+ end
187
+
188
+ context "content revisions" do
189
+ setup do
190
+ @revision = 1
191
+ # @now = Sequel.datetime_class.now
192
+ Sequel.datetime_class.stubs(:now).returns(@@now)
193
+ end
194
+ teardown do
195
+ Content.delete_revision(@revision)
196
+ Content.delete_revision(@revision+1)
197
+ end
198
+ should "be testable for existance" do
199
+ Content.revision_exists?(@revision).should be_false
200
+ Content.create_revision(@revision)
201
+ Content.revision_exists?(@revision).should be_true
202
+ end
203
+ should "be deletable en masse" do
204
+ tables = (1..10).map { |i| Content.revision_table(i).to_sym }
205
+ tables.each do |t|
206
+ DB.create_table(t){Integer :id}
207
+ end
208
+ tables.each do |t|
209
+ DB.tables.include?(t).should be_true
210
+ end
211
+ Content.delete_all_revisions!
212
+ tables.each do |t|
213
+ DB.tables.include?(t).should be_false
214
+ end
215
+ end
216
+
217
+ should "be creatable from current content" do
218
+ DB.tables.include?(Content.revision_table(@revision).to_sym).should be_false
219
+ Content.create_revision(@revision)
220
+ DB.tables.include?(Content.revision_table(@revision).to_sym).should be_true
221
+ count = Content.count
222
+ Content.with_revision(@revision) do
223
+ Content.count.should == count
224
+ Content.all.each do |published|
225
+ Content.with_editable do
226
+ e = Content[published.id]
227
+ e.should == published
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ should "be creatable from any revision" do
234
+ revision = 2
235
+ source_revision = @revision
236
+ source_revision_count = nil
237
+
238
+ Content.create_revision(source_revision)
239
+
240
+ Content.with_revision(source_revision) do
241
+ Content.filter(:depth => 0).limit(1).each do |c|
242
+ c.destroy
243
+ end
244
+ source_revision_count = Content.count
245
+ end
246
+
247
+ Content.count.should == source_revision_count + 7
248
+
249
+ Content.create_revision(revision, source_revision)
250
+
251
+ Content.with_revision(revision) do
252
+ Content.count.should == source_revision_count
253
+ Content.all.each do |published|
254
+ Content.with_revision(source_revision) do
255
+ e = Content[published.id]
256
+ e.should == published
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ should "have the correct indexes" do
263
+ Content.create_revision(@revision)
264
+ content_indexes = DB.indexes(:content)
265
+ published_indexes = DB.indexes(Content.revision_table(@revision))
266
+ # made slightly complex by the fact that the index names depend on the table names
267
+ # (which are different)
268
+ assert_same_elements published_indexes.values, content_indexes.values
269
+ end
270
+
271
+
272
+ context "incremental publishing" do
273
+ setup do
274
+ @initial_revision = 1
275
+ @final_revision = 2
276
+ Content.create_revision(@initial_revision)
277
+ # DB.logger = Logger.new($stdout)
278
+ end
279
+
280
+ teardown do
281
+ begin
282
+ Content.delete_revision(@initial_revision)
283
+ Content.delete_revision(@final_revision)
284
+ Content.delete_revision(@final_revision+1)
285
+ rescue
286
+ end
287
+ DB.logger = nil
288
+ end
289
+
290
+ should "duplicate changes to only a single item" do
291
+ editable1 = Content.first(:uid => '1.0')
292
+ editable1.label.should be_nil
293
+ editable1.label = "published"
294
+ editable1.save
295
+ # editable1.reload
296
+ editable2 = Content.first(:uid => '1.1')
297
+ editable2.label = "unpublished"
298
+ editable2.save
299
+ # editable2.reload
300
+ Content.publish(@final_revision, [editable1.id])
301
+ editable1.reload
302
+ Content.with_revision(@final_revision) do
303
+ published = Content[editable1.id]
304
+ unpublished = Content[editable2.id]
305
+ assert_content_equal(published, editable1)
306
+
307
+ # published.should == editable1
308
+ unpublished.should_not == editable2
309
+ end
310
+ end
311
+
312
+ should "publish additions to contents of a page" do
313
+ editable1 = Content.first(:uid => '0')
314
+ new_content = Content.new(:uid => "new")
315
+
316
+ editable1 << new_content
317
+ editable1.save
318
+ Content.publish(@final_revision, [editable1.id])
319
+ new_content.reload
320
+ editable1.reload
321
+ Content.with_revision(@final_revision) do
322
+ published1 = Content[editable1.id]
323
+ published2 = Content[new_content.id]
324
+ # published2.should == new_content
325
+ assert_content_equal(published2, new_content)
326
+ # published1.should == editable1
327
+ assert_content_equal(published1, editable1)
328
+ end
329
+ end
330
+
331
+ should "publish deletions to contents of page" do
332
+ editable1 = Content.first(:uid => '0')
333
+ deleted = editable1.pieces.first
334
+ editable1.pieces.first.destroy
335
+ Content.publish(@final_revision, [editable1.id])
336
+ editable1.reload
337
+ Content.with_revision(@final_revision) do
338
+ published1 = Content[editable1.id]
339
+ assert_content_equal(published1, editable1)
340
+ # published1.should == editable1
341
+ Content[deleted.id].should be_nil
342
+ end
343
+ end
344
+
345
+ should "publish page additions" do
346
+ editable1 = Content.first(:uid => '0')
347
+ new_page = Page.new(:uid => "new")
348
+ slot = editable1.pieces.first
349
+ slot << new_page
350
+ editable1.save
351
+ slot.save
352
+ new_page.save
353
+ Content.publish(@final_revision, [editable1.id])
354
+ new_page.reload
355
+ editable1.reload
356
+ slot.reload
357
+ Content.with_revision(@final_revision) do
358
+ published1 = Content[editable1.id]
359
+ published2 = Content[new_page.id]
360
+ published3 = Content[slot.id]
361
+ # published1.should == editable1
362
+ assert_content_equal(published1, editable1)
363
+ # published2.should == new_page
364
+ assert_content_equal(published2, new_page)
365
+ # published3.should == slot
366
+ assert_content_equal(published3, slot)
367
+ end
368
+ end
369
+
370
+ should "not publish changes to existing pages unless explicitly asked" do
371
+ editable1 = Content.first(:uid => '0')
372
+ editable1 << Content.new(:uid => "added")
373
+ editable1.save
374
+ editable2 = Content.first(:uid => '0.0.0')
375
+ new_content = Content.new(:uid => "new")
376
+ editable2 << new_content
377
+ editable2.save
378
+ Content.publish(@final_revision, [editable1.id])
379
+ editable1.reload
380
+ editable2.reload
381
+ new_content.reload
382
+ Content.with_revision(@final_revision) do
383
+ published1 = Content[editable1.id]
384
+ Content.first(:uid => "added").should_not be_nil
385
+ published3 = Content[editable2.id]
386
+ assert_content_equal(published1, editable1)
387
+ # published1.should == editable1
388
+ published3.should_not == editable2
389
+ published3.uid.should_not == "new"
390
+ end
391
+ Content.publish(@final_revision+1, [editable2.id])
392
+ editable1.reload
393
+ editable2.reload
394
+ new_content.reload
395
+ Content.with_revision(@final_revision+1) do
396
+ published1 = Content[editable1.id]
397
+ # published1.should == editable1
398
+ assert_content_equal(published1, editable1)
399
+ assert_content_equal(published1, editable1)
400
+ published3 = Content[editable2.id]
401
+ # published3.should == editable2
402
+ assert_content_equal(published3, editable2)
403
+ published4 = Content[editable2.pieces.first.id]
404
+ # published4.should == editable2.pieces.first
405
+ assert_content_equal(published4, editable2.pieces.first)
406
+ end
407
+ end
408
+ end
409
+
410
+
411
+ end
412
+
413
+ context "modification timestamps" do
414
+ setup do
415
+ Sequel.datetime_class.stubs(:now).returns(@@now)
416
+ end
417
+ should "register creation date of all content" do
418
+ c = Content.create
419
+ c.created_at.should == @@now
420
+ p = Page.create
421
+ p.created_at.should == @@now
422
+ end
423
+
424
+ should "register modification date of all content" do
425
+ now = @@now + 100
426
+ Time.stubs(:now).returns(now)
427
+ c = Content.first
428
+ (c.modified_at.to_i - @@now.to_i).abs.should <= 1
429
+ c.label = "changed"
430
+ c.save
431
+ (c.modified_at - now).abs.should <= 1
432
+ end
433
+
434
+ should "update page timestamps on modification of a piece" do
435
+ Time.stubs(:now).returns(@@now+3600)
436
+ page = Page.first
437
+ (page.modified_at.to_i - @@now.to_i).abs.should <= 1
438
+ content = page.pieces.first
439
+ content.page.should == page
440
+ content.label = "changed"
441
+ content.save
442
+ page.reload
443
+ page.modified_at.to_i.should == @@now.to_i + 3600
444
+ end
445
+
446
+ should "update page timestamp on addition of piece" do
447
+ Sequel.datetime_class.stubs(:now).returns(@@now+3600)
448
+ page = Page.first
449
+ content = Content[page.pieces.first.id]
450
+ content << Content.new
451
+ content.save
452
+ content.modified_at.to_i.should == @@now.to_i + 3600
453
+ page.reload
454
+ page.modified_at.to_i.should == @@now.to_i + 3600
455
+ end
456
+ end
457
+
458
+ context "change sets" do
459
+ setup do
460
+ Change.delete
461
+ @@now = Sequel.datetime_class.now
462
+ Sequel.datetime_class.stubs(:now).returns(@@now)
463
+ Time.stubs(:now).returns(@@now)
464
+ end
465
+
466
+ should "have a testable state" do
467
+ Change.recording?.should be_false
468
+ Change.record do
469
+ Change.recording?.should be_true
470
+ end
471
+ Change.recording?.should be_false
472
+ end
473
+
474
+ should "be created on updating a page's attributes" do
475
+ page = Page.first
476
+ Change.record do
477
+ page.label = "changed"
478
+ page.save
479
+ end
480
+ page.reload
481
+ Change.count.should == 1
482
+ change = Change.first
483
+ change.modified_list.must_be_instance_of(Array)
484
+ change.modified_list.length.should == 1
485
+ change.modified_list.should == [page.id]
486
+ change.modified.should == [page]
487
+ change.created_at.to_i.should == @@now.to_i
488
+ end
489
+
490
+ should "be created on updating a page's content" do
491
+ page = Page.first
492
+ content = Content[page.pieces.first.id]
493
+ Change.record do
494
+ content.label = "changed"
495
+ content.save
496
+ end
497
+ page.reload
498
+ Change.count.should == 1
499
+ change = Change.first
500
+ change.modified_list.must_be_instance_of(Array)
501
+ change.modified_list.length.should == 1
502
+ change.modified_list.should == [page.id]
503
+ change.modified.should == [page]
504
+ end
505
+
506
+ should "include newly created pages" do
507
+ page = Page.first
508
+ content = Content[page.pieces.first.id]
509
+ new_page = nil
510
+ Change.record do
511
+ new_page = Page.new
512
+ page << new_page
513
+ page.save
514
+ end
515
+ new_page.reload
516
+ page = Content[page.id]
517
+ new_page = Content[new_page.id]
518
+ Change.count.should == 1
519
+ change = Change.first
520
+ change.modified_list.must_be_instance_of(Array)
521
+ change.modified_list.length.should == 2
522
+ change.modified_list.should == [page.id, new_page.id]
523
+ change.modified.should == [page, new_page]
524
+ end
525
+
526
+
527
+ should "handle being called twice" do
528
+ page = Page.first
529
+ p2 = Page.filter(~{:id => page.id}).first
530
+ Change.record do
531
+ page.label = "changed"
532
+ page.save
533
+ Change.record do
534
+ p2.label = "another change"
535
+ p2.save
536
+ end
537
+ end
538
+ page.reload
539
+ p2.reload
540
+ Change.count.should == 1
541
+ change = Change.first
542
+ change.modified_list.must_be_instance_of(Array)
543
+ change.modified_list.length.should == 2
544
+ change.modified_list.should == [page.id, p2.id]
545
+ change.modified.should == [page, p2]
546
+ end
547
+
548
+ should "not create a change if nothing actually happens" do
549
+ Change.record do
550
+ end
551
+ Change.count.should == 0
552
+ end
553
+
554
+ should "reset state despite exceptions" do
555
+ page = Page.first
556
+ begin
557
+ Change.record do
558
+ page.label = "caught"
559
+ page.save
560
+ Change.recording?.should be_true
561
+ raise Exception.new
562
+ end
563
+ rescue Exception
564
+ end
565
+ Change.recording?.should be_false
566
+ Change.count.should == 1
567
+ end
568
+
569
+
570
+
571
+ should "correctly calculate dependencies" do
572
+ Change.delete
573
+ changes = [
574
+ [201, 202],
575
+ [104, 105, 106],
576
+ [100, 101],
577
+ [100],
578
+ [101],
579
+ [100],
580
+ [101, 102],
581
+ [102],
582
+ [101],
583
+ [102, 103],
584
+ [103, 104],
585
+ [200],
586
+ [200, 201],
587
+ [201]
588
+ ]
589
+ publish_sets = [
590
+ [1, 13, 14, 12],
591
+ [2, 11, 8, 10, 3, 4, 5, 6, 7, 9]
592
+ ]
593
+ changes.each_with_index do |ids, i|
594
+ Change.insert(:id => (i+1), :modified_list => ids.to_json)
595
+ ids.each { |id| Content.insert(:id => id) rescue nil }
596
+ end
597
+ result = Change.outstanding
598
+ result.must_be_instance_of(Array)
599
+ result.length.should == 2
600
+ page_ids = [
601
+ [200, 201, 202],
602
+ [100, 101, 102, 103, 104, 105, 106]
603
+ ]
604
+ result.each_with_index do |set, i|
605
+ changes = publish_sets[i].map { |id| Change[id] }
606
+ pages = page_ids[i].map { |id| Content[id] }
607
+ set.must_be_instance_of(Spontaneous::Collections::ChangeSet)
608
+ set.changes.should == changes
609
+ set.pages.should == pages
610
+ end
611
+ end
612
+
613
+ should "serialize changes to json" do
614
+ Change.delete
615
+ @page1 = Page.new(:title => "Page \"1\"", :path => "/page-1")
616
+ @page2 = Page.new(:title => "Page 2", :path => "/page-2")
617
+ @page1[:id] = 1
618
+ @page2[:id] = 2
619
+ @page1.stubs(:path).returns("/page-1")
620
+ @page2.stubs(:path).returns("/page-2")
621
+ Content.stubs(:[]).with(1).returns(@page1)
622
+ Content.stubs(:[]).with(2).returns(@page2)
623
+ change = Change.new
624
+ change.push(@page1)
625
+ change.push(@page2)
626
+ change.save
627
+ result = Change.outstanding
628
+ result.first.export.should == {
629
+ :changes => [{:id => change.id, :created_at => change.created_at.to_s, :page_ids => [1, 2]}],
630
+ :pages => [
631
+ {:id => 1, :title => "Page \"1\"", :path => "/page-1", :depth => 0},
632
+ {:id => 2, :title => "Page 2", :path => "/page-2", :depth => 0}
633
+ ]
634
+ }
635
+ end
636
+
637
+ should "silently work around deleted content" do
638
+ Change.delete
639
+ @page1 = Page.create(:title => "Page \"1\"", :path => "/page-1")
640
+ @page2 = Page.create(:title => "Page 2", :path => "/page-2")
641
+ @page3 = Page.create(:title => "Page 3", :path => "/page-3")
642
+ change = Change.new
643
+ change.push(@page1)
644
+ change.push(@page2)
645
+ change.push(@page3)
646
+ change.save
647
+ @page2.destroy
648
+ result = Change.outstanding
649
+ result.first.export.must_be_instance_of(Hash)
650
+ end
651
+ end
652
+
653
+ context "publication timestamps" do
654
+ setup do
655
+ @revision = 1
656
+ @@now = Sequel.datetime_class.now
657
+ Sequel.datetime_class.stubs(:now).returns(@@now)
658
+ end
659
+ teardown do
660
+ Content.delete_revision(@revision)
661
+ Content.delete_revision(@revision+1)
662
+ end
663
+
664
+ should "set correct timestamps on first publish" do
665
+ Content.first.first_published_at.should be_nil
666
+ Content.first.last_published_at.should be_nil
667
+ Content.publish(@revision)
668
+ Content.first.first_published_at.to_i.should == @@now.to_i
669
+ Content.first.last_published_at.to_i.should == @@now.to_i
670
+ Content.first.first_published_revision.should == @revision
671
+ Content.with_editable do
672
+ Content.first.first_published_at.to_i.should == @@now.to_i
673
+ Content.first.last_published_at.to_i.should == @@now.to_i
674
+ Content.first.first_published_revision.should == @revision
675
+ end
676
+ Content.with_revision(@revision) do
677
+ Content.first.first_published_at.to_i.should == @@now.to_i
678
+ Content.first.last_published_at.to_i.should == @@now.to_i
679
+ Content.first.first_published_revision.should == @revision
680
+ end
681
+ end
682
+
683
+ should "set correct timestamps on later publishes" do
684
+ Content.first.first_published_at.should be_nil
685
+ Content.publish(@revision)
686
+ Content.first.first_published_at.to_i.should == @@now.to_i
687
+ c = Content.create
688
+ c.first_published_at.should be_nil
689
+ Sequel.datetime_class.stubs(:now).returns(@@now+100)
690
+ Content.publish(@revision+1)
691
+ Content.first.first_published_at.to_i.should == @@now.to_i
692
+ Content.first.last_published_at.to_i.should == @@now.to_i + 100
693
+ Content.with_editable do
694
+ c = Content[c.id]
695
+ c.first_published_at.to_i.should == @@now.to_i + 100
696
+ end
697
+ Content.with_revision(@revision+1) do
698
+ c = Content[c.id]
699
+ c.first_published_at.to_i.should == @@now.to_i + 100
700
+ end
701
+ end
702
+
703
+ should "not set publishing date for items not published" do
704
+ Content.publish(@revision)
705
+ page = Content.first
706
+ page.uid = "fish"
707
+ page.save
708
+ added = Content.create
709
+ added.first_published_at.should be_nil
710
+ Content.publish(@revision+1, [page])
711
+ page.first_published_at.to_i.should == @@now.to_i
712
+ added.first_published_at.should be_nil
713
+ added.last_published_at.should be_nil
714
+ end
715
+
716
+ should "not set publishing dates if exception raised in passed block" do
717
+ Content.first.first_published_at.should be_nil
718
+ begin
719
+ Content.publish(@revision) do
720
+ raise Exception
721
+ end
722
+ rescue Exception; end
723
+ Content.first.first_published_at.should be_nil
724
+ end
725
+
726
+ should "delete revision tables if exception raised in passed block" do
727
+ Content.revision_exists?(@revision).should be_false
728
+ begin
729
+ Content.publish(@revision) do
730
+ Content.revision_exists?(@revision).should be_true
731
+ Content.revision.should == @revision
732
+ raise Exception
733
+ end
734
+ rescue Exception; end
735
+ Content.revision_exists?(@revision).should be_false
736
+ end
737
+
738
+ should "always publish all if no previous revisions exist" do
739
+ page = Content.first
740
+ Content.filter(:first_published_at => nil).count.should == Content.count
741
+ Content.publish(@revision, [page])
742
+ Content.filter(:first_published_at => nil).count.should == 0
743
+ end
744
+ end
745
+ context "site publishing" do
746
+ setup do
747
+ Content.delete
748
+ @revision = 3
749
+ @@now = Time.at(Time.now.to_i)
750
+ Time.stubs(:now).returns(@@now)
751
+ State.delete
752
+ Change.delete
753
+ State.create(:revision => @revision, :published_revision => 2)
754
+ State.revision.should == @revision
755
+ end
756
+
757
+ teardown do
758
+ Content.delete_revision(@revision)
759
+ Revision.delete
760
+ Change.delete
761
+ end
762
+
763
+ should "work with lists of change sets" do
764
+ change1 = Change.new
765
+ change1.modified_list = [1, 2, 3]
766
+ change1.save
767
+ change2 = Change.new
768
+ change2.modified_list = [3, 4, 5]
769
+ change2.save
770
+ change3 = Change.new
771
+ change3.modified_list = [8, 9]
772
+ change3.save
773
+ Content.expects(:publish).with(@revision, [1, 2, 3, 4, 5])
774
+ Site.publish_changes([change1.id, change2.id])
775
+ end
776
+
777
+ should "issue a publish_all if passed list including all change sets" do
778
+ change1 = Change.new
779
+ change1.modified_list = [1, 2, 3]
780
+ change1.save
781
+ change2 = Change.new
782
+ change2.modified_list = [3, 4, 5]
783
+ change2.save
784
+ change3 = Change.new
785
+ change3.modified_list = [8, 9]
786
+ change3.save
787
+ Content.expects(:publish).with(@revision, nil)
788
+ Site.publish_changes([change1.id, change2.id, change3.id])
789
+ end
790
+
791
+ should "publish all" do
792
+ Content.expects(:publish).with(@revision, nil)
793
+ Site.publish_all
794
+ end
795
+
796
+ ## publishing individual pages without dependencies is a bad idea
797
+ ## if this were to be an option we would have to do it properly
798
+ ## and calculate the full revision set involved
799
+ # should "publish individual pages" do
800
+ # page = Page.new
801
+ # Content.expects(:publish).with(@revision, [page])
802
+ # Site.publish_page(page)
803
+ # end
804
+
805
+ should "record date and time of publish" do
806
+ Content.expects(:publish).with(@revision, nil)
807
+ Revision.expects(:create).with(:revision => @revision, :published_at => @@now)
808
+ Site.publish_all
809
+ end
810
+ should "bump revision after a publish" do
811
+ Site.publish_all
812
+ Site.revision.should == @revision + 1
813
+ Site.published_revision.should == @revision
814
+ end
815
+ should "delete included changes after partial publish" do
816
+ change1 = Change.new
817
+ change1.modified_list = [1, 2, 3]
818
+ change1.save
819
+ change2 = Change.new
820
+ change2.modified_list = [3, 4, 5]
821
+ change2.save
822
+ Change.count.should == 2
823
+ Site.publish_changes([change1.id])
824
+ Change.count.should == 1
825
+ Change[change1.id].should be_nil
826
+ end
827
+ should "delete all changes after full publish" do
828
+ change1 = Change.new
829
+ change1.modified_list = [1, 2, 3]
830
+ change1.save
831
+ change2 = Change.new
832
+ change2.modified_list = [3, 4, 5]
833
+ change2.save
834
+ Change.count.should == 2
835
+ Site.publish_all
836
+ Change.count.should == 0
837
+ end
838
+
839
+ should "not delete changes after an exception during publish" do
840
+ change1 = Change.new
841
+ change1.modified_list = [1, 2, 3]
842
+ change1.save
843
+ change2 = Change.new
844
+ change2.modified_list = [3, 4, 5]
845
+ change2.save
846
+ Content.expects(:publish).at_least_once.raises(Exception)
847
+ Change.count.should == 2
848
+ begin
849
+ silence_logger { Site.publish_all }
850
+ rescue Exception; end
851
+ Change.count.should == 2
852
+ begin
853
+ silence_logger { Site.publish_changes([change1]) }
854
+ rescue Exception; end
855
+ Change.count.should == 2
856
+ end
857
+
858
+ should "set Site.pending_revision before publishing" do
859
+ Content.expects(:publish).with(@revision, nil) { Site.pending_revision == @revision }
860
+ Site.publish_all
861
+ end
862
+
863
+ should "reset Site.pending_revision after publishing" do
864
+ Site.publish_all
865
+ Site.pending_revision.should be_nil
866
+ end
867
+
868
+ should "not update first_published or last_published if rendering fails" do
869
+ c = Content.create
870
+ Content.first.first_published_at.should be_nil
871
+ # Content.delete_all_revisions!
872
+ S::Render.expects(:render_pages).raises(Exception)
873
+ begin
874
+ silence_logger { Site.publish_all }
875
+ rescue Exception; end
876
+ Content.with_editable do
877
+ Content.first.first_published_at.should be_nil
878
+ end
879
+ end
880
+
881
+ should "clean up state on publishing failure" do
882
+ # Content.delete_all_revisions!
883
+ Site.pending_revision.should be_nil
884
+ Content.revision_exists?(@revision).should be_false
885
+ # don't like peeking into implementation here but don't know how else
886
+ # to simulate the right error
887
+ S::Render.expects(:render_pages).raises(Exception)
888
+ begin
889
+ silence_logger { Site.publish_all }
890
+ rescue Exception; end
891
+ Site.pending_revision.should be_nil
892
+ Content.revision_exists?(@revision).should be_false
893
+ begin
894
+ silence_logger { Site.publish_changes([change1]) }
895
+ rescue Exception; end
896
+ Site.pending_revision.should be_nil
897
+ Content.revision_exists?(@revision).should be_false
898
+ end
899
+ end
900
+
901
+ context "rendering" do
902
+ setup do
903
+ @revision = 2
904
+ Content.delete
905
+ State.delete
906
+ State.create(:revision => @revision, :published_revision => 2)
907
+ Site.revision.should == @revision
908
+ Spontaneous.root = File.expand_path(File.dirname(__FILE__) / "../fixtures/example_application")
909
+
910
+ @revision_dir = File.expand_path(File.dirname(__FILE__) / "../../tmp/revisions")
911
+ self.template_root = File.expand_path(File.dirname(__FILE__) / "../fixtures/templates/publishing")
912
+ FileUtils.rm_r(@revision_dir) if File.exists?(@revision_dir)
913
+ class ::PublishablePage < Page; end
914
+ PublishablePage.layout :"static"
915
+ PublishablePage.layout :"dynamic"
916
+ Spontaneous.revision_root = @revision_dir
917
+ Spontaneous.template_root = @template_root
918
+ # Cutaneous::PreviewRenderEngine.context_class = Cutaneous::PublishContext
919
+ Spontaneous::Render.renderer_class = Spontaneous::Render::PublishedRenderer
920
+
921
+ @home = PublishablePage.create(:title => 'Home')
922
+ @home.layout = :"dynamic"
923
+ @about = PublishablePage.create(:title => "About", :slug => "about")
924
+ @blog = PublishablePage.create(:title => "Blog", :slug => "blog")
925
+ @post1 = PublishablePage.create(:title => "Post 1", :slug => "post-1")
926
+ @post2 = PublishablePage.create(:title => "Post 2", :slug => "post-2")
927
+ @post3 = PublishablePage.create(:title => "Post 3", :slug => "post-3")
928
+ @home << @about
929
+ @home << @blog
930
+ @blog << @post1
931
+ @blog << @post2
932
+ @blog << @post3
933
+ @pages = [@home, @about, @blog, @post1, @post2, @post3]
934
+ @pages.each { |p| p.save }
935
+ Site.publish_all
936
+ end
937
+
938
+ teardown do
939
+ # FileUtils.rm_r(@revision_dir) if File.exists?(@revision_dir)
940
+ Content.delete_revision(@revision)
941
+ Content.delete
942
+ State.delete
943
+ Object.send(:remove_const, :PublishablePage)
944
+ end
945
+
946
+ should "put its files into a numbered revision directory" do
947
+ Spontaneous.revision_dir(2).should == @revision_dir / "00002"
948
+ end
949
+
950
+ should "symlink the latest revision to 'current'" do
951
+ revision_dir = @revision_dir / "00002"
952
+ current_dir = @revision_dir / "current"
953
+ File.exists?(current_dir).should be_true
954
+ File.symlink?(current_dir).should be_true
955
+ File.readlink(current_dir).should == revision_dir
956
+ end
957
+
958
+ should "produce rendered versions of each page" do
959
+ revision_dir = @revision_dir / "00002/html"
960
+ file = result = nil
961
+ @pages.each do |page|
962
+ if page.root?
963
+ file = revision_dir / "index.html.cut"
964
+ result = "Page: '#{page.title}' \#{Time.now.to_i}\n"
965
+ else
966
+ file = revision_dir / "#{page.path}/index.html"
967
+ result = "Page: '#{page.title}'\n"
968
+ end
969
+ File.exists?(file).should be_true
970
+ File.read(file).should == result
971
+ end
972
+ revision_dir = @revision_dir / "00002"
973
+ Dir[S.root / "public/**/*"].each do |public_file|
974
+ site_file = public_file.gsub(%r(^#{S.root}/), '')
975
+ publish_file = revision_dir / site_file
976
+ File.exists?(publish_file).should be_true
977
+ end
978
+ end
979
+
980
+ should "generate a config.ru file pointing to the current root" do
981
+ config_file = @revision_dir / "00002/config.ru"
982
+ File.exists?(config_file).should be_true
983
+ File.read(config_file).should =~ %r(#{Spontaneous.root})
984
+ end
985
+ end
986
+ end
987
+ end