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,902 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+ require 'tmpdir'
5
+
6
+ # set :environment, :test
7
+
8
+
9
+ class BackTest < MiniTest::Spec
10
+ include ::Rack::Test::Methods
11
+
12
+
13
+ def self.startup
14
+ # Spontaneous.logger = nil
15
+ Spontaneous.logger.silent!
16
+ end
17
+
18
+ def self.shutdown
19
+ end
20
+
21
+ def app
22
+ Spontaneous::Rack::Back.application
23
+ end
24
+
25
+ def teardown
26
+ # teardown_site_fixture
27
+ end
28
+
29
+ def setup
30
+ @media_dir = File.expand_path('../../../tmp/media', __FILE__)
31
+ Spontaneous.media_dir = @media_dir
32
+ end
33
+ def teardown
34
+ ::FileUtils.rm_rf(@media_dir)
35
+ end
36
+
37
+ def auth_post(path, params={})
38
+ post(path, params.merge("__key" => @key))
39
+ end
40
+
41
+ def auth_get(path, params={})
42
+ get(path, params.merge("__key" => @key))
43
+ end
44
+
45
+ context "Editing interface" do
46
+ setup do
47
+ config = mock()
48
+ config.stubs(:reload_classes).returns(false)
49
+ config.stubs(:auto_login).returns('root')
50
+ config.stubs(:default_charset).returns('utf-8')
51
+ config.stubs(:publishing_method).returns(:immediate)
52
+ root = File.expand_path("../../fixtures/back", __FILE__)
53
+ Spontaneous.root = root
54
+ instance = Spontaneous::Site.instantiate(root, :test, :back)
55
+ instance.stubs(:config).returns(config)
56
+ Spontaneous.instance = instance
57
+ app.send(:set, :raise_errors, true)
58
+ app.send(:set, :dump_errors, true)
59
+ app.send(:set, :show_exceptions, true)
60
+ S::Schema.reset!
61
+ Content.delete
62
+ Spontaneous::Permissions::User.delete
63
+ self.template_root = File.expand_path('../../fixtures/back/templates', __FILE__)
64
+ @app_dir = File.expand_path("../../fixtures/application", __FILE__)
65
+ File.exists?(@app_dir).should be_true
66
+ Spontaneous.stubs(:application_dir).returns(@app_dir)
67
+ Spontaneous::Rack::Back.application.send :set, :show_exceptions, false
68
+ # setup_site_fixture
69
+ # annoying to have to do this, but there you go
70
+ @user = Spontaneous::Permissions::User.create(:email => "root@example.com", :login => "root", :name => "root", :password => "rootpass", :password_confirmation => "rootpass")
71
+ @user.update(:level => Spontaneous::Permissions.root)
72
+ @user.save
73
+ @key = "c5AMX3r5kMHX2z9a5ExLKjAmCcnT6PFf22YQxzb4Codj"
74
+ Spontaneous::Permissions.stubs(:active_user).returns(@user)
75
+ Spontaneous::Permissions::AccessKey.stubs(:valid?).with(@key, @user).returns(true)
76
+
77
+ class Page < Spot::Page
78
+ field :title
79
+ end
80
+ class Piece < Spot::Piece; end
81
+ class Project < Page; end
82
+ class Image < Piece
83
+ field :image, :image
84
+ end
85
+
86
+ class Job < Piece
87
+ field :title
88
+ field :image, :image
89
+
90
+ box :images do
91
+ field :title
92
+ field :image
93
+ allow Image
94
+ end
95
+ end
96
+
97
+ class LinkedJob < Piece
98
+ alias_of Job
99
+ end
100
+
101
+ class HomePage < Page
102
+ field :introduction, :text
103
+ box :projects do
104
+ allow Project
105
+ end
106
+ box :in_progress do
107
+ allow Job
108
+ end
109
+
110
+ box :featured_jobs do
111
+ allow LinkedJob
112
+ end
113
+ end
114
+
115
+
116
+
117
+ @home = HomePage.new(:title => "Home")
118
+ @project1 = Project.new(:title => "Project 1", :slug => "project1")
119
+ @project2 = Project.new(:title => "Project 2", :slug => "project2")
120
+ @project3 = Project.new(:title => "Project 3", :slug => "project3")
121
+ @home.projects << @project1
122
+ @home.projects << @project2
123
+ @home.projects << @project3
124
+
125
+ @job1 = Job.new(:title => "Job 1", :image => "/i/job1.jpg")
126
+ @job2 = Job.new(:title => "Job 2", :image => "/i/job2.jpg")
127
+ @job3 = Job.new(:title => "Job 3", :image => "/i/job3.jpg")
128
+ @image1 = Image.new
129
+ @job1.images << @image1
130
+ @home.in_progress << @job1
131
+ @home.in_progress << @job2
132
+ @home.in_progress << @job3
133
+
134
+ @home.save
135
+ @home = Content[@home.id]
136
+ end
137
+
138
+ teardown do
139
+ [:Page, :Piece, :HomePage, :Job, :Project, :Image, :LinkedJob].each { |klass| BackTest.send(:remove_const, klass) rescue nil }
140
+ Spontaneous::Permissions::User.delete
141
+ Content.delete
142
+ end
143
+
144
+ context "@spontaneous" do
145
+ setup do
146
+ Spontaneous.stubs(:reload!)
147
+ end
148
+
149
+ should "return application page" do
150
+ get '/@spontaneous/'
151
+ assert last_response.ok?
152
+ last_response.body.should =~ /<title>Spontaneous<\/title>/
153
+ end
154
+
155
+ should "return json for root page" do
156
+ auth_get '/@spontaneous/root'
157
+ assert last_response.ok?
158
+ last_response.content_type.should == "application/json;charset=utf-8"
159
+ assert_equal S::JSON.encode(Site.root.export), last_response.body
160
+ end
161
+
162
+ should "return json for individual pages" do
163
+ page = Site.root.children.first
164
+ auth_get "/@spontaneous/page/#{page.id}"
165
+ assert last_response.ok?
166
+ last_response.content_type.should == "application/json;charset=utf-8"
167
+ assert_equal S::JSON.encode(page.export), last_response.body
168
+ end
169
+
170
+ should "return json for all types" do
171
+ auth_get "/@spontaneous/types"
172
+ assert last_response.ok?
173
+ last_response.content_type.should == "application/json;charset=utf-8"
174
+ assert_equal Schema.serialise_http, last_response.body
175
+ end
176
+
177
+ should "return scripts from js dir" do
178
+ get '/@spontaneous/js/test.js'
179
+ assert last_response.ok?
180
+ # last_response.content_type.should == "application/javascript;charset=utf-8"
181
+ last_response.content_type.should == "application/javascript"
182
+ assert_equal File.read(@app_dir / 'js/test.js'), last_response.body
183
+ end
184
+
185
+ should "return less rendered to css from css dir" do
186
+ get '/@spontaneous/css/test.css'
187
+ assert last_response.ok?
188
+ last_response.content_type.should == "text/css;charset=utf-8"
189
+ assert_equal "h1 { color: #4d926f; }\n", last_response.body
190
+ end
191
+
192
+ should "return a site map for root by default" do
193
+ auth_get '/@spontaneous/map'
194
+ assert last_response.ok?
195
+ last_response.content_type.should == "application/json;charset=utf-8"
196
+ assert_equal Site.map.to_json, last_response.body
197
+ end
198
+
199
+ should "return a site map for any page id" do
200
+ auth_get "/@spontaneous/map/#{@home.id}"
201
+ assert last_response.ok?
202
+ last_response.content_type.should == "application/json;charset=utf-8"
203
+ assert_equal Site.map(@home.id).to_json, last_response.body
204
+ end
205
+
206
+ should "return a site map for any url" do
207
+ page = @project1
208
+ auth_get "/@spontaneous/location#{@project1.path}"
209
+ assert last_response.ok?
210
+ last_response.content_type.should == "application/json;charset=utf-8"
211
+ assert_equal Site.map(@project1.id).to_json, last_response.body
212
+ end
213
+
214
+ should "return 404 when asked for map of non-existant page" do
215
+ id = '9999'
216
+ S::Content.stubs(:[]).with(id).returns(nil)
217
+ auth_get "/@spontaneous/map/#{id}"
218
+ assert last_response.status == 404
219
+ end
220
+
221
+ should "reorder pieces" do
222
+ auth_post "/@spontaneous/content/#{@job2.id}/position/0"
223
+ assert last_response.ok?
224
+ last_response.content_type.should == "application/json;charset=utf-8"
225
+ @home.reload
226
+ @home.in_progress.pieces.first.id.should == @job2.id
227
+
228
+ page = Content[@home.id]
229
+ page.in_progress.pieces.first.id.should == @job2.id
230
+ end
231
+
232
+ # should "reorder pages" do
233
+ # post "/@spontaneous/page/#{@about.id}/position/0"
234
+ # assert last_response.ok?
235
+ # last_response.content_type.should == "application/json;charset=utf-8"
236
+ # # can't actually be bothered to set this test up
237
+ # # @piece2_2.reload.pieces.first.target.id.should == @piece2_5.id
238
+ # end
239
+
240
+ context "saving" do
241
+ setup do
242
+ # @home = HomePage.new
243
+ # @piece = Text.new
244
+ # @home.in_progress << @piece
245
+ # @home.save
246
+ # @piece.save
247
+ end
248
+
249
+ should "update content field values" do
250
+ params = {
251
+ "field[#{@job1.fields.title.schema_id.to_s}][value]" => "Updated field_name_1"
252
+ }
253
+ auth_post "/@spontaneous/save/#{@job1.id}", params
254
+ assert last_response.ok?
255
+ last_response.content_type.should == "application/json;charset=utf-8"
256
+ @job1 = Content[@job1.id]
257
+ last_response.body.should == @job1.serialise_http
258
+ @job1.fields.title.value.should == "Updated field_name_1"
259
+ end
260
+
261
+ should "update page field values" do
262
+ params = {
263
+ "field[#{@home.fields.title.schema_id.to_s}][value]" => "Updated title",
264
+ "field[#{@home.fields.introduction.schema_id.to_s}][value]" => "Updated intro"
265
+ }
266
+ auth_post "/@spontaneous/save/#{@home.id}", params
267
+ assert last_response.ok?
268
+ last_response.content_type.should == "application/json;charset=utf-8"
269
+ @home = Content[@home.id]
270
+ last_response.body.should == @home.serialise_http
271
+ @home.fields.title.value.should == "Updated title"
272
+ @home.fields.introduction.value.should == "<p>Updated intro</p>\n"
273
+ end
274
+ should "update box field values" do
275
+ box = @job1.images
276
+ box.fields.title.to_s.should_not == "Updated title"
277
+ params = {
278
+ "field[#{box.fields.title.schema_id.to_s}][value]" => "Updated title"
279
+ }
280
+ auth_post "/@spontaneous/savebox/#{@job1.id}/#{box.schema_id.to_s}", params
281
+ assert last_response.ok?
282
+ last_response.content_type.should == "application/json;charset=utf-8"
283
+ @job1 = Content[@job1.id]
284
+ @job1.images.title.value.should == "Updated title"
285
+ end
286
+
287
+ should "generate an error if there is a field version conflict" do
288
+ field = @job1.fields.title
289
+ sid = field.schema_id.to_s
290
+ params = { "fields" => {sid => "2"} }
291
+ field.stubs(:version).returns(3)
292
+ Content.stubs(:[]).with(@job1.id.to_s).returns(@job1)
293
+
294
+ auth_post "/@spontaneous/version/#{@job1.id}", params
295
+ assert last_response.status == 409, "Should have recieved a 409 conflict but instead received a #{last_response.status}"
296
+ last_response.content_type.should == "application/json;charset=utf-8"
297
+ result = Spontaneous.deserialise_http(last_response.body)
298
+ result.should == {
299
+ sid.to_sym => [ field.version, field.value ]
300
+ }
301
+ end
302
+
303
+ should "generate an error if there is a field version conflict for boxes" do
304
+ box = @job1.images
305
+ field = box.fields.title
306
+ sid = field.schema_id.to_s
307
+ params = { "fields" => {sid => "2"} }
308
+ field.stubs(:version).returns(3)
309
+ Content.stubs(:[]).with(@job1.id.to_s).returns(@job1)
310
+
311
+ auth_post "/@spontaneous/version/#{@job1.id}/#{box.schema_id.to_s}", params
312
+ assert last_response.status == 409, "Should have recieved a 409 conflict but instead received a #{last_response.status}"
313
+ last_response.content_type.should == "application/json;charset=utf-8"
314
+ result = Spontaneous.deserialise_http(last_response.body)
315
+ result.should == {
316
+ sid.to_sym => [ field.version, field.value ]
317
+ }
318
+ end
319
+
320
+ end
321
+ end # context @spontaneous
322
+
323
+ context "Visibility" do
324
+ setup do
325
+ Spontaneous.stubs(:reload!)
326
+ # @home = HomePage.new
327
+ # @piece = Text.new
328
+ # @home.in_progress << @piece
329
+ # @home.save
330
+ # @piece.save
331
+ end
332
+ should "be toggled" do
333
+ @job1.reload.visible?.should == true
334
+ auth_post "/@spontaneous/toggle/#{@job1.id}"
335
+ assert last_response.ok?
336
+ last_response.content_type.should == "application/json;charset=utf-8"
337
+ Spot::JSON.parse(last_response.body).should == {:id => @job1.id, :hidden => true}
338
+ @job1.reload.visible?.should == false
339
+ auth_post "/@spontaneous/toggle/#{@job1.id}"
340
+ assert last_response.ok?
341
+ @job1.reload.visible?.should == true
342
+ Spot::JSON.parse(last_response.body).should == {:id => @job1.id, :hidden => false}
343
+ end
344
+ end
345
+
346
+ context "preview" do
347
+ setup do
348
+ Spontaneous.stubs(:reload!)
349
+ @now = Time.now
350
+ Time.stubs(:now).returns(@now)
351
+ end
352
+ should "return rendered root page" do
353
+ get "/"
354
+ assert last_response.ok?
355
+ last_response.content_type.should == "text/html;charset=utf-8"
356
+ assert_equal S::Render.with_preview_renderer { @home.render }, last_response.body
357
+ end
358
+
359
+ should "return rendered child-page" do
360
+ get "/project1"
361
+ assert last_response.ok?
362
+ last_response.content_type.should == "text/html;charset=utf-8"
363
+ assert_equal S::Render.with_preview_renderer { @project1.render }, last_response.body
364
+ end
365
+
366
+ should "return cache-busting headers" do
367
+ ["/project1", "/"].each do |path|
368
+ get path
369
+ assert last_response.ok?
370
+ last_response.headers['Expires'].should == @now.to_formatted_s(:rfc822)
371
+ last_response.headers['Last-Modified'].should == @now.to_formatted_s(:rfc822)
372
+ end
373
+ end
374
+
375
+ should "return cache-control headers" do
376
+ ["/project1", "/"].each do |path|
377
+ get path
378
+ assert last_response.ok?
379
+ ["no-store", 'no-cache', 'must-revalidate', 'max-age=0'].each do |p|
380
+ last_response.headers['Cache-Control'].should =~ %r(#{p})
381
+ end
382
+ end
383
+ end
384
+ end
385
+
386
+ context "static files" do
387
+ setup do
388
+ Spontaneous.stubs(:reload!)
389
+ end
390
+ should "work for site" do
391
+ get "/test.html"
392
+ assert last_response.ok?
393
+ assert_equal <<-HTML, last_response.body
394
+ <html><head><title>Test</title></head></html>
395
+ HTML
396
+ end
397
+ should "work for @spontaneous files" do
398
+ get "/@spontaneous/static/test.html"
399
+ assert last_response.ok?
400
+ assert_equal <<-HTML, last_response.body
401
+ <html><head><title>@spontaneous Test</title></head></html>
402
+ HTML
403
+ end
404
+ # should "return a custom favicon" do
405
+ # get "/favicon.ico"
406
+ # assert last_response.ok?
407
+ # p @app_dir
408
+ # assert_equal File.read(@app_dir / 'static/favicon.ico'), last_response.body
409
+ # end
410
+ end
411
+
412
+ context "media files" do
413
+ setup do
414
+ Spontaneous.stubs(:reload!)
415
+ Spontaneous.media_dir = File.join(File.dirname(__FILE__), "../fixtures/media")
416
+ end
417
+ teardown do
418
+ end
419
+ should "be available under /media" do
420
+ get "/media/101/003/rose.jpg"
421
+ assert last_response.ok?
422
+ last_response.content_type.should == "image/jpeg"
423
+ end
424
+ end
425
+
426
+ context "file uploads" do
427
+ setup do
428
+ Spontaneous.stubs(:reload!)
429
+ @src_file = Pathname.new(File.join(File.dirname(__FILE__), "../fixtures/images/rose.jpg")).realpath.to_s
430
+ @upload_id = 9723
431
+ Time.stubs(:now).returns(Time.at(1288882153))
432
+ Spontaneous::Media.stubs(:upload_index).returns(23)
433
+ end
434
+
435
+ should "create a file in a safe subdirectory of media/tmp" do
436
+ auth_post "@spontaneous/file/upload/9723", "file" => ::Rack::Test::UploadedFile.new(@src_file, "image/jpeg")
437
+ assert last_response.ok?
438
+ last_response.content_type.should == "application/json;charset=utf-8"
439
+ assert_equal({
440
+ :id => '9723',
441
+ :src => "/media/tmp/1288882153.23/rose.jpg",
442
+ :path => "#{Spontaneous.media_dir}/tmp/1288882153.23/rose.jpg"
443
+ }.to_json, last_response.body)
444
+ end
445
+
446
+ should "replace values of fields immediately when required" do
447
+ @image1.image.processed_value.should == ""
448
+ auth_post "@spontaneous/file/replace/#{@image1.id}", "file" => ::Rack::Test::UploadedFile.new(@src_file, "image/jpeg"), "field" => @image1.image.schema_id.to_s
449
+ assert last_response.ok?
450
+ last_response.content_type.should == "application/json;charset=utf-8"
451
+ @image1 = Content[@image1.id]
452
+ src = @image1.image.src
453
+ src.should =~ /^\/media(.+)\/rose\.jpg$/
454
+ last_response.body.should == {
455
+ :id => @image1.id,
456
+ :src => src,
457
+ :version => 1
458
+ }.to_json
459
+ File.exist?(Media.to_filepath(src)).should be_true
460
+ get src
461
+ assert last_response.ok?
462
+ end
463
+
464
+ should "replace values of box file fields" do
465
+ @job1.images.image.processed_value.should == ""
466
+ auth_post "@spontaneous/file/replace/#{@job1.id}/#{@job1.images.schema_id}", "file" => ::Rack::Test::UploadedFile.new(@src_file, "image/jpeg"), "field" => @job1.images.image.schema_id.to_s
467
+ assert last_response.ok?
468
+ last_response.content_type.should == "application/json;charset=utf-8"
469
+ @job1 = Content[@job1.id]
470
+ src = @job1.images.image.src
471
+ src.should =~ /^\/media(.+)\/rose\.jpg$/
472
+ last_response.body.should == {
473
+ :id => @job1.id,
474
+ :src => src,
475
+ :version => 1
476
+ }.to_json
477
+ File.exist?(Media.to_filepath(src)).should be_true
478
+ get src
479
+ assert last_response.ok?
480
+ end
481
+
482
+ should "be able to wrap pieces around files using default addable class" do
483
+ box = @job1.images
484
+ current_count = box.pieces.length
485
+ first_id = box.pieces.first.id.to_s
486
+
487
+ auth_post "/@spontaneous/file/wrap/#{@job1.id}/#{box.schema_id.to_s}", "file" => ::Rack::Test::UploadedFile.new(@src_file, "image/jpeg")
488
+ assert last_response.ok?
489
+ last_response.content_type.should == "application/json;charset=utf-8"
490
+ box = @job1.reload.images
491
+ first = box.pieces.first
492
+ box.pieces.length.should == current_count+1
493
+ first.image.src.should =~ /^\/media(.+)\/#{File.basename(@src_file)}$/
494
+ required_response = {
495
+ :position => 0,
496
+ :entry => first.export
497
+ }
498
+ Spot::JSON.parse(last_response.body).should == required_response
499
+ end
500
+ end
501
+ context "pieces" do
502
+ setup do
503
+ Spontaneous.stubs(:reload!)
504
+ end
505
+ should "be addable" do
506
+ current_count = @home.in_progress.pieces.length
507
+ first_id = @home.in_progress.pieces.first.id
508
+ @home.in_progress.pieces.first.class.name.should_not == "BackTest::Image"
509
+ auth_post "/@spontaneous/add/#{@home.id}/#{@home.in_progress.schema_id.to_s}/#{Image.schema_id.to_s}"
510
+ assert last_response.ok?, "Recieved #{last_response.status} not 200"
511
+ last_response.content_type.should == "application/json;charset=utf-8"
512
+ @home.reload
513
+ @home.in_progress.pieces.length.should == current_count+1
514
+ @home.in_progress.pieces.first.id.should_not == first_id
515
+ @home.in_progress.pieces.first.class.name.should == "BackTest::Image"
516
+ required_response = {
517
+ :position => 0,
518
+ :entry => @home.in_progress.pieces.first.export
519
+ }
520
+ Spot::JSON.parse(last_response.body).should == required_response
521
+ end
522
+
523
+ should "be removable" do
524
+ target = @home.in_progress.first
525
+ auth_post "/@spontaneous/destroy/#{target.id}"
526
+ assert last_response.ok?
527
+ last_response.content_type.should == "application/json;charset=utf-8"
528
+ Content[target.id].should be_nil
529
+ end
530
+ end
531
+
532
+ context "Page paths" do
533
+ setup do
534
+ Spontaneous.stubs(:reload!)
535
+ end
536
+ should "be editable" do
537
+ @project1.path.should == '/project1'
538
+ auth_post "/@spontaneous/slug/#{@project1.id}", 'slug' => 'howabout'
539
+ assert last_response.ok?
540
+ last_response.content_type.should == "application/json;charset=utf-8"
541
+ @project1.reload
542
+ @project1.path.should == "/howabout"
543
+ Spot::JSON.parse(last_response.body).should == {:path => '/howabout' }
544
+ end
545
+ should "raise error when trying to save duplicate path" do
546
+ auth_post "/@spontaneous/slug/#{@project1.id}", 'slug' => 'project2'
547
+ last_response.status.should == 409
548
+ @project1.reload.path.should == '/project1'
549
+ end
550
+ should "raise error when trying to save empty slug" do
551
+ auth_post "/@spontaneous/slug/#{@project1.id}", 'slug' => ''
552
+ last_response.status.should == 406
553
+ @project1.reload.path.should == '/project1'
554
+ auth_post "/@spontaneous/slug/#{@project1.id}"
555
+ last_response.status.should == 406
556
+ @project1.reload.path.should == '/project1'
557
+ end
558
+ should "provide a list of unavailable slugs for a page" do
559
+ auth_get "/@spontaneous/slug/#{@project1.id}/unavailable"
560
+ assert last_response.ok?
561
+ last_response.content_type.should == "application/json;charset=utf-8"
562
+ Spot::JSON.parse(last_response.body).should == %w(project2 project3)
563
+ end
564
+ end
565
+ context "UIDs" do
566
+ should "be editable" do
567
+ uid = "fishy"
568
+ @project1.uid.should_not == uid
569
+ auth_post "/@spontaneous/uid/#{@project1.id}", 'uid' => uid
570
+ assert last_response.ok?
571
+ Spot::JSON.parse(last_response.body).should == {:uid => uid}
572
+ @project1.reload.uid.should == uid
573
+ end
574
+ should "not be editable by non-developer users" do
575
+ @user.stubs(:developer?).returns(false)
576
+ uid = "boom"
577
+ orig = @project1.uid
578
+ @project1.uid.should_not == uid
579
+ auth_post "/@spontaneous/uid/#{@project1.id}", 'uid' => uid
580
+ assert last_response.status == 401
581
+ @project1.reload.uid.should == orig
582
+ end
583
+ end
584
+ context "Request cache" do
585
+ setup do
586
+ Spontaneous.stubs(:reload!)
587
+ # @home = HomePage.new
588
+ # @piece = Text.new
589
+ # @home.in_progress << @piece
590
+ # @home.save
591
+ # @piece.save
592
+ Change.delete
593
+ end
594
+ should "wrap all updates in a Change.record" do
595
+ params = {
596
+ "field[#{@job1.fields.title.schema_id.to_s}][value]" => "Updated field_name_1"
597
+ }
598
+ Change.count.should == 0
599
+ auth_post "/@spontaneous/save/#{@job1.id}", params
600
+ Change.count.should == 1
601
+ Change.first.modified_list.should == [@home.id]
602
+ end
603
+
604
+ end
605
+
606
+ context "Publishing" do
607
+ setup do
608
+ Spontaneous.stubs(:reload!)
609
+ @now = Time.now
610
+ Time.stubs(:now).returns(@now)
611
+ Change.delete
612
+ @c1 = Change.new
613
+ @c1.push(@home)
614
+ @c1.push(@project1)
615
+ @c1.save
616
+ @c2 = Change.new
617
+ @c2.push(@home)
618
+ @c2.push(@project1)
619
+ @c2.save
620
+ end
621
+
622
+ teardown do
623
+ Change.delete
624
+ end
625
+
626
+ should "be able to retrieve a serialised list of all unpublished changes" do
627
+ auth_get "/@spontaneous/publish/changes"
628
+ assert last_response.ok?
629
+ last_response.content_type.should == "application/json;charset=utf-8"
630
+ last_response.body.should == Change.serialise_http
631
+ end
632
+
633
+ should "be able to start a publish with a set of change sets" do
634
+ Site.expects(:publish_changes).with([@c1.id])
635
+ auth_post "/@spontaneous/publish/publish", :change_set_ids => [@c1.id]
636
+ assert last_response.ok?
637
+ end
638
+
639
+ should "not launch publish if list of changes is empty" do
640
+ Site.expects(:publish_changes).with().never
641
+ auth_post "/@spontaneous/publish/publish", :change_set_ids => ""
642
+ assert last_response.status == 400, "Expected 400, recieved #{last_response.status}"
643
+
644
+ auth_post "/@spontaneous/publish/publish", :change_set_ids => nil
645
+ assert last_response.status == 400
646
+ end
647
+ should "recognise when the list of changes is complete" do
648
+ Site.expects(:publish_changes).with([@c1.id, @c2.id])
649
+ auth_post "/@spontaneous/publish/publish", :change_set_ids => [@c1.id, @c2.id]
650
+ assert last_response.ok?
651
+ end
652
+
653
+ should "be able to retrieve the publishing status" do
654
+ Site.publishing_method.status = "something:50"
655
+ auth_get "/@spontaneous/publish/status"
656
+ assert last_response.ok?
657
+ last_response.body.should == {:status => "something", :progress => "50"}.to_json
658
+ end
659
+ end
660
+
661
+ context "New sites" do
662
+ setup do
663
+ Spontaneous.stubs(:reload!)
664
+ @root_class = Site.root.class
665
+ Content.delete
666
+ Change.delete
667
+ end
668
+ should "raise a 406 Not Acceptable error when downloading page details" do
669
+ auth_get "/@spontaneous/location/"
670
+ last_response.status.should == 406
671
+ end
672
+ should "create a homepage of the specified type" do
673
+ auth_post "/@spontaneous/root", 'type' => @root_class.schema_id
674
+ assert last_response.ok?
675
+ Site.root.must_be_instance_of(@root_class)
676
+ Site.root.title.value.should =~ /Home/
677
+ end
678
+ should "only create one root" do
679
+ auth_post "/@spontaneous/root", 'type' => @root_class.schema_id
680
+ assert last_response.ok?
681
+ Content.count.should == 1
682
+ auth_post "/@spontaneous/root", 'type' => @root_class.schema_id
683
+ assert last_response.status == 403
684
+ Content.count.should == 1
685
+ end
686
+ should "have a change reflecting creation of root" do
687
+ Change.count.should == 0
688
+ auth_post "/@spontaneous/root", 'type' => @root_class.schema_id
689
+ Change.count.should == 1
690
+ end
691
+ end
692
+
693
+ context "Aliases" do
694
+ setup do
695
+ Spontaneous.stubs(:reload!)
696
+ end
697
+
698
+ teardown do
699
+ end
700
+
701
+ should "be able to retrieve a list of potential targets" do
702
+ auth_get "/@spontaneous/targets/#{LinkedJob.schema_id}"
703
+ assert last_response.ok?
704
+ Spot::JSON.parse(last_response.body).should == LinkedJob.targets.map do |job|
705
+ {
706
+ :id => job.id,
707
+ :title => job.title.to_s,
708
+ :icon => job.image.export
709
+ }
710
+ end
711
+ end
712
+ should "be able to add an alias to a box" do
713
+ @home.featured_jobs.pieces.length.should == 0
714
+ auth_post "/@spontaneous/alias/#{@home.id}/#{HomePage.boxes[:featured_jobs].schema_id.to_s}", 'alias_id' => LinkedJob.schema_id.to_s, 'target_id' => Job.first.id
715
+ assert last_response.ok?, "Recieved #{last_response.status} not 200"
716
+ last_response.content_type.should == "application/json;charset=utf-8"
717
+ @home.reload
718
+ @home.featured_jobs.pieces.length.should == 1
719
+ a = @home.featured_jobs.first
720
+ a.alias?.should be_true
721
+ a.target.should == Job.first
722
+ required_response = {
723
+ :position => 0,
724
+ :entry => @home.featured_jobs.pieces.first.export
725
+ }
726
+ Spot::JSON.parse(last_response.body).should == required_response
727
+ end
728
+ end
729
+
730
+ context "Schema conflicts" do
731
+ setup do
732
+ # enable schema validation errors by creating and using a permanent map file
733
+ @schema_map = File.join(Dir.tmpdir, "schema.yml")
734
+ FileUtils.rm(@schema_map) if File.exists?(@schema_map)
735
+ S::Schema.schema_map_file = @schema_map
736
+ S::Schema.validate!
737
+ S::Schema.write_schema
738
+ S::Schema.schema_loader_class = S::Schema::PersistentMap
739
+ S::Schema.reload!
740
+ Job.field :replaced
741
+ @df1 = Job.field_prototypes[:title]
742
+ @af1 = Job.field_prototypes[:replaced]
743
+ @f1 = Job.field_prototypes[:image]
744
+ @uid = @df1.schema_id.to_s
745
+ Job.stubs(:field_prototypes).returns({:replaced => @af1, :image => @f1})
746
+ Job.stubs(:fields).returns([@af1, @f1])
747
+ S::Schema.reload!
748
+ lambda { S::Schema.validate! }.must_raise(Spontaneous::SchemaModificationError)
749
+ # hammer, meet nut
750
+ S::Rack::Back::EditingInterface.use Spontaneous::Rack::Reloader
751
+ Spontaneous::Loader.stubs(:reload!)
752
+ end
753
+
754
+ teardown do
755
+ S::Schema.schema_loader_class = S::Schema::TransientMap
756
+ FileUtils.rm(@schema_map) if File.exists?(@schema_map)
757
+ end
758
+
759
+ should "raise a 412 error" do
760
+ get '/@spontaneous/'
761
+ assert last_response.status == 412, "Schema validation errors should raise a 412 but instead recieved a #{last_response.status}"
762
+ end
763
+
764
+ should "present a dialogue page with possible solutions" do
765
+ get '/@spontaneous/'
766
+ assert last_response.status == 412, "Schema validation errors should raise a 412 but instead recieved a #{last_response.status}"
767
+ last_response.body.should =~ %r{<form action="/@spontaneous/schema/delete" method="post"}
768
+ last_response.body.should =~ %r{<input type="hidden" name="uid" value="#{@df1.schema_id}"}
769
+
770
+ last_response.body.should =~ %r{<form action="/@spontaneous/schema/rename" method="post"}
771
+ last_response.body.should =~ %r{<input type="hidden" name="ref" value="#{@af1.schema_name}"}
772
+ end
773
+
774
+ should "perform renames via a link" do
775
+ action ="/@spontaneous/schema/rename"
776
+ auth_post action, "uid" => @df1.schema_id, "ref" => @af1.schema_name, "origin" => "/@spontaneous"
777
+ last_response.status.should == 302
778
+ begin
779
+ S::Schema.validate!
780
+ rescue Spontaneous::SchemaModificationError => e
781
+ flunk("Schema modification link should have resolved schema errors")
782
+ end
783
+ end
784
+
785
+ should "perform deletions via a link" do
786
+ action ="/@spontaneous/schema/delete"
787
+ auth_post action, "uid" => @df1.schema_id, "origin" => "/@spontaneous"
788
+ last_response.status.should == 302
789
+ begin
790
+ S::Schema.validate!
791
+ rescue Spontaneous::SchemaModificationError => e
792
+ flunk("Schema modification link should have resolved schema errors")
793
+ end
794
+ end
795
+
796
+ should "redirect back to original page"
797
+ end
798
+ context "sharded uploading" do
799
+ setup do
800
+ @temp_dir = Dir.mktmpdir
801
+ @shard_dir = @temp_dir / "tmp"
802
+ @image = File.expand_path("../../fixtures/sharding/rose.jpg", __FILE__)
803
+ # read the digest dynamically in case I change that image
804
+ @image_digest = S::Media.digest(@image)
805
+ FileUtils.mkdir(@shard_dir)
806
+ Spontaneous.media_dir = @temp_dir
807
+ end
808
+ teardown do
809
+ FileUtils.rm_rf(@temp_dir)
810
+ end
811
+
812
+ should "have the right setting for shard_dir" do
813
+ Spontaneous.shard_path.should == @shard_dir
814
+ Spontaneous.shard_path("abcdef0123").should == @shard_dir/ "ab/cd/abcdef0123"
815
+ end
816
+
817
+ should "know when it already has a shard" do
818
+ hash = '4d68c8f13459c0edb40504de5003ec2a6b74e613'
819
+ FileUtils.touch(Spontaneous.shard_path(hash))
820
+ FileUtils.expects(:touch).with(Spontaneous.shard_path(hash))
821
+ auth_get "/@spontaneous/shard/#{hash}"
822
+ last_response.status.should == 200
823
+ end
824
+
825
+ should "know when it doesn't have a shard" do
826
+ auth_get "/@spontaneous/shard/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
827
+ last_response.status.should == 404
828
+ end
829
+
830
+ should "receive a shard and put it in the right place" do
831
+ auth_post "@spontaneous/shard/#{@image_digest}", "file" => ::Rack::Test::UploadedFile.new(@image, "image/jpeg")
832
+ assert last_response.ok?
833
+ auth_get "/@spontaneous/shard/#{@image_digest}"
834
+ last_response.status.should == 200
835
+ end
836
+
837
+ should "return an error if the uploaded file has the wrong hash" do
838
+ S::Media.expects(:digest).with(anything).returns("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
839
+ auth_post "@spontaneous/shard/#{@image_digest}", "file" => ::Rack::Test::UploadedFile.new(@image, "image/jpeg")
840
+ last_response.status.should == 409
841
+ end
842
+
843
+ should "reassemble multiple parts into a single file and attach it to a content item" do
844
+ parts = %w(xaa xab xac xad xae xaf xag)
845
+ paths = parts.map { |part| File.expand_path("../../fixtures/sharding/#{part}", __FILE__) }
846
+ hashes = paths.map { |path| S::Media.digest(path) }
847
+ paths.each_with_index do |part, n|
848
+ auth_post "/@spontaneous/shard/#{hashes[n]}", "file" => ::Rack::Test::UploadedFile.new(part, "application/octet-stream")
849
+ end
850
+ hashes.each do |hash|
851
+ auth_get "/@spontaneous/shard/#{hash}"
852
+ last_response.status.should == 200
853
+ end
854
+ @image1.image.processed_value.should == ""
855
+ auth_post "/@spontaneous/shard/replace/#{@image1.id}", "filename" => "rose.jpg",
856
+ "shards" => hashes, "field" => @image1.image.schema_id.to_s
857
+ assert last_response.ok?
858
+ last_response.content_type.should == "application/json;charset=utf-8"
859
+ @image1 = Content[@image1.id]
860
+ src = @image1.image.src
861
+ src.should =~ %r{^(.+)/rose\.jpg$}
862
+ Spot::JSON.parse(last_response.body).should == {
863
+ :id => @image1.id,
864
+ :src => src,
865
+ :version => 1
866
+ }
867
+ File.exist?(Media.to_filepath(src)).should be_true
868
+ S::Media.digest(Media.to_filepath(src)).should == @image_digest
869
+ end
870
+
871
+ should "be able to wrap pieces around files using default addable class" do
872
+ parts = %w(xaa xab xac xad xae xaf xag)
873
+ paths = parts.map { |part| File.expand_path("../../fixtures/sharding/#{part}", __FILE__) }
874
+ hashes = paths.map { |path| S::Media.digest(path) }
875
+ paths.each_with_index do |part, n|
876
+ auth_post "/@spontaneous/shard/#{hashes[n]}", "file" => ::Rack::Test::UploadedFile.new(part, "application/octet-stream")
877
+ end
878
+ hashes.each do |hash|
879
+ auth_get "/@spontaneous/shard/#{hash}"
880
+ last_response.status.should == 200
881
+ end
882
+ box = @job1.images
883
+ current_count = box.pieces.length
884
+ first_id = box.pieces.first.id.to_s
885
+
886
+ auth_post "/@spontaneous/shard/wrap/#{@job1.id}/#{box.schema_id.to_s}", "filename" => "rose.jpg", "shards" => hashes, "mime_type" => "image/jpeg"
887
+ assert last_response.ok?, "Should have got status 200 but got #{last_response.status}"
888
+ last_response.content_type.should == "application/json;charset=utf-8"
889
+ box = @job1.reload.images
890
+ first = box.pieces.first
891
+ box.pieces.length.should == current_count+1
892
+ first.image.src.should =~ %r{^(.+)/rose\.jpg$}
893
+ required_response = {
894
+ :position => 0,
895
+ :entry => first.export
896
+ }
897
+ Spot::JSON.parse(last_response.body).should == required_response
898
+ end
899
+ end
900
+ end
901
+ end
902
+