spontaneous 0.1.0.alpha1 → 0.2.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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