spontaneous 0.1.0.alpha1 → 0.2.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 (413) hide show
  1. data/Gemfile +40 -35
  2. data/LICENSE +20 -0
  3. data/Rakefile +39 -42
  4. data/Readme.markdown +155 -0
  5. data/application/css/definitions.scss +3 -0
  6. data/application/css/editing.scss +9 -4
  7. data/application/css/min/565d4c25e82148acb01c45c8d675b37a08676d77.css +1 -0
  8. data/application/css/min/84dbe894ea96eafd321c30823d630817bfc4b03b.css +1 -0
  9. data/application/css/min/d1b54ff4847c613618267ca1c15658e2aee0a4e5.css +1 -0
  10. data/application/css/v2.scss +144 -50
  11. data/application/js/add_alias_dialogue.js +18 -8
  12. data/application/js/ajax.js +1 -1
  13. data/application/js/authentication.js +8 -1
  14. data/application/js/box.js +37 -7
  15. data/application/js/box_container.js +1 -1
  16. data/application/js/compatibility.js +7 -0
  17. data/application/js/content.js +7 -0
  18. data/application/js/content_area.js +4 -0
  19. data/application/js/edit_panel.js +44 -18
  20. data/application/js/editing.js +12 -0
  21. data/application/js/event_source.js +17 -0
  22. data/application/js/extensions.js +2 -0
  23. data/application/js/field_types/file_field.js +2 -1
  24. data/application/js/field_types/image_field.js +35 -48
  25. data/application/js/field_types/long_string_field.js +12 -0
  26. data/application/js/field_types/markdown_field.js +2 -5
  27. data/application/js/field_types/string_field.js +5 -1
  28. data/application/js/field_types/webvideo_field.js +15 -0
  29. data/application/js/init.js +1 -0
  30. data/application/js/location.js +30 -19
  31. data/application/js/login.js +1 -2
  32. data/application/js/min/2a0c2962537a3181fedfff5c92596ba6d3122dc9.js +3 -0
  33. data/application/js/min/4cf1c493d3379ecba5287758c61238034c0893f9.js +2 -0
  34. data/application/js/min/78ac6b99d96750bb6b9f9aad4cb9cd91cd03f391.js +3 -0
  35. data/application/js/min/c8efb9b9f7c3f6613fcebc6be60f605b6570a382.js +90 -0
  36. data/application/js/page.js +1 -0
  37. data/application/js/page_entry.js +6 -40
  38. data/application/js/preview.js +2 -3
  39. data/application/js/top_bar.js +76 -31
  40. data/application/js/upload.js +5 -2
  41. data/application/js/upload_manager.js +2 -81
  42. data/application/js/vendor/jquery-1.7.1.min.js +4 -0
  43. data/application/js/vendor/jquery-ui-1.8.16.custom.min.js +791 -0
  44. data/application/js/views.js +3 -3
  45. data/application/js/views/box_view.js +164 -83
  46. data/application/js/views/page_piece_view.js +2 -2
  47. data/application/js/views/page_view.js +46 -15
  48. data/application/js/views/piece_view.js +29 -8
  49. data/application/static/diagonal-texture.png +0 -0
  50. data/application/static/item-buttons-highlight.png +0 -0
  51. data/application/static/plus-box.png +0 -0
  52. data/application/static/plus_alt.svg +8 -0
  53. data/application/static/slot-up-arrow.png +0 -0
  54. data/application/views/{index.erubis → index.erb} +9 -5
  55. data/application/views/{login.erubis → login.erb} +14 -10
  56. data/application/views/schema_modification_error.html.erb +18 -6
  57. data/application/views/{unsupported.erubis → unsupported.erb} +0 -0
  58. data/bin/spot +15 -2
  59. data/db/migrations/20100610142136_init.rb +2 -2
  60. data/db/migrations/20101130104334_timestamps.rb +2 -2
  61. data/db/migrations/20110209152710_users_and_groups.rb +11 -11
  62. data/db/migrations/20120106171423_visibility_path.rb +19 -0
  63. data/db/migrations/20120107124541_owner_id.rb +19 -0
  64. data/docs/recipe-interface-screenshot.png +0 -0
  65. data/lib/cutaneous.rb +11 -10
  66. data/lib/cutaneous/context_helper.rb +119 -14
  67. data/lib/cutaneous/preview_context.rb +4 -3
  68. data/lib/cutaneous/preview_renderer.rb +1 -6
  69. data/lib/cutaneous/publish_context.rb +1 -0
  70. data/lib/cutaneous/{first_pass_renderer.rb → publish_renderer.rb} +2 -6
  71. data/lib/cutaneous/publish_template.rb +62 -0
  72. data/lib/cutaneous/publish_token_parser.rb +8 -0
  73. data/lib/cutaneous/renderer.rb +4 -2
  74. data/lib/cutaneous/request_context.rb +1 -0
  75. data/lib/cutaneous/{second_pass_renderer.rb → request_renderer.rb} +2 -5
  76. data/lib/cutaneous/request_template.rb +11 -0
  77. data/lib/cutaneous/request_token_parser.rb +9 -0
  78. data/lib/cutaneous/token_parser.rb +125 -0
  79. data/lib/sequel/plugins/content_table_inheritance.rb +19 -12
  80. data/lib/sequel/plugins/scoped_table_name.rb +45 -0
  81. data/lib/spontaneous.rb +123 -126
  82. data/lib/spontaneous/box.rb +127 -47
  83. data/lib/spontaneous/box_style.rb +39 -22
  84. data/lib/spontaneous/change.rb +2 -2
  85. data/lib/spontaneous/cli.rb +1 -0
  86. data/lib/spontaneous/cli/base.rb +12 -4
  87. data/lib/spontaneous/cli/console.rb +72 -0
  88. data/lib/spontaneous/cli/server.rb +36 -12
  89. data/lib/spontaneous/cli/site.rb +175 -1
  90. data/lib/spontaneous/collections/box_set.rb +19 -1
  91. data/lib/spontaneous/collections/entry_set.rb +70 -22
  92. data/lib/spontaneous/collections/field_set.rb +3 -3
  93. data/lib/spontaneous/collections/prototype_set.rb +28 -0
  94. data/lib/spontaneous/config.rb +14 -5
  95. data/lib/spontaneous/content.rb +23 -24
  96. data/lib/spontaneous/errors.rb +20 -4
  97. data/lib/spontaneous/extensions/array.rb +2 -2
  98. data/lib/spontaneous/extensions/json.rb +1 -1
  99. data/lib/spontaneous/extensions/nil.rb +13 -0
  100. data/lib/spontaneous/facet.rb +61 -7
  101. data/lib/spontaneous/field_types.rb +1 -3
  102. data/lib/spontaneous/field_types/date_field.rb +1 -0
  103. data/lib/spontaneous/field_types/field.rb +32 -15
  104. data/lib/spontaneous/field_types/image_field.rb +138 -69
  105. data/lib/spontaneous/field_types/location_field.rb +59 -0
  106. data/lib/spontaneous/field_types/long_string_field.rb +13 -0
  107. data/lib/spontaneous/field_types/markdown_field.rb +2 -1
  108. data/lib/spontaneous/field_types/string_field.rb +2 -2
  109. data/lib/spontaneous/field_types/webvideo_field.rb +255 -0
  110. data/lib/spontaneous/generators/page/inline.html.cut +1 -1
  111. data/lib/spontaneous/generators/page/page.html.cut.tt +5 -4
  112. data/lib/spontaneous/generators/page/page.rb.tt +1 -5
  113. data/lib/spontaneous/generators/site.rb +1 -0
  114. data/lib/spontaneous/generators/site/.gitignore +2 -0
  115. data/lib/spontaneous/generators/site/Gemfile.tt +3 -5
  116. data/lib/spontaneous/generators/site/config/environment.rb.tt +1 -0
  117. data/lib/spontaneous/generators/site/config/environments/development.rb.tt +3 -1
  118. data/lib/spontaneous/generators/site/config/environments/production.rb.tt +3 -1
  119. data/lib/spontaneous/generators/site/config/indexes.rb.tt +23 -0
  120. data/lib/spontaneous/generators/site/public/css/{site.css → site.scss} +0 -0
  121. data/lib/spontaneous/generators/site/schema/box.rb.tt +5 -0
  122. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +11 -5
  123. data/lib/spontaneous/image_size.rb +0 -1
  124. data/lib/spontaneous/loader.rb +335 -218
  125. data/lib/spontaneous/logger.rb +7 -2
  126. data/lib/spontaneous/media.rb +64 -59
  127. data/lib/spontaneous/media/file.rb +74 -0
  128. data/lib/spontaneous/page.rb +47 -18
  129. data/lib/spontaneous/page_piece.rb +29 -27
  130. data/lib/spontaneous/paths.rb +0 -1
  131. data/lib/spontaneous/permissions.rb +3 -21
  132. data/lib/spontaneous/permissions/user.rb +5 -1
  133. data/lib/spontaneous/permissions/user_level.rb +6 -7
  134. data/lib/spontaneous/piece.rb +12 -7
  135. data/lib/spontaneous/plugins/aliases.rb +76 -31
  136. data/lib/spontaneous/plugins/allowed_types.rb +36 -37
  137. data/lib/spontaneous/plugins/application/facets.rb +7 -10
  138. data/lib/spontaneous/plugins/application/features.rb +17 -0
  139. data/lib/spontaneous/plugins/application/paths.rb +25 -31
  140. data/lib/spontaneous/plugins/application/render.rb +1 -5
  141. data/lib/spontaneous/plugins/application/serialisation.rb +2 -0
  142. data/lib/spontaneous/plugins/application/state.rb +6 -9
  143. data/lib/spontaneous/plugins/boxes.rb +65 -40
  144. data/lib/spontaneous/plugins/controllers.rb +22 -22
  145. data/lib/spontaneous/plugins/entries.rb +149 -150
  146. data/lib/spontaneous/plugins/entry.rb +50 -35
  147. data/lib/spontaneous/plugins/field/editor_class.rb +13 -0
  148. data/lib/spontaneous/plugins/fields.rb +41 -33
  149. data/lib/spontaneous/plugins/instance_code.rb +9 -9
  150. data/lib/spontaneous/plugins/layouts.rb +31 -35
  151. data/lib/spontaneous/plugins/media.rb +31 -32
  152. data/lib/spontaneous/plugins/page/formats.rb +56 -31
  153. data/lib/spontaneous/plugins/page/request.rb +15 -9
  154. data/lib/spontaneous/plugins/page_search.rb +30 -31
  155. data/lib/spontaneous/plugins/page_tree.rb +59 -12
  156. data/lib/spontaneous/plugins/paths.rb +84 -79
  157. data/lib/spontaneous/plugins/permissions.rb +26 -28
  158. data/lib/spontaneous/plugins/prototypes.rb +42 -37
  159. data/lib/spontaneous/plugins/publishing.rb +50 -94
  160. data/lib/spontaneous/plugins/render.rb +8 -16
  161. data/lib/spontaneous/plugins/schema_hierarchy.rb +20 -21
  162. data/lib/spontaneous/plugins/schema_id.rb +33 -25
  163. data/lib/spontaneous/plugins/schema_title.rb +3 -4
  164. data/lib/spontaneous/plugins/serialisation.rb +32 -35
  165. data/lib/spontaneous/plugins/site/features.rb +32 -0
  166. data/lib/spontaneous/plugins/site/instance.rb +3 -2
  167. data/lib/spontaneous/plugins/site/level.rb +18 -0
  168. data/lib/spontaneous/plugins/site/map.rb +2 -1
  169. data/lib/spontaneous/plugins/site/publishing.rb +56 -59
  170. data/lib/spontaneous/plugins/site/revisions.rb +24 -19
  171. data/lib/spontaneous/plugins/site/schema.rb +33 -0
  172. data/lib/spontaneous/plugins/site/search.rb +43 -0
  173. data/lib/spontaneous/plugins/site/selectors.rb +35 -30
  174. data/lib/spontaneous/plugins/site/storage.rb +63 -0
  175. data/lib/spontaneous/plugins/site_map.rb +23 -25
  176. data/lib/spontaneous/plugins/styles.rb +35 -43
  177. data/lib/spontaneous/plugins/supertype.rb +3 -1
  178. data/lib/spontaneous/plugins/visibility.rb +90 -83
  179. data/lib/spontaneous/prototypes/box_prototype.rb +55 -25
  180. data/lib/spontaneous/prototypes/field_prototype.rb +66 -19
  181. data/lib/spontaneous/prototypes/style_prototype.rb +2 -2
  182. data/lib/spontaneous/publishing.rb +1 -1
  183. data/lib/spontaneous/publishing/immediate.rb +128 -49
  184. data/lib/spontaneous/publishing/simultaneous.rb +70 -0
  185. data/lib/spontaneous/rack.rb +38 -26
  186. data/lib/spontaneous/rack/around_back.rb +3 -23
  187. data/lib/spontaneous/rack/around_preview.rb +3 -8
  188. data/lib/spontaneous/rack/assets.rb +7 -6
  189. data/lib/spontaneous/rack/authentication.rb +21 -0
  190. data/lib/spontaneous/rack/back.rb +310 -331
  191. data/lib/spontaneous/rack/cacheable_file.rb +27 -0
  192. data/lib/spontaneous/rack/cookie_authentication.rb +38 -0
  193. data/lib/spontaneous/rack/css.rb +43 -0
  194. data/lib/spontaneous/rack/event_source.rb +31 -0
  195. data/lib/spontaneous/rack/front.rb +30 -8
  196. data/lib/spontaneous/rack/helpers.rb +31 -0
  197. data/lib/spontaneous/rack/media.rb +22 -21
  198. data/lib/spontaneous/rack/public.rb +2 -1
  199. data/lib/spontaneous/rack/query_authentication.rb +35 -0
  200. data/lib/spontaneous/rack/reloader.rb +6 -3
  201. data/lib/spontaneous/rack/user_helpers.rb +28 -0
  202. data/lib/spontaneous/render.rb +64 -23
  203. data/lib/spontaneous/render/context_base.rb +143 -0
  204. data/lib/spontaneous/render/format.rb +24 -19
  205. data/lib/spontaneous/render/helpers.rb +14 -0
  206. data/lib/spontaneous/render/helpers/html_helper.rb +69 -0
  207. data/lib/spontaneous/render/helpers/script_helper.rb +17 -0
  208. data/lib/spontaneous/render/helpers/stylesheet_helper.rb +16 -0
  209. data/lib/spontaneous/render/preview_context.rb +8 -0
  210. data/lib/spontaneous/render/preview_renderer.rb +6 -0
  211. data/lib/spontaneous/render/publish_context.rb +22 -0
  212. data/lib/spontaneous/render/published_renderer.rb +12 -13
  213. data/lib/spontaneous/render/publishing_renderer.rb +3 -0
  214. data/lib/spontaneous/render/render_cache.rb +26 -0
  215. data/lib/spontaneous/render/renderer.rb +5 -1
  216. data/lib/spontaneous/render/request_context.rb +8 -0
  217. data/lib/spontaneous/schema.rb +56 -24
  218. data/lib/spontaneous/schema/schema_modification.rb +2 -2
  219. data/lib/spontaneous/schema/uid.rb +22 -106
  220. data/lib/spontaneous/schema/uid_map.rb +120 -0
  221. data/lib/spontaneous/search.rb +11 -0
  222. data/lib/spontaneous/search/compound_indexer.rb +26 -0
  223. data/lib/spontaneous/search/database.rb +72 -0
  224. data/lib/spontaneous/search/field.rb +95 -0
  225. data/lib/spontaneous/search/index.rb +184 -0
  226. data/lib/spontaneous/search/results.rb +34 -0
  227. data/lib/spontaneous/server.rb +5 -1
  228. data/lib/spontaneous/site.rb +56 -16
  229. data/lib/spontaneous/storage.rb +22 -0
  230. data/lib/spontaneous/storage/backend.rb +10 -0
  231. data/lib/spontaneous/storage/cloud.rb +104 -0
  232. data/lib/spontaneous/storage/local.rb +52 -0
  233. data/lib/spontaneous/style.rb +27 -9
  234. data/lib/spontaneous/version.rb +1 -1
  235. data/spontaneous.gemspec +719 -0
  236. data/test/disabled/test_slots.rb +1 -1
  237. data/test/experimental/test_cutaneous.rb +309 -0
  238. data/test/experimental/test_features.rb +129 -0
  239. data/test/fixtures/application/views/{index.erubis → index.erb} +0 -0
  240. data/test/fixtures/back/public/css/sass_include.scss +5 -0
  241. data/test/fixtures/back/public/css/sass_template.scss +4 -0
  242. data/test/fixtures/example_application/templates/client_project.html.cut +4 -4
  243. data/test/fixtures/example_application/templates/client_project/images.html.cut +1 -1
  244. data/test/fixtures/example_application/templates/client_projects.html.cut +2 -2
  245. data/test/fixtures/example_application/templates/inline_image.html.cut +1 -1
  246. data/test/fixtures/example_application/templates/layouts/home.html.cut +7 -7
  247. data/test/fixtures/example_application/templates/layouts/info.html.cut +1 -1
  248. data/test/fixtures/example_application/templates/layouts/project.html.cut +4 -4
  249. data/test/fixtures/example_application/templates/layouts/projects.html.cut +2 -2
  250. data/test/fixtures/example_application/templates/project.html.cut +2 -2
  251. data/test/fixtures/example_application/templates/project/inline.html.cut +3 -3
  252. data/test/fixtures/example_application/templates/project_image.html.cut +1 -1
  253. data/test/fixtures/example_application/templates/text.html.cut +1 -1
  254. data/test/fixtures/fields/youtube_api_response.xml +102 -0
  255. data/test/fixtures/layouts/layouts/custom4.html.cut +0 -0
  256. data/test/fixtures/plugins/schema_plugin/public/css/plugin.css +1 -0
  257. data/test/fixtures/plugins/schema_plugin/public/js/plugin.js +1 -0
  258. data/test/fixtures/plugins/schema_plugin/public/static.html +1 -0
  259. data/test/fixtures/plugins/schema_plugin/public/subdir/image.gif +1 -0
  260. data/test/fixtures/plugins/schema_plugin/public/subdir/include1.scss +3 -0
  261. data/test/fixtures/plugins/schema_plugin/public/subdir/sass.scss +4 -0
  262. data/test/fixtures/plugins/schema_plugin/public/subdir/sass/include2.scss +4 -0
  263. data/test/fixtures/public/templates/layouts/default.html.cut +1 -1
  264. data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -1
  265. data/test/fixtures/public/templates/layouts/default.rss.cut +1 -1
  266. data/test/fixtures/public/templates/layouts/dynamic.html.cut +1 -1
  267. data/test/fixtures/schema_modification/config/database.yml +0 -0
  268. data/test/fixtures/schema_modification/config/environment.rb +2 -0
  269. data/test/fixtures/schema_modification/schema/box.rb +1 -0
  270. data/test/fixtures/schema_modification/schema/custom_box.rb +1 -0
  271. data/test/fixtures/schema_modification/schema/page.rb +6 -0
  272. data/test/fixtures/schema_modification/schema/piece.rb +1 -0
  273. data/test/fixtures/search/config/database.yml +1 -0
  274. data/test/fixtures/search/config/indexes.rb +4 -0
  275. data/test/fixtures/serialisation/root_hash.yaml.erb +16 -20
  276. data/test/fixtures/storage/cloud/environment.rb +12 -0
  277. data/test/fixtures/storage/default/environment.rb +1 -0
  278. data/test/fixtures/templates/aliases/layouts/c_alias.html.cut +1 -1
  279. data/test/fixtures/templates/content/include.html.cut +1 -1
  280. data/test/fixtures/templates/content/include_dir.html.cut +1 -1
  281. data/test/fixtures/templates/content/included.epub.cut +1 -1
  282. data/test/fixtures/templates/content/included.html.cut +1 -1
  283. data/test/fixtures/templates/content/partial/included.html.cut +1 -1
  284. data/test/fixtures/templates/content/preprocess.html.cut +1 -1
  285. data/test/fixtures/templates/content/second.html.cut +1 -1
  286. data/test/fixtures/templates/content/template.epub.cut +1 -1
  287. data/test/fixtures/templates/content/template.html.cut +1 -1
  288. data/test/fixtures/templates/default_style_class.html.cut +1 -1
  289. data/test/fixtures/templates/engine/braces.html.cut +6 -0
  290. data/test/fixtures/templates/engine/multiline.html.cut +5 -0
  291. data/test/fixtures/templates/extended/grandparent.html.cut +7 -7
  292. data/test/fixtures/templates/extended/main.html.cut +5 -5
  293. data/test/fixtures/templates/extended/parent.html.cut +10 -8
  294. data/test/fixtures/templates/extended/partial.html.cut +1 -0
  295. data/test/fixtures/templates/extended/partial_with_locals.html.cut +1 -0
  296. data/test/fixtures/templates/extended/with_includes.html.cut +9 -0
  297. data/test/fixtures/templates/extended/with_includes_and_locals.html.cut +9 -0
  298. data/test/fixtures/templates/layouts/entries.html.cut +7 -7
  299. data/test/fixtures/templates/layouts/page_style.html.cut +1 -1
  300. data/test/fixtures/templates/layouts/params.html.cut +1 -1
  301. data/test/fixtures/templates/layouts/preview_render.html.cut +2 -2
  302. data/test/fixtures/templates/layouts/standard_page.html.cut +1 -1
  303. data/test/fixtures/templates/layouts/subdir_style.html.cut +1 -1
  304. data/test/fixtures/templates/layouts/template_params.html.cut +1 -1
  305. data/test/fixtures/templates/layouts/variables.html.cut +7 -0
  306. data/test/fixtures/templates/page_class/inline_style.html.cut +1 -1
  307. data/test/fixtures/templates/preview_render/variables.html.cut +1 -0
  308. data/test/fixtures/templates/publishing/templates/layouts/dynamic.html.cut +1 -0
  309. data/test/fixtures/templates/publishing/templates/layouts/dynamic.rtf.cut +1 -0
  310. data/test/fixtures/templates/publishing/templates/layouts/static.html.cut +1 -0
  311. data/test/fixtures/templates/template_class/anonymous_style.html.cut +2 -2
  312. data/test/fixtures/templates/template_class/complex_template.html.cut +4 -4
  313. data/test/fixtures/templates/template_class/complex_template.pdf.cut +4 -4
  314. data/test/fixtures/templates/template_class/default_template_style.html.cut +2 -2
  315. data/test/fixtures/templates/template_class/images_with_template.html.cut +3 -3
  316. data/test/fixtures/templates/template_class/slots_template.html.cut +3 -3
  317. data/test/fixtures/templates/template_class/slots_template.pdf.cut +3 -3
  318. data/test/fixtures/templates/template_class/this_template.epub.cut +1 -1
  319. data/test/fixtures/templates/template_class/this_template.html.cut +1 -1
  320. data/test/fixtures/templates/template_class/this_template.pdf.cut +1 -1
  321. data/test/fixtures/templates/with_default_style_class.html.cut +1 -1
  322. data/test/functional/test_application.rb +19 -15
  323. data/test/functional/test_back.rb +130 -98
  324. data/test/functional/test_front.rb +72 -28
  325. data/test/javascript/test_dom.rb +1 -1
  326. data/test/javascript/test_markdown.rb +1 -1
  327. data/test/slow/test_publishing.rb +94 -75
  328. data/test/slow/test_visibility.rb +47 -14
  329. data/test/test_helper.rb +30 -3
  330. data/test/ui/test_page_editing.rb +1 -1
  331. data/test/unit/test_alias.rb +200 -16
  332. data/test/unit/test_authentication.rb +26 -28
  333. data/test/unit/test_boxes.rb +146 -75
  334. data/test/unit/test_config.rb +42 -20
  335. data/test/unit/test_content.rb +156 -37
  336. data/test/unit/test_content_inheritance.rb +4 -2
  337. data/test/unit/test_extensions.rb +7 -1
  338. data/test/unit/test_fields.rb +558 -290
  339. data/test/{experimental → unit}/test_formats.rb +42 -3
  340. data/test/unit/test_generators.rb +3 -2
  341. data/test/unit/test_helpers.rb +54 -0
  342. data/test/unit/test_image_size.rb +1 -1
  343. data/test/unit/test_images.rb +51 -40
  344. data/test/unit/test_layouts.rb +21 -4
  345. data/test/unit/test_logger.rb +1 -1
  346. data/test/unit/test_media.rb +165 -7
  347. data/test/unit/test_page.rb +158 -27
  348. data/test/unit/test_permissions.rb +170 -187
  349. data/test/unit/test_piece.rb +27 -9
  350. data/test/unit/test_plugins.rb +153 -0
  351. data/test/unit/test_prototype_set.rb +60 -2
  352. data/test/unit/test_prototypes.rb +81 -20
  353. data/test/unit/test_render.rb +97 -9
  354. data/test/unit/test_schema.rb +167 -120
  355. data/test/unit/test_search.rb +588 -0
  356. data/test/unit/test_serialisation.rb +26 -10
  357. data/test/unit/test_site.rb +42 -25
  358. data/test/unit/test_storage.rb +88 -0
  359. data/test/unit/test_structure.rb +11 -5
  360. data/test/unit/test_styles.rb +64 -3
  361. data/test/unit/test_table_scoping.rb +76 -0
  362. data/test/unit/test_templates.rb +69 -31
  363. data/test/unit/test_type_hierarchy.rb +9 -1
  364. metadata +274 -208
  365. data/Gemfile.lock +0 -146
  366. data/application/css/min/54ee0ed3c7fac7632bd5c020d69e9a2503e0c88c.css +0 -1
  367. data/application/css/min/c256adc144e2bdd0b0539356b04eb62db01e1dc3.css +0 -1
  368. data/application/js/edit_dialogue.js +0 -137
  369. data/application/js/min/492a209de8ee955fa9c729a765377495001e11b1.js +0 -17
  370. data/application/js/min/80f684d77c940887a1d4a63e3a96102e993baa98.js +0 -88
  371. data/application/js/min/c7140ec9475e5bf868b901e0621338d7d162358b.js +0 -3
  372. data/application/js/min/f07f2bd6630ee31e1c2288ec223383d8f0658ba6.js +0 -2
  373. data/application/js/vendor/.DS_Store +0 -0
  374. data/application/js/vendor/JS.Class-2.1.5/src/command.js +0 -93
  375. data/application/js/vendor/JS.Class-2.1.5/src/comparable.js +0 -37
  376. data/application/js/vendor/JS.Class-2.1.5/src/constant_scope.js +0 -48
  377. data/application/js/vendor/JS.Class-2.1.5/src/core.js +0 -1060
  378. data/application/js/vendor/JS.Class-2.1.5/src/decorator.js +0 -50
  379. data/application/js/vendor/JS.Class-2.1.5/src/enumerable.js +0 -505
  380. data/application/js/vendor/JS.Class-2.1.5/src/forwardable.js +0 -22
  381. data/application/js/vendor/JS.Class-2.1.5/src/hash.js +0 -334
  382. data/application/js/vendor/JS.Class-2.1.5/src/linked_list.js +0 -114
  383. data/application/js/vendor/JS.Class-2.1.5/src/loader.js +0 -553
  384. data/application/js/vendor/JS.Class-2.1.5/src/method_chain.js +0 -172
  385. data/application/js/vendor/JS.Class-2.1.5/src/observable.js +0 -55
  386. data/application/js/vendor/JS.Class-2.1.5/src/package.js +0 -472
  387. data/application/js/vendor/JS.Class-2.1.5/src/proxy.js +0 -58
  388. data/application/js/vendor/JS.Class-2.1.5/src/ruby.js +0 -44
  389. data/application/js/vendor/JS.Class-2.1.5/src/set.js +0 -332
  390. data/application/js/vendor/JS.Class-2.1.5/src/stack_trace.js +0 -151
  391. data/application/js/vendor/JS.Class-2.1.5/src/state.js +0 -95
  392. data/application/js/vendor/JS.Class-2.1.5/src/stdlib.js +0 -2612
  393. data/application/js/vendor/jquery-1.4.2.min.js +0 -154
  394. data/application/js/vendor/jquery-1.4.3.min.js +0 -166
  395. data/application/js/vendor/jquery-1.5.1.min.js +0 -16
  396. data/application/js/vendor/jquery-1.5.1rc1.min.js +0 -24
  397. data/application/js/vendor/jquery-ui-1.8.6.custom.min.js +0 -265
  398. data/application/js/vendor/jquery-ui-1.8.custom.min.js +0 -106
  399. data/application/js/vendor/jquery.hotkeys-0.7.9.js +0 -248
  400. data/application/js/vendor/jquery.hotkeys-0.7.9.min.js +0 -19
  401. data/application/js/vendor/jsdiff.js +0 -169
  402. data/lib/cutaneous/first_pass_parser.rb +0 -23
  403. data/lib/cutaneous/parser_core.rb +0 -18
  404. data/lib/cutaneous/second_pass_parser.rb +0 -23
  405. data/lib/sequel/plugins/yajl_serialization.rb +0 -154
  406. data/lib/spontaneous/plugins.rb +0 -20
  407. data/lib/spontaneous/proxy_object.rb +0 -12
  408. data/lib/spontaneous/publishing/fire_and_forget.rb +0 -57
  409. data/lib/spontaneous/render/context.rb +0 -100
  410. data/spontaneous.gemspec.tmpl +0 -66
  411. data/test/experimental/test_plugins.rb +0 -64
  412. data/test/fixtures/templates/publishing/layouts/dynamic.html.cut +0 -1
  413. data/test/fixtures/templates/publishing/layouts/static.html.cut +0 -1
@@ -1,16 +1,21 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'test_helper'
3
+ require File.expand_path('../../test_helper', __FILE__)
4
4
 
5
5
 
6
6
  class ContentTest < MiniTest::Spec
7
7
  context "Content:" do
8
8
  setup do
9
- Spot::Schema.reset!
10
- class C < Content; end
9
+ @site = setup_site
10
+ class Piece < Spontaneous::Piece; end
11
+ class C < Piece; end
12
+ C.box :things
11
13
  end
14
+
12
15
  teardown do
13
- ContentTest.send(:remove_const, :C)
16
+ teardown_site
17
+ ContentTest.send(:remove_const, :Piece) rescue nil
18
+ ContentTest.send(:remove_const, :C) rescue nil
14
19
  end
15
20
  context "Content instances" do
16
21
  should "evaluate instance code" do
@@ -37,46 +42,61 @@ class ContentTest < MiniTest::Spec
37
42
 
38
43
  should "accept addition of child content" do
39
44
  e = C.new
40
- @instance << e
45
+ @instance.things << e
41
46
  @instance.pieces.length.should == 1
47
+ @instance.things.pieces.length.should == 1
42
48
  @instance.pieces.first.should == e
43
- @instance.pieces.first.container.should == @instance
44
- e.content_path.should == "#{@instance.id}"
45
- @instance.pieces.first.content_path.should == "#{@instance.id}"
49
+ @instance.pieces.first.container.should == @instance.things
50
+ @instance.pieces.first.owner.should == @instance
51
+ @instance.pieces.first.parent.should == @instance
52
+ e.visibility_path.should == "#{@instance.id}"
53
+ @instance.pieces.first.visibility_path.should == "#{@instance.id}"
46
54
  end
47
55
 
48
56
  should "accept addition of multiple children" do
49
57
  e = C.new
50
58
  f = C.new
51
- @instance << e
52
- @instance << f
59
+ @instance.things << e
60
+ @instance.things << f
53
61
  @instance.pieces.length.should == 2
62
+ @instance.things.pieces.length.should == 2
54
63
  @instance.pieces.first.should == e
64
+ @instance.things.pieces.first.should == e
55
65
  @instance.pieces.last.should == f
56
- @instance.pieces.first.container.should == @instance
57
- @instance.pieces.last.container.should == @instance
58
- @instance.pieces.first.content_path.should == "#{@instance.id}"
59
- @instance.pieces.last.content_path.should == "#{@instance.id}"
66
+ @instance.things.pieces.last.should == f
67
+ @instance.pieces.first.container.should == @instance.things
68
+ @instance.pieces.last.container.should == @instance.things
69
+ @instance.pieces.first.parent.should == @instance
70
+ @instance.pieces.last.parent.should == @instance
71
+ @instance.pieces.first.owner.should == @instance
72
+ @instance.pieces.last.owner.should == @instance
73
+ @instance.pieces.first.visibility_path.should == "#{@instance.id}"
74
+ @instance.pieces.last.visibility_path.should == "#{@instance.id}"
60
75
  end
61
76
 
62
77
  should "allow for a deep hierarchy" do
63
78
  e = C.new
64
79
  f = C.new
65
- @instance << e
66
- e << f
80
+ @instance.things << e
81
+ e.things << f
67
82
  @instance.pieces.length.should == 1
68
83
  @instance.pieces.first.should == e
69
- e.container.should == @instance
70
- e.content_path.should == "#{@instance.id}"
71
- f.container.id.should == e.id
72
- f.content_path.should == "#{@instance.id}.#{e.id}"
84
+ e.owner.id.should == @instance.id
85
+ e.parent.id.should == @instance.id
86
+ e.container.should == @instance.things
87
+ e.visibility_path.should == "#{@instance.id}"
88
+
89
+ f.owner.id.should == e.id
90
+ f.parent.id.should == e.id
91
+ f.container.should == e.things
92
+ f.visibility_path.should == "#{@instance.id}.#{e.id}"
73
93
  end
74
94
 
75
95
  should "persist hierarchy" do
76
96
  e = C.new
77
97
  f = C.new
78
- e << f
79
- @instance << e
98
+ e.things << f
99
+ @instance.things << e
80
100
  @instance.save
81
101
  e.save
82
102
  f.save
@@ -88,8 +108,12 @@ class ContentTest < MiniTest::Spec
88
108
  i.pieces.length.should == 1
89
109
  i.pieces.first.should == e
90
110
 
91
- e.container.should == i
92
- f.container.should == e
111
+ e.container.should == i.things
112
+ e.owner.should == i
113
+ e.parent.should == i
114
+ f.container.should == e.things
115
+ f.parent.should == e
116
+ f.owner.should == e
93
117
  e.entry.should == i.pieces.first
94
118
  f.entry.should == e.pieces.first
95
119
  e.pieces.first.should == f
@@ -98,8 +122,8 @@ class ContentTest < MiniTest::Spec
98
122
  should "have a list of child nodes" do
99
123
  e = C.new
100
124
  f = C.new
101
- e << f
102
- @instance << e
125
+ e.things << f
126
+ @instance.things << e
103
127
  @instance.save
104
128
  e.save
105
129
  f.save
@@ -111,6 +135,46 @@ class ContentTest < MiniTest::Spec
111
135
  e.pieces.should == [f]
112
136
  end
113
137
 
138
+ should "allow for testing of position" do
139
+ e = C.new
140
+ f = C.new
141
+ g = C.new
142
+ @instance.things << e
143
+ @instance.things << f
144
+ @instance.things << g
145
+ e.first?.should be_true
146
+ f.first?.should be_false
147
+ g.first?.should be_false
148
+ e.last?.should be_false
149
+ f.last?.should be_false
150
+ g.last?.should be_true
151
+ end
152
+
153
+ should "know their next neighbour" do
154
+ e = C.new
155
+ f = C.new
156
+ g = C.new
157
+ @instance.things << e
158
+ @instance.things << f
159
+ @instance.things << g
160
+ e.next.should == f
161
+ f.next.should == g
162
+ g.next.should be_nil
163
+ end
164
+
165
+ should "know their previous neighbour" do
166
+ e = C.new
167
+ f = C.new
168
+ g = C.new
169
+ @instance.things << e
170
+ @instance.things << f
171
+ @instance.things << g
172
+ e.previous.should be_nil
173
+ f.previous.should == e
174
+ g.previous.should == f
175
+ g.prev.should == f
176
+ end
177
+
114
178
  should "record the depth of the nodes" do
115
179
  a = C.new
116
180
  b = C.new
@@ -120,8 +184,8 @@ class ContentTest < MiniTest::Spec
120
184
  b.depth.should == 0
121
185
  c.depth.should == 0
122
186
 
123
- a << b
124
- b << c
187
+ a.things << b
188
+ b.things << c
125
189
 
126
190
  b.depth.should == 1
127
191
  c.depth.should == 2
@@ -136,9 +200,9 @@ class ContentTest < MiniTest::Spec
136
200
  @b = C.new(:label => 'b')
137
201
  @c = C.new(:label => 'c')
138
202
  @d = C.new(:label => 'd')
139
- @a << @b
140
- @a << @d
141
- @b << @c
203
+ @a.things << @b
204
+ @a.things << @d
205
+ @b.things << @c
142
206
  @a.save
143
207
  @b.save
144
208
  @c.save
@@ -164,20 +228,75 @@ class ContentTest < MiniTest::Spec
164
228
  C.all.map { |c| c.id }.sort.should == [@a, @d].map { |c| c.id }.sort
165
229
  end
166
230
 
167
- ## doesn't work due to
168
231
  should "work through pieces" do
169
- @a.pieces.first.destroy
232
+ @a.things.length.should == 2
233
+ @a.things.first.destroy
170
234
  C.count.should == 2
171
- @a.pieces.length.should == 1
235
+ @a.things.length.should == 1
236
+ end
237
+ end
238
+
239
+ context "Moving" do
240
+ setup do
241
+ C.delete
242
+ @r = C.new(:label => 'r')
243
+ @a = C.new(:label => 'a')
244
+ @b = C.new(:label => 'b')
245
+ @c = C.new(:label => 'c')
246
+ @d = C.new(:label => 'd')
247
+ @r.things << @a
248
+ @r.things << @c
249
+ @a.things << @b
250
+ @c.things << @d
251
+ [@r, @a, @b, @c, @d].each { |c| c.save; c.reload }
252
+ end
253
+
254
+ teardown do
255
+ C.delete
256
+ end
257
+
258
+ should "default to adding at the end" do
259
+ @b.parent.should == @a
260
+ @r.things.adopt(@b)
261
+ @b.reload
262
+ @r.reload
263
+ @b.parent.should == @r
264
+ @b.container.should == @r.things
265
+ @b.depth.should == 1
266
+ @a.reload
267
+ @a.things.count.should == 0
268
+ @r.reload
269
+ @r.things.last.should == @b
270
+ end
271
+
272
+ should "allow for adding in any position" do
273
+ @b.parent.should == @a
274
+ @r.things.adopt(@b, 1)
275
+ @b.reload
276
+ @r.reload
277
+ @b.parent.should == @r
278
+ @b.container.should == @r.things
279
+ @b.depth.should == 1
280
+ @a.reload
281
+ @a.things.count.should == 0
282
+ @r.reload
283
+ @r.things[1].should == @b
284
+ @r.things.adopt(@d, 0)
285
+ @d.reload
286
+ @r.reload
287
+ @r.things[0].should == @d
288
+ end
289
+
290
+ should "re-set the visibility path" do
291
+ @r.things.adopt(@b)
292
+ @b.reload
293
+ @b.visibility_path.should == @r.id.to_s
172
294
  end
173
295
  end
174
296
 
175
297
 
176
298
  context "identity map" do
177
299
  setup do
178
- instance = Spontaneous::Site.instantiate(Spontaneous.root, :test, :back)
179
- Spontaneous.instance = instance
180
- Spontaneous.instance.database = DB
181
300
 
182
301
  Content.delete
183
302
  Content.delete_all_revisions!
@@ -1,13 +1,14 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'test_helper'
3
+ require File.expand_path('../../test_helper', __FILE__)
4
4
 
5
5
 
6
6
  class ContentInheritanceTest < MiniTest::Spec
7
7
 
8
8
  context "Single table inheritance" do
9
9
  setup do
10
- Spot::Schema.reset!
10
+ @site = setup_site
11
+
11
12
  Content.delete
12
13
  class ::Page < Spontaneous::Page; end
13
14
  class ::PageClass1 < ::Page; end
@@ -44,6 +45,7 @@ class ContentInheritanceTest < MiniTest::Spec
44
45
  ].each do |klass|
45
46
  Object.send(:remove_const, klass)
46
47
  end
48
+ teardown_site
47
49
  end
48
50
 
49
51
  should "aaa correctly type subclasses found via Content" do
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'test_helper'
3
+ require File.expand_path('../../test_helper', __FILE__)
4
4
 
5
5
 
6
6
  class ExtensionsTest < MiniTest::Spec
@@ -10,5 +10,11 @@ class ExtensionsTest < MiniTest::Spec
10
10
  ("/this" / "/that").should == "/this/that"
11
11
  end
12
12
  end
13
+
14
+ context "Nil" do
15
+ should "always return the argument for the slash switch" do
16
+ (nil / "something").should == "something"
17
+ end
18
+ end
13
19
  end
14
20
 
@@ -1,392 +1,660 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'test_helper'
3
+ require File.expand_path('../../test_helper', __FILE__)
4
4
 
5
5
  class FieldsTest < MiniTest::Spec
6
6
 
7
7
  def setup
8
+ @site = setup_site
8
9
  Site.publishing_method = :immediate
9
10
  end
10
- context "New content instances" do
11
- setup do
12
- @content_class = Class.new(Piece) do
13
- field :title, :default => "Magic"
14
- field :thumbnail, :image
15
- end
16
- @instance = @content_class.new
17
- end
18
-
19
- should "have fields with values defined by prototypes" do
20
- f = @instance.fields[:title]
21
- f.class.should == Spontaneous::FieldTypes::StringField
22
- f.value.should == "Magic"
23
- end
24
-
25
- should "have shortcut access methods to fields" do
26
- @instance.fields.thumbnail.should == @instance.fields[:thumbnail]
27
- end
28
- should "have a shortcut setter on the Content fields" do
29
- @instance.fields.title = "New Title"
30
- end
31
-
32
- should "have a shortcut getter on the Content instance itself" do
33
- @instance.title.should == @instance.fields[:title]
34
- end
35
11
 
36
- should "have a shortcut setter on the Content instance itself" do
37
- @instance.title = "Boing!"
38
- @instance.fields[:title].value.should == "Boing!"
39
- end
12
+ def teardown
13
+ teardown_site
40
14
  end
41
15
 
42
- context "Overwriting fields" do
43
- setup do
44
- @class1 = Class.new(Piece) do
45
- field :title, :string, :default => "One"
46
- field :date, :string
47
- end
48
- @class2 = Class.new(@class1) do
49
- field :title, :image, :default => "Two"
50
- end
51
- @instance = @class2.new
52
- end
53
-
54
- should "overwrite field definitions" do
55
- @class2.fields.first.name.should == :title
56
- @class2.fields.last.name.should == :date
57
- @class2.fields.length.should == 2
58
- @instance.title.class.should == Spontaneous::FieldTypes::ImageField
59
- @instance.title.value.to_s.should == "Two"
60
- end
61
- end
62
- context "Field Prototypes" do
16
+ context "Fields" do
63
17
  setup do
64
- @content_class = Class.new(Piece) do
65
- field :title
66
- field :synopsis, :string
67
- end
68
- @content_class.field :complex, :image, :default => "My default", :comment => "Use this to"
69
- end
70
-
71
- should "be creatable with just a field name" do
72
- @content_class.field_prototypes[:title].must_be_instance_of(Spontaneous::Prototypes::FieldPrototype)
73
- @content_class.field_prototypes[:title].name.should == :title
74
- end
75
-
76
- should "work with just a name & options" do
77
- @content_class.field :minimal, :default => "Small"
78
- @content_class.field_prototypes[:minimal].name.should == :minimal
79
- @content_class.field_prototypes[:minimal].default.should == "Small"
80
- end
81
- should "map :string type to FieldTypes::Text" do
82
- @content_class.field_prototypes[:synopsis].field_class.should == Spontaneous::FieldTypes::StringField
18
+ class ::Page < Spontaneous::Page; end
19
+ class ::Piece < Spontaneous::Piece; end
83
20
  end
84
21
 
85
- should "be listable" do
86
- @content_class.field_names.should == [:title, :synopsis, :complex]
87
- end
88
-
89
- should "be testable for existance" do
90
- @content_class.field?(:title).should be_true
91
- @content_class.field?(:synopsis).should be_true
92
- @content_class.field?(:non_existant).should be_false
93
- i = @content_class.new
94
- i.field?(:title).should be_true
95
- i.field?(:non_existant).should be_false
22
+ teardown do
23
+ Object.send(:remove_const, :Page)
24
+ Object.send(:remove_const, :Piece)
96
25
  end
97
26
 
98
-
99
- context "default values" do
27
+ context "New content instances" do
100
28
  setup do
101
- @prototype = @content_class.field_prototypes[:title]
29
+ @content_class = Class.new(Piece) do
30
+ field :title, :default => "Magic"
31
+ field :thumbnail, :image
32
+ end
33
+ @instance = @content_class.new
102
34
  end
103
35
 
104
- should "default to basic string class" do
105
- @prototype.field_class.should == Spontaneous::FieldTypes::StringField
36
+ should "have fields with values defined by prototypes" do
37
+ f = @instance.fields[:title]
38
+ assert f.class < Spontaneous::FieldTypes::StringField
39
+ f.value.should == "Magic"
106
40
  end
107
41
 
108
- should "default to a value of ''" do
109
- @prototype.default.should == ""
42
+ should "have shortcut access methods to fields" do
43
+ @instance.fields.thumbnail.should == @instance.fields[:thumbnail]
44
+ end
45
+ should "have a shortcut setter on the Content fields" do
46
+ @instance.fields.title = "New Title"
110
47
  end
111
48
 
112
- should "match name to type if sensible" do
113
- content_class = Class.new(Piece) do
114
- field :image
115
- field :date
116
- field :chunky
117
- end
49
+ should "have a shortcut getter on the Content instance itself" do
50
+ @instance.title.should == @instance.fields[:title]
51
+ end
118
52
 
119
- content_class.field_prototypes[:image].field_class.should == Spontaneous::FieldTypes::ImageField
120
- content_class.field_prototypes[:date].field_class.should == Spontaneous::FieldTypes::DateField
121
- content_class.field_prototypes[:chunky].field_class.should == Spontaneous::FieldTypes::StringField
53
+ should "have a shortcut setter on the Content instance itself" do
54
+ @instance.title = "Boing!"
55
+ @instance.fields[:title].value.should == "Boing!"
122
56
  end
123
57
  end
124
58
 
125
- context "Field titles" do
59
+ context "Overwriting fields" do
126
60
  setup do
127
- @content_class = Class.new(Piece) do
128
- field :title
129
- field :having_fun_yet
130
- field :synopsis, :title => "Custom Title"
131
- field :description, :title => "Simple Description"
61
+ @class1 = Class.new(Piece) do
62
+ field :title, :string, :default => "One"
63
+ field :date, :string
64
+ end
65
+ @class2 = Class.new(@class1) do
66
+ field :title, :image, :default => "Two", :title => "Two"
132
67
  end
133
- @title = @content_class.field_prototypes[:title]
134
- @having_fun = @content_class.field_prototypes[:having_fun_yet]
135
- @synopsis = @content_class.field_prototypes[:synopsis]
136
- @description = @content_class.field_prototypes[:description]
68
+ @class3 = Class.new(@class2) do
69
+ field :date, :image, :default => "Three", :title => "Three"
70
+ end
71
+ @instance = @class2.new
137
72
  end
138
73
 
139
- should "default to a sensible title" do
140
- @title.title.should == "Title"
141
- @having_fun.title.should == "Having Fun Yet"
142
- @synopsis.title.should == "Custom Title"
143
- @description.title.should == "Simple Description"
74
+ should "overwrite field definitions" do
75
+ @class2.fields.first.name.should == :title
76
+ @class2.fields.last.name.should == :date
77
+ @class2.fields.length.should == 2
78
+ @class2.fields.title.schema_id.should == @class1.fields.title.schema_id
79
+ @class2.fields.title.title.should == "Two"
80
+ @class2.fields.title.title.should == "Two"
81
+ @class3.fields.date.title.should == "Three"
82
+ @class3.fields.date.schema_id.should == @class1.fields.date.schema_id
83
+ assert @instance.title.class < Spontaneous::FieldTypes::ImageField
84
+ @instance.title.value.to_s.should == "Two"
85
+ instance1 = @class1.new
86
+ instance3 = @class3.new
87
+ @instance.title.schema_id.should == instance1.title.schema_id
88
+ instance1.title.schema_id.should == instance3.title.schema_id
144
89
  end
145
90
  end
146
- context "option parsing" do
91
+ context "Field Prototypes" do
147
92
  setup do
148
- @prototype = @content_class.field_prototypes[:complex]
93
+ @content_class = Class.new(Piece) do
94
+ field :title
95
+ field :synopsis, :string
96
+ end
97
+ @content_class.field :complex, :image, :default => "My default", :comment => "Use this to"
149
98
  end
150
99
 
151
- should "parse field class" do
152
- @prototype.field_class.should == Spontaneous::FieldTypes::ImageField
100
+ should "be creatable with just a field name" do
101
+ @content_class.field_prototypes[:title].must_be_instance_of(Spontaneous::Prototypes::FieldPrototype)
102
+ @content_class.field_prototypes[:title].name.should == :title
153
103
  end
154
104
 
155
- should "parse default value" do
156
- @prototype.default.should == "My default"
105
+ should "work with just a name & options" do
106
+ @content_class.field :minimal, :default => "Small"
107
+ @content_class.field_prototypes[:minimal].name.should == :minimal
108
+ @content_class.field_prototypes[:minimal].default.should == "Small"
109
+ end
110
+ should "map :string type to FieldTypes::Text" do
111
+ assert @content_class.field_prototypes[:synopsis].instance_class < Spontaneous::FieldTypes::StringField
157
112
  end
158
113
 
159
- should "parse ui comment" do
160
- @prototype.comment.should == "Use this to"
114
+ should "be listable" do
115
+ @content_class.field_names.should == [:title, :synopsis, :complex]
161
116
  end
162
- end
163
117
 
164
- context "sub-classes" do
165
- setup do
166
- @subclass = Class.new(@content_class) do
167
- field :child_field
118
+ should "be testable for existance" do
119
+ @content_class.field?(:title).should be_true
120
+ @content_class.field?(:synopsis).should be_true
121
+ @content_class.field?(:non_existant).should be_false
122
+ i = @content_class.new
123
+ i.field?(:title).should be_true
124
+ i.field?(:non_existant).should be_false
125
+ end
126
+
127
+
128
+ context "default values" do
129
+ setup do
130
+ @prototype = @content_class.field_prototypes[:title]
131
+ end
132
+
133
+ should "default to basic string class" do
134
+ assert @prototype.field_class < Spontaneous::FieldTypes::StringField
168
135
  end
169
- @subsubclass = Class.new(@subclass) do
170
- field :distant_relation
136
+
137
+ should "default to a value of ''" do
138
+ @prototype.default.should == ""
171
139
  end
172
- end
173
140
 
174
- should "inherit super class's field prototypes" do
175
- @subclass.field_names.should == [:title, :synopsis, :complex, :child_field]
176
- @subsubclass.field_names.should == [:title, :synopsis, :complex, :child_field, :distant_relation]
141
+ should "match name to type if sensible" do
142
+ content_class = Class.new(Piece) do
143
+ field :image
144
+ field :date
145
+ field :chunky
146
+ end
147
+
148
+ assert content_class.field_prototypes[:image].field_class < Spontaneous::FieldTypes::ImageField
149
+ assert content_class.field_prototypes[:date].field_class < Spontaneous::FieldTypes::DateField
150
+ assert content_class.field_prototypes[:chunky].field_class < Spontaneous::FieldTypes::StringField
151
+ end
177
152
  end
178
153
 
179
- should "deal intelligently with manual setting of field order" do
180
- @reordered_class = Class.new(@subsubclass) do
181
- field_order :child_field, :complex
154
+ context "Field titles" do
155
+ setup do
156
+ @content_class = Class.new(Piece) do
157
+ field :title
158
+ field :having_fun_yet
159
+ field :synopsis, :title => "Custom Title"
160
+ field :description, :title => "Simple Description"
161
+ end
162
+ @title = @content_class.field_prototypes[:title]
163
+ @having_fun = @content_class.field_prototypes[:having_fun_yet]
164
+ @synopsis = @content_class.field_prototypes[:synopsis]
165
+ @description = @content_class.field_prototypes[:description]
166
+ end
167
+
168
+ should "default to a sensible title" do
169
+ @title.title.should == "Title"
170
+ @having_fun.title.should == "Having Fun Yet"
171
+ @synopsis.title.should == "Custom Title"
172
+ @description.title.should == "Simple Description"
182
173
  end
183
- @reordered_class.field_names.should == [:child_field, :complex, :title, :synopsis, :distant_relation]
184
174
  end
185
- end
186
- end
175
+ context "option parsing" do
176
+ setup do
177
+ @prototype = @content_class.field_prototypes[:complex]
178
+ end
187
179
 
188
- context "Values" do
189
- setup do
190
- @field_class = Class.new(FieldTypes::Field) do
191
- def outputs
192
- [:html, :plain, :fancy]
180
+ should "parse field class" do
181
+ assert @prototype.field_class < Spontaneous::FieldTypes::ImageField
182
+ end
183
+
184
+ should "parse default value" do
185
+ @prototype.default.should == "My default"
193
186
  end
194
- def generate_html(value)
195
- "<#{value}>"
187
+
188
+ should "parse ui comment" do
189
+ @prototype.comment.should == "Use this to"
190
+ end
191
+ end
192
+
193
+ context "sub-classes" do
194
+ setup do
195
+ @subclass = Class.new(@content_class) do
196
+ field :child_field
197
+ end
198
+ @subsubclass = Class.new(@subclass) do
199
+ field :distant_relation
200
+ end
196
201
  end
197
- def generate_plain(value)
198
- "*#{value}*"
202
+
203
+ should "inherit super class's field prototypes" do
204
+ @subclass.field_names.should == [:title, :synopsis, :complex, :child_field]
205
+ @subsubclass.field_names.should == [:title, :synopsis, :complex, :child_field, :distant_relation]
199
206
  end
200
207
 
201
- def generate(output, value)
202
- case output
203
- when :fancy
204
- "#{value}!"
205
- else
206
- value
208
+ should "deal intelligently with manual setting of field order" do
209
+ @reordered_class = Class.new(@subsubclass) do
210
+ field_order :child_field, :complex
207
211
  end
212
+ @reordered_class.field_names.should == [:child_field, :complex, :title, :synopsis, :distant_relation]
208
213
  end
209
214
  end
210
- @field = @field_class.new
211
215
  end
212
216
 
213
- should "be transformed by the update method" do
214
- @field.value = "Hello"
215
- @field.value.should == "<Hello>"
216
- @field.value(:html).should == "<Hello>"
217
- @field.value(:plain).should == "*Hello*"
218
- @field.value(:fancy).should == "Hello!"
219
- @field.unprocessed_value.should == "Hello"
220
- end
217
+ context "Values" do
218
+ setup do
219
+ @field_class = Class.new(FieldTypes::Field) do
220
+ def outputs
221
+ [:html, :plain, :fancy]
222
+ end
223
+ def generate_html(value)
224
+ "<#{value}>"
225
+ end
226
+ def generate_plain(value)
227
+ "*#{value}*"
228
+ end
221
229
 
222
- should "appear in the to_s method" do
223
- @field.value = "String"
224
- @field.to_s.should == "<String>"
225
- @field.to_s(:html).should == "<String>"
226
- @field.to_s(:plain).should == "*String*"
227
- end
230
+ def generate(output, value)
231
+ case output
232
+ when :fancy
233
+ "#{value}!"
234
+ else
235
+ value
236
+ end
237
+ end
238
+ end
239
+ @field = @field_class.new
240
+ end
228
241
 
229
- should "escape ampersands by default" do
230
- field_class = Class.new(FieldTypes::StringField) do
242
+ should "be transformed by the update method" do
243
+ @field.value = "Hello"
244
+ @field.value.should == "<Hello>"
245
+ @field.value(:html).should == "<Hello>"
246
+ @field.value(:plain).should == "*Hello*"
247
+ @field.value(:fancy).should == "Hello!"
248
+ @field.unprocessed_value.should == "Hello"
231
249
  end
232
- field = field_class.new
233
- field.value = "Hello & Welcome"
234
- field.value(:html).should == "Hello &amp; Welcome"
235
- field.value(:plain).should == "Hello & Welcome"
236
- end
237
250
 
238
- should "not process values coming from db" do
239
- content_class = Class.new(Piece)
251
+ should "appear in the to_s method" do
252
+ @field.value = "String"
253
+ @field.to_s.should == "<String>"
254
+ @field.to_s(:html).should == "<String>"
255
+ @field.to_s(:plain).should == "*String*"
256
+ end
240
257
 
241
- content_class.field :title do
242
- def generate_html(value)
243
- "<#{value}>"
258
+ should "escape ampersands by default" do
259
+ field_class = Class.new(FieldTypes::StringField) do
244
260
  end
261
+ field = field_class.new
262
+ field.value = "Hello & Welcome"
263
+ field.value(:html).should == "Hello &amp; Welcome"
264
+ field.value(:plain).should == "Hello & Welcome"
245
265
  end
246
- instance = content_class.new
247
- instance.fields.title = "Monkey"
248
- instance.save
249
266
 
250
- new_content_class = Class.new(Piece)
251
- new_content_class.field :title do
252
- def generate_html(value)
253
- "*#{value}*"
267
+ should "not process values coming from db" do
268
+ ContentClass1 = Class.new(Piece)
269
+
270
+ ContentClass1.field :title do
271
+ def generate_html(value)
272
+ "<#{value}>"
273
+ end
254
274
  end
255
- end
256
- instance = new_content_class[instance.id]
257
- instance.fields.title.value.should == "<Monkey>"
258
- end
259
- end
275
+ instance = ContentClass1.new
276
+ instance.fields.title = "Monkey"
277
+ instance.save
260
278
 
261
- context "field instances" do
262
- setup do
263
- ::CC = Class.new(Piece) do
264
- field :title, :default => "Magic" do
279
+ ContentClass2 = Class.new(Piece)
280
+ ContentClass2.field :title do
265
281
  def generate_html(value)
266
282
  "*#{value}*"
267
283
  end
268
284
  end
285
+ instance = ContentClass2[instance.id]
286
+ instance.fields.title.value.should == "<Monkey>"
287
+ FieldsTest.send(:remove_const, :ContentClass1)
288
+ FieldsTest.send(:remove_const, :ContentClass2)
269
289
  end
270
- @instance = CC.new
271
290
  end
272
291
 
273
- teardown do
274
- Object.send(:remove_const, :CC)
275
- end
292
+ context "field instances" do
293
+ setup do
294
+ ::CC = Class.new(Piece) do
295
+ field :title, :default => "Magic" do
296
+ def generate_html(value)
297
+ "*#{value}*"
298
+ end
299
+ end
300
+ end
301
+ @instance = CC.new
302
+ end
276
303
 
277
- should "have a link back to their owner" do
278
- @instance.fields.title.owner.should == @instance
279
- end
304
+ teardown do
305
+ Object.send(:remove_const, :CC)
306
+ end
280
307
 
281
- should "eval blocks from prototype defn" do
282
- f = @instance.fields.title
283
- f.value = "Boo"
284
- f.value.should == "*Boo*"
285
- f.unprocessed_value.should == "Boo"
286
- end
308
+ should "have a link back to their owner" do
309
+ @instance.fields.title.owner.should == @instance
310
+ end
287
311
 
288
- should "have a reference to their prototype" do
289
- f = @instance.fields.title
290
- f.prototype.should == CC.field_prototypes[:title]
291
- end
292
- end
312
+ should "eval blocks from prototype defn" do
313
+ f = @instance.fields.title
314
+ f.value = "Boo"
315
+ f.value.should == "*Boo*"
316
+ f.unprocessed_value.should == "Boo"
317
+ end
293
318
 
294
- context "Field value persistence" do
295
- setup do
296
- class ::PersistedField < Piece
297
- field :title, :default => "Magic"
319
+ should "have a reference to their prototype" do
320
+ f = @instance.fields.title
321
+ f.prototype.should == CC.field_prototypes[:title]
298
322
  end
299
- end
300
- teardown do
301
- Object.send(:remove_const, :PersistedField)
302
- end
303
323
 
304
- should "work" do
305
- instance = ::PersistedField.new
306
- instance.fields.title.value = "Changed"
307
- instance.save
308
- id = instance.id
309
- instance = ::PersistedField[id]
310
- instance.fields.title.value.should == "Changed"
324
+ should "return the item which isnt empty when using the / method" do
325
+ a = CC.new(:title => "")
326
+ b = CC.new(:title => "b")
327
+ (a.title / b.title).should == b.title
328
+ a.title = "a"
329
+ (a.title / b.title).should == a.title
330
+ end
331
+ should "return the item which isnt empty when using the | method" do
332
+ a = CC.new(:title => "")
333
+ b = CC.new(:title => "b")
334
+ (a.title | b.title).should == b.title
335
+ a.title = "a"
336
+ (a.title | b.title).should == a.title
337
+ end
311
338
  end
312
- end
313
339
 
314
- context "Value version" do
315
- setup do
316
- class ::PersistedField < Piece
317
- field :title, :default => "Magic"
340
+ context "Field value persistence" do
341
+ setup do
342
+ class ::PersistedField < Piece
343
+ field :title, :default => "Magic"
344
+ end
345
+ end
346
+ teardown do
347
+ Object.send(:remove_const, :PersistedField)
318
348
  end
319
- end
320
- teardown do
321
- Object.send(:remove_const, :PersistedField)
322
- end
323
349
 
324
- should "be increased after a change" do
325
- instance = ::PersistedField.new
326
- instance.fields.title.version.should == 0
327
- instance.fields.title.value = "Changed"
328
- instance.save
329
- instance = ::PersistedField[instance.id]
330
- instance.fields.title.value.should == "Changed"
331
- instance.fields.title.version.should == 1
350
+ should "work" do
351
+ instance = ::PersistedField.new
352
+ instance.fields.title.value = "Changed"
353
+ instance.save
354
+ id = instance.id
355
+ instance = ::PersistedField[id]
356
+ instance.fields.title.value.should == "Changed"
357
+ end
332
358
  end
333
359
 
334
- should "not be increased if the value remains constant" do
335
- instance = ::PersistedField.new
336
- instance.fields.title.version.should == 0
337
- instance.fields.title.value = "Changed"
338
- instance.save
339
- instance = ::PersistedField[instance.id]
340
- instance.fields.title.value = "Changed"
341
- instance.save
342
- instance = ::PersistedField[instance.id]
343
- instance.fields.title.value.should == "Changed"
344
- instance.fields.title.version.should == 1
345
- instance.fields.title.value = "Changed!"
346
- instance.save
347
- instance = ::PersistedField[instance.id]
348
- instance.fields.title.version.should == 2
349
- end
350
- end
360
+ context "Value version" do
361
+ setup do
362
+ class ::PersistedField < Piece
363
+ field :title, :default => "Magic"
364
+ end
365
+ end
366
+ teardown do
367
+ Object.send(:remove_const, :PersistedField)
368
+ end
351
369
 
352
- context "Available output formats" do
353
- should "include HTML & PDF and default to default value" do
354
- f = FieldTypes::Field.new
355
- f.value = "Value"
356
- f.to_html.should == "Value"
357
- f.to_pdf.should == "Value"
358
- end
359
- end
370
+ should "be increased after a change" do
371
+ instance = ::PersistedField.new
372
+ instance.fields.title.version.should == 0
373
+ instance.fields.title.value = "Changed"
374
+ instance.save
375
+ instance = ::PersistedField[instance.id]
376
+ instance.fields.title.value.should == "Changed"
377
+ instance.fields.title.version.should == 1
378
+ end
360
379
 
361
- context "Markdown fields" do
362
- setup do
363
- class ::MarkdownContent < Piece
364
- field :text1, :markdown
365
- field :text2, :text
380
+ should "not be increased if the value remains constant" do
381
+ instance = ::PersistedField.new
382
+ instance.fields.title.version.should == 0
383
+ instance.fields.title.value = "Changed"
384
+ instance.save
385
+ instance = ::PersistedField[instance.id]
386
+ instance.fields.title.value = "Changed"
387
+ instance.save
388
+ instance = ::PersistedField[instance.id]
389
+ instance.fields.title.value.should == "Changed"
390
+ instance.fields.title.version.should == 1
391
+ instance.fields.title.value = "Changed!"
392
+ instance.save
393
+ instance = ::PersistedField[instance.id]
394
+ instance.fields.title.version.should == 2
366
395
  end
367
- @instance = MarkdownContent.new
368
396
  end
369
- teardown do
370
- Object.send(:remove_const, :MarkdownContent)
397
+
398
+ context "Available output formats" do
399
+ should "include HTML & PDF and default to default value" do
400
+ f = FieldTypes::Field.new
401
+ f.value = "Value"
402
+ f.to_html.should == "Value"
403
+ f.to_pdf.should == "Value"
404
+ end
371
405
  end
372
406
 
373
- should "be available as the :markdown type" do
374
- MarkdownContent.field_prototypes[:text1].field_class.should == Spontaneous::FieldTypes::MarkdownField
407
+ context "Markdown fields" do
408
+ setup do
409
+ class ::MarkdownContent < Piece
410
+ field :text1, :markdown
411
+ field :text2, :text
412
+ end
413
+ @instance = MarkdownContent.new
414
+ end
415
+ teardown do
416
+ Object.send(:remove_const, :MarkdownContent)
417
+ end
418
+
419
+ should "be available as the :markdown type" do
420
+ assert MarkdownContent.field_prototypes[:text1].field_class < Spontaneous::FieldTypes::MarkdownField
421
+ end
422
+ should "be available as the :text type" do
423
+ assert MarkdownContent.field_prototypes[:text2].field_class < Spontaneous::FieldTypes::MarkdownField
424
+ end
425
+
426
+ should "process input into HTML" do
427
+ @instance.text1 = "*Hello* **World**"
428
+ @instance.text1.value.should == "<p><em>Hello</em> <strong>World</strong></p>\n"
429
+ end
430
+
431
+ should "use more sensible linebreaks" do
432
+ @instance.text1 = "With\nLinebreak"
433
+ @instance.text1.value.should == "<p>With<br />\nLinebreak</p>\n"
434
+ @instance.text2 = "With \nLinebreak"
435
+ @instance.text2.value.should == "<p>With<br />\nLinebreak</p>\n"
436
+ end
375
437
  end
376
- should "be available as the :text type" do
377
- MarkdownContent.field_prototypes[:text2].field_class.should == Spontaneous::FieldTypes::MarkdownField
438
+
439
+ context "Editor classes" do
440
+ should "be defined in base types" do
441
+ base_class = Spontaneous::FieldTypes::ImageField
442
+ base_class.editor_class.should == "Spontaneous.FieldTypes.ImageField"
443
+ base_class = Spontaneous::FieldTypes::DateField
444
+ base_class.editor_class.should == "Spontaneous.FieldTypes.DateField"
445
+ base_class = Spontaneous::FieldTypes::MarkdownField
446
+ base_class.editor_class.should == "Spontaneous.FieldTypes.MarkdownField"
447
+ base_class = Spontaneous::FieldTypes::StringField
448
+ base_class.editor_class.should == "Spontaneous.FieldTypes.StringField"
449
+ end
450
+
451
+ should "be inherited in subclasses" do
452
+ base_class = Spontaneous::FieldTypes::ImageField
453
+ @field_class = Class.new(base_class)
454
+ @field_class.stubs(:name).returns("CustomField")
455
+ @field_class.editor_class.should == base_class.editor_class
456
+ @field_class2 = Class.new(@field_class)
457
+ @field_class2.stubs(:name).returns("CustomField2")
458
+ @field_class2.editor_class.should == base_class.editor_class
459
+ end
460
+ should "correctly defined by field prototypes" do
461
+ base_class = Spontaneous::FieldTypes::ImageField
462
+ class ::CustomField < Spontaneous::FieldTypes::ImageField
463
+ self.register(:custom)
464
+ end
465
+
466
+ class ::CustomContent < Spontaneous::Piece
467
+ field :custom
468
+ end
469
+ assert CustomContent.fields.custom.instance_class < CustomField
470
+
471
+ CustomContent.fields.custom.instance_class.editor_class.should == Spontaneous::FieldTypes::ImageField.editor_class
472
+
473
+ Object.send(:remove_const, :CustomContent)
474
+ Object.send(:remove_const, :CustomField)
475
+ end
378
476
  end
379
477
 
380
- should "process input into HTML" do
381
- @instance.text1 = "*Hello* **World**"
382
- @instance.text1.value.should == "<p><em>Hello</em> <strong>World</strong></p>\n"
478
+ context "WebVideo fields" do
479
+ setup do
480
+ @content_class = Class.new(::Piece) do
481
+ field :video, :webvideo
482
+ end
483
+ @content_class.stubs(:name).returns("ContentClass")
484
+ @instance = @content_class.new
485
+ end
486
+
487
+ should "have their own editor type" do
488
+ @content_class.fields.video.export(nil)[:type].should == "Spontaneous.FieldTypes.WebVideoField"
489
+ @instance.video = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
490
+ fields = @instance.export(nil)[:fields]
491
+ fields[0][:processed_value].should == @instance.video.render(:html, :width => 480, :height => 270)
492
+ end
493
+
494
+ should "recognise youtube URLs" do
495
+ @instance.video = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
496
+ @instance.video.value.should == "http://www.youtube.com/watch?v=_0jroAM_pO4&amp;feature=feedrec_grec_index"
497
+ @instance.video.id.should == "_0jroAM_pO4"
498
+ @instance.video.video_type.should == "youtube"
499
+ end
500
+
501
+ should "recognise Vimeo URLs" do
502
+ @instance.video = "http://vimeo.com/31836285"
503
+ @instance.video.value.should == "http://vimeo.com/31836285"
504
+ @instance.video.id.should == "31836285"
505
+ @instance.video.video_type.should == "vimeo"
506
+ end
507
+
508
+ context "with player settings" do
509
+ setup do
510
+ @content_class.field :video2, :webvideo, :player => {
511
+ :width => 680, :height => 384,
512
+ :fullscreen => true, :autoplay => true, :loop => true,
513
+ :showinfo => false,
514
+ :youtube => { :theme => 'light', :hd => true, :controls => false },
515
+ :vimeo => { :color => "ccc", :api => true }
516
+ }
517
+ @instance = @content_class.new
518
+ @field = @instance.video2
519
+ end
520
+
521
+ should "use the configuration in the youtube player HTML" do
522
+ @field.value = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
523
+ html = @field.render(:html)
524
+ html.should =~ /^<iframe/
525
+ html.should =~ %r{src="http://www\.youtube\.com/embed/_0jroAM_pO4}
526
+ html.should =~ /width="680"/
527
+ html.should =~ /height="384"/
528
+ html.should =~ /theme=light/
529
+ html.should =~ /hd=1/
530
+ html.should =~ /fs=1/
531
+ html.should =~ /controls=0/
532
+ html.should =~ /autoplay=1/
533
+ html.should =~ /showinfo=0/
534
+ html.should =~ /showsearch=0/
535
+ @field.render(:html, :youtube => {:showsearch => 1}).should =~ /showsearch=1/
536
+ @field.render(:html, :youtube => {:theme => 'dark'}).should =~ /theme=dark/
537
+ @field.render(:html, :width => 100).should =~ /width="100"/
538
+ @field.render(:html, :loop => true).should =~ /loop=1/
539
+ end
540
+
541
+ should "use the configuration in the Vimeo player HTML" do
542
+ @field.value = "http://vimeo.com/31836285"
543
+ html = @field.render(:html)
544
+ html.should =~ /^<iframe/
545
+ html.should =~ %r{src="http://player\.vimeo\.com/video/31836285}
546
+ html.should =~ /width="680"/
547
+ html.should =~ /height="384"/
548
+ html.should =~ /color=ccc/
549
+ html.should =~ /webkitAllowFullScreen="yes"/
550
+ html.should =~ /allowFullScreen="yes"/
551
+ html.should =~ /autoplay=1/
552
+ html.should =~ /title=0/
553
+ html.should =~ /byline=0/
554
+ html.should =~ /portrait=0/
555
+ html.should =~ /api=1/
556
+ @field.render(:html, :vimeo => {:color => 'f0abcd'}).should =~ /color=f0abcd/
557
+ @field.render(:html, :loop => true).should =~ /loop=1/
558
+ @field.render(:html, :title => true).should =~ /title=1/
559
+ @field.render(:html, :title => true).should =~ /byline=0/
560
+ end
561
+
562
+ should "provide a version of the YouTube player params in JSON/JS format" do
563
+ @field.value = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
564
+ json = JSON.parse(@field.render(:json))
565
+ json[:"tagname"].should == "iframe"
566
+ json[:"tag"].should == "<iframe/>"
567
+ attr = json[:"attr"]
568
+ attr.must_be_instance_of(Hash)
569
+ attr[:"src"].should =~ %r{^http://www\.youtube\.com/embed/_0jroAM_pO4}
570
+ attr[:"src"].should =~ /theme=light/
571
+ attr[:"src"].should =~ /hd=1/
572
+ attr[:"src"].should =~ /fs=1/
573
+ attr[:"src"].should =~ /controls=0/
574
+ attr[:"src"].should =~ /autoplay=1/
575
+ attr[:"src"].should =~ /showinfo=0/
576
+ attr[:"src"].should =~ /showsearch=0/
577
+ attr[:"width"].should == 680
578
+ attr[:"height"].should == 384
579
+ attr[:"frameborder"].should == "0"
580
+ attr[:"type"].should == "text/html"
581
+ end
582
+
583
+ should "provide a version of the Vimeo player params in JSON/JS format" do
584
+ @field.value = "http://vimeo.com/31836285"
585
+ json = JSON.parse(@field.render(:json))
586
+ json[:"tagname"].should == "iframe"
587
+ json[:"tag"].should == "<iframe/>"
588
+ attr = json[:"attr"]
589
+ attr.must_be_instance_of(Hash)
590
+ attr[:"src"].should =~ /color=ccc/
591
+ attr[:"src"].should =~ /autoplay=1/
592
+ attr[:"src"].should =~ /title=0/
593
+ attr[:"src"].should =~ /byline=0/
594
+ attr[:"src"].should =~ /portrait=0/
595
+ attr[:"src"].should =~ /api=1/
596
+ attr[:"webkitAllowFullScreen"].should == "yes"
597
+ attr[:"allowFullScreen"].should == "yes"
598
+ attr[:"width"].should == 680
599
+ attr[:"height"].should == 384
600
+ attr[:"frameborder"].should == "0"
601
+ attr[:"type"].should == "text/html"
602
+ end
603
+
604
+
605
+ should "use the YouTube api to extract video metadata" do
606
+ youtube_info = {"thumbnail_large" => "http://i.ytimg.com/vi/_0jroAM_pO4/hqdefault.jpg", "thumbnail_small"=>"http://i.ytimg.com/vi/_0jroAM_pO4/default.jpg", "title" => "Hilarious QI Moment - Cricket", "description" => "Rob Brydon makes a rather embarassing choice of words whilst discussing the relationship between a cricket's chirping and the temperature. Taken from QI XL Series H episode 11 - Highs and Lows", "user_name" => "morthasa", "upload_date" => "2011-01-14 19:49:44", "tags" => "Hilarious, QI, Moment, Cricket, fun, 11, stephen, fry, alan, davies, Rob, Brydon, SeriesH, Fred, MacAulay, Sandi, Toksvig", "duration" => 78, "stats_number_of_likes" => 297, "stats_number_of_plays" => 53295, "stats_number_of_comments" => 46}#.symbolize_keys
607
+
608
+ response_xml_file = File.expand_path("../../fixtures/fields/youtube_api_response.xml", __FILE__)
609
+ connection = mock()
610
+ @field.expects(:open).with("http://gdata.youtube.com/feeds/api/videos/_0jroAM_pO4?v=2").returns(connection)
611
+ doc = Nokogiri::XML(File.open(response_xml_file))
612
+ Nokogiri.expects(:XML).with(connection).returns(doc)
613
+ @field.value = "http://www.youtube.com/watch?v=_0jroAM_pO4"
614
+ @field.values.should == youtube_info.merge(:id => "_0jroAM_pO4", :type => "youtube", :html => "http://www.youtube.com/watch?v=_0jroAM_pO4")
615
+ end
616
+
617
+ should "use the Vimeo api to extract video metadata" do
618
+ vimeo_info = {"id"=>29987529, "title"=>"Neon Indian Plays The UO Music Shop", "description"=>"Neon Indian plays electronic instruments from the UO Music Shop, Fall 2011. Read more at blog.urbanoutfitters.com.", "url"=>"http://vimeo.com/29987529", "upload_date"=>"2011-10-03 18:32:47", "mobile_url"=>"http://vimeo.com/m/29987529", "thumbnail_small"=>"http://b.vimeocdn.com/ts/203/565/203565974_100.jpg", "thumbnail_medium"=>"http://b.vimeocdn.com/ts/203/565/203565974_200.jpg", "thumbnail_large"=>"http://b.vimeocdn.com/ts/203/565/203565974_640.jpg", "user_name"=>"Urban Outfitters", "user_url"=>"http://vimeo.com/urbanoutfitters", "user_portrait_small"=>"http://b.vimeocdn.com/ps/251/111/2511118_30.jpg", "user_portrait_medium"=>"http://b.vimeocdn.com/ps/251/111/2511118_75.jpg", "user_portrait_large"=>"http://b.vimeocdn.com/ps/251/111/2511118_100.jpg", "user_portrait_huge"=>"http://b.vimeocdn.com/ps/251/111/2511118_300.jpg", "stats_number_of_likes"=>85, "stats_number_of_plays"=>26633, "stats_number_of_comments"=>0, "duration"=>100, "width"=>640, "height"=>360, "tags"=>"neon indian, analog, korg, moog, theremin, micropiano, microkorg, kaossilator, kaossilator pro", "embed_privacy"=>"anywhere"}.symbolize_keys
619
+ connection = mock()
620
+ connection.expects(:read).returns(Spontaneous.encode_json([vimeo_info]))
621
+ @field.expects(:open).with("http://vimeo.com/api/v2/video/29987529.json").returns(connection)
622
+ @field.value = "http://vimeo.com/29987529"
623
+ @field.values.should == vimeo_info.merge(:id => "29987529", :type => "vimeo", :html => "http://vimeo.com/29987529")
624
+ end
625
+ end
626
+
383
627
  end
384
628
 
385
- should "use more sensible linebreaks" do
386
- @instance.text1 = "With\nLinebreak"
387
- @instance.text1.value.should == "<p>With<br />\nLinebreak</p>\n"
388
- @instance.text2 = "With \nLinebreak"
389
- @instance.text2.value.should == "<p>With<br />\nLinebreak</p>\n"
629
+ context "Location fields" do
630
+ setup do
631
+ @content_class = Class.new(::Piece) do
632
+ field :location
633
+ end
634
+ @content_class.stubs(:name).returns("ContentClass")
635
+ @instance = @content_class.new
636
+ @field = @instance.location
637
+ end
638
+
639
+ should "use a standard string editor" do
640
+ @content_class.fields.location.export(nil)[:type].should == "Spontaneous.FieldTypes.StringField"
641
+ end
642
+
643
+ should "successfullt geolocate an address" do
644
+ @field.value = "Cambridge, England"
645
+ @field.value(:lat).should == 52.2053370
646
+ @field.value(:lng).should == 0.1218170
647
+ @field.value(:country).should == "United Kingdom"
648
+ @field.value(:formatted_address).should == "Cambridge, UK"
649
+
650
+ @field.latitude.should == 52.2053370
651
+ @field.longitude.should == 0.1218170
652
+ @field.lat.should == 52.2053370
653
+ @field.lng.should == 0.1218170
654
+
655
+ @field.country.should == "United Kingdom"
656
+ @field.formatted_address.should == "Cambridge, UK"
657
+ end
390
658
  end
391
659
  end
392
660
  end