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
@@ -0,0 +1,23 @@
1
+ # Use this file to configure the search indexes for your site
2
+
3
+ # In an index definition you can exclude and include things to index
4
+ # by type, by ID or UID or by position in page tree.
5
+ #
6
+ # You can have as many indexes as you need.
7
+ #
8
+ # Additional control over what appears in each index is provided in
9
+ # the field definitions. See the documentation there for more details.
10
+ #
11
+ # Site.index :blog do
12
+ # include_types :BlogPost, "Text"
13
+ # exclude_types :Image, Video
14
+ # language :english
15
+ # include_pages "> #blog", 99
16
+ # exclude_pages ">= /blog/archives", "#search_results", 23
17
+ # end
18
+ #
19
+ # To create an index purely for tags, specify the index here
20
+ # and then include only the tag field in the values to be indexed
21
+ #
22
+ # Site.index :tags do
23
+ # end
@@ -0,0 +1,5 @@
1
+ # encoding: UTF-8
2
+
3
+ class Box < Spontaneous::Box
4
+ end
5
+
@@ -1,13 +1,19 @@
1
- <!DOCTYPE HTML>
1
+ <!DOCTYPE html>
2
+ <!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
3
+ <!--[if IE 7]> <html class="no-js ie7 oldie" lang="en"> <![endif]-->
4
+ <!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]-->
5
+ <!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
2
6
  <html lang="en">
3
7
  <head>
4
8
  <meta charset="UTF-8">
5
- <title>{{ title }} ­ <%= @domain %></title>
9
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
10
+ <title>${ title } ­ <%= @domain %></title>
11
+ ${ stylesheets '/css/site' }
6
12
  </head>
7
13
  <body>
8
-
9
- {% block :content %}
14
+ %{ block :content }
10
15
  <h2>Welcome to <%= @domain %></h2>
11
- {% endblock :content %}
16
+ %{ endblock :content }
17
+ ${ scripts %w(/js/site) }
12
18
  </body>
13
19
  </html>
@@ -114,4 +114,3 @@ module Spontaneous
114
114
  end
115
115
  end
116
116
  end
117
-
@@ -5,276 +5,393 @@ require 'active_support/ordered_hash'
5
5
  module Spontaneous
6
6
  OrderedHash = ActiveSupport::OrderedHash unless defined?(OrderedHash)
7
7
 
8
- module Loader
9
- class << self
8
+ class Loader
9
+ attr_reader :use_reloader, :load_paths
10
10
 
11
- def use_reloader?
12
- Site.config.reload_classes
13
- end
11
+ alias_method :use_reloader?, :use_reloader
14
12
 
15
- def load!
16
- Reloader.run! if use_reloader?
17
- Spontaneous.load_paths.each do |path|
18
- load_classes(path)
19
- end
20
- end
13
+ def initialize(load_paths, use_reloader)
14
+ @load_paths = load_paths
15
+ @use_reloader = use_reloader
16
+ end
21
17
 
22
- def reload!
23
- Reloader.reload!
18
+ def reloader
19
+ @reloader ||= Reloader.new(load_paths)
20
+ end
21
+
22
+ def load!
23
+ reloader.run! if use_reloader?
24
+ load_paths.each do |path|
25
+ load_classes(path)
24
26
  end
27
+ end
25
28
 
26
- # thank you oh makers of Merb for your genius
27
- def load_classes(*paths)
28
- orphaned_classes = []
29
- paths.flatten.each do |path|
30
- Dir[path].sort.each do |file|
31
- begin
32
- load_file(file)
33
- rescue NameError => ne
34
- # puts "Stashed file with missing requirements for later reloading: #{file}"
35
- # ne.backtrace.each_with_index { |line, idx| puts "[#{idx}]: #{line}" }
36
- orphaned_classes.unshift(file)
37
- end
29
+ def reload!
30
+ reloader.reload!
31
+ end
32
+
33
+ def load_classes(*paths)
34
+ orphaned_classes = []
35
+ paths.flatten.each do |path|
36
+ Dir[path].sort.each do |file|
37
+ begin
38
+ load_file(file)
39
+ rescue NameError => ne
40
+ # puts "Stashed file with missing requirements for later reloading: #{file}"
41
+ # ne.backtrace.each_with_index { |line, idx| puts "[#{idx}]: #{line}" }
42
+ orphaned_classes.unshift(file)
38
43
  end
39
44
  end
40
- load_classes_with_requirements(orphaned_classes)
41
45
  end
46
+ load_classes_with_requirements(orphaned_classes)
47
+ end
42
48
 
43
- def load_file(file)
44
- if use_reloader?
45
- Reloader.safe_load(file)
46
- else
47
- require(file)
48
- end
49
+ def load_file(file)
50
+ if use_reloader?
51
+ reloader.safe_load(file)
52
+ else
53
+ require(file)
49
54
  end
55
+ end
50
56
 
51
- def load_classes_with_requirements(klasses)
52
- klasses.uniq!
57
+ def load_classes_with_requirements(klasses)
58
+ klasses.uniq!
53
59
 
54
- while klasses.size > 0
55
- # Note size to make sure things are loading
56
- size_at_start = klasses.size
60
+ while klasses.size > 0
61
+ # Note size to make sure things are loading
62
+ size_at_start = klasses.size
57
63
 
58
- # List of failed classes
59
- failed_classes = []
60
- # Map classes to exceptions
61
- error_map = {}
64
+ # List of failed classes
65
+ failed_classes = []
66
+ # Map classes to exceptions
67
+ error_map = {}
62
68
 
63
- klasses.each do |klass|
64
- begin
65
- load_file klass
66
- rescue NameError => ne
67
- error_map[klass] = ne
68
- failed_classes.push(klass)
69
- end
69
+ klasses.each do |klass|
70
+ begin
71
+ load_file klass
72
+ rescue NameError => ne
73
+ error_map[klass] = ne
74
+ failed_classes.push(klass)
70
75
  end
71
- klasses.clear
76
+ end
77
+ klasses.clear
72
78
 
73
- # Keep list of classes unique
74
- failed_classes.each { |k| klasses.push(k) unless klasses.include?(k) }
79
+ # Keep list of classes unique
80
+ failed_classes.each { |k| klasses.push(k) unless klasses.include?(k) }
75
81
 
76
- # Stop processing if nothing loads or if everything has loaded
77
- if klasses.size == size_at_start && klasses.size != 0
78
- # Write all remaining failed classes and their exceptions to the log
79
- messages = error_map.map do |klass, e|
80
- ["Could not load #{klass}:\n\n#{e.message} - (#{e.class})",
81
- "#{(e.backtrace || []).join("\n")}"]
82
- end
83
- messages.each { |msg, trace| puts("#{msg}\n\n#{trace}") }
84
- puts "#{failed_classes.join(", ")} failed to load."
82
+ # Stop processing if nothing loads or if everything has loaded
83
+ if klasses.size == size_at_start && klasses.size != 0
84
+ # Write all remaining failed classes and their exceptions to the log
85
+ messages = error_map.map do |klass, e|
86
+ backtrace = (e.backtrace.select { |line| is_site_page?(line) } || []).join("\n") << "\n..."
87
+ ["Could not load #{klass}:\n\n#{e.message} - (#{e.class})", "#{ backtrace }"]
85
88
  end
86
- break if(klasses.size == size_at_start || klasses.size == 0)
89
+ messages.each { |msg, trace| logger.fatal("#{msg}\n\n#{trace}") }
90
+ abort("\n#{failed_classes.join(", ")} failed to load.")
87
91
  end
88
-
89
- nil
92
+ break if(klasses.size == size_at_start || klasses.size == 0)
90
93
  end
94
+
95
+ nil
91
96
  end
92
- module Reloader
93
- class << self
94
- CACHE = {}
95
- MTIMES = {}
96
- FILES_LOADED = {}
97
- LOADED_CLASSES = {}
98
-
99
- ##
100
- # Reload all files with changes detected.
101
- #
102
- def reload!
103
- rotation do |file, mtime|
104
- # Retrive the last modified time
105
- new_file = MTIMES[file].nil?
106
- previous_mtime = MTIMES[file] ||= mtime
107
- logger.debug "Detected a new file #{file}" if new_file
108
- # We skip to next file if it is not new and not modified
109
- next unless new_file || mtime > previous_mtime
110
- # Now we can reload our file
111
- safe_load(file, mtime)
112
- end
97
+
98
+ def is_site_page?(backtrace)
99
+ path, line, context = backtrace.split(":")
100
+ path = File.expand_path(path)
101
+ root = Site.instance.root
102
+ path.start_with?(root)
103
+ end
104
+
105
+ class Reloader
106
+ attr_reader :load_paths, :cache, :mtimes, :files_loaded, :loaded_classes
107
+
108
+ def initialize(load_paths)
109
+ @load_paths = load_paths
110
+ @cache = {}
111
+ @mtimes = {}
112
+ @files_loaded = {}
113
+ @loaded_classes = {}
114
+ end
115
+
116
+ def reset!
117
+ cache.clear
118
+ mtimes.clear
119
+ files_loaded.clear
120
+ loaded_classes.clear
121
+ end
122
+
123
+ def reload!
124
+ rotation do |file, mtime|
125
+ # Retrive the last modified time
126
+ new_file = mtimes[file].nil?
127
+ previous_mtime = mtimes[file] ||= mtime
128
+ logger.debug "Detected a new file #{file}" if new_file
129
+ # We skip to next file if it is not new and not modified
130
+ next unless new_file || mtime > previous_mtime
131
+ # Now we can reload our file
132
+ safe_load(file, mtime)
113
133
  end
134
+ end
114
135
 
115
- ##
116
- # Returns true if any file changes are detected and populates the MTIMES cache
117
- #
118
- def changed?
119
- changed = false
120
- rotation do |file, mtime|
121
- new_file = MTIMES[file].nil?
122
- previous_mtime = MTIMES[file] ||= mtime
123
- changed = true if new_file || mtime > previous_mtime
124
- end
125
- changed
136
+ ##
137
+ # Returns true if any file changes are detected and populates the MTIMES cache
138
+ #
139
+ def changed?
140
+ changed = false
141
+ rotation do |file, mtime|
142
+ new_file = mtimes[file].nil?
143
+ previous_mtime = mtimes[file] ||= mtime
144
+ changed = true if new_file || mtime > previous_mtime
126
145
  end
127
- alias :run! :changed?
146
+ changed
147
+ end
148
+ alias :run! :changed?
128
149
 
129
- ##
130
- # A safe Kernel::load which issues the necessary hooks depending on results
131
- #
132
- def safe_load(file, mtime=nil)
133
- reload = mtime && mtime > MTIMES[file]
134
150
 
135
- logger.debug "Reloading #{file}" if reload
151
+ def dependency_file?(file)
152
+ true
153
+ end
136
154
 
137
- # Removes all classes declared in the specified file
138
- if klasses = LOADED_CLASSES.delete(file)
139
- klasses.each { |klass| remove_constant(klass) }
140
- end
155
+ def classes_for_file(file)
156
+ loaded_classes[file]
157
+ end
141
158
 
142
- # Keeps track of which constants were loaded and the files
143
- # that have been added so that the constants can be removed
144
- # and the files can be removed from $LOADED_FEAUTRES
145
- if FILES_LOADED[file]
146
- FILES_LOADED[file].each do |fl|
147
- next if fl == file
148
- $LOADED_FEATURES.delete(fl)
149
- end
159
+ def file_for_class(klass)
160
+ loaded_classes.select do |file, classes|
161
+ classes.include?(klass)
162
+ end.keys
163
+ end
164
+ ##
165
+ # A safe Kernel::load which issues the necessary hooks depending on results
166
+ #
167
+ def safe_load(file, mtime=nil)
168
+ reload = mtime && mtime > mtimes[file]
169
+
170
+ logger.debug "Reloading #{file}" if reload
171
+
172
+ # Removes all classes declared in the specified file
173
+ if klasses = loaded_classes.delete(file)
174
+ klasses.each { |klass| remove_constant(klass) }
175
+ end
176
+
177
+ # Keeps track of which constants were loaded and the files
178
+ # that have been added so that the constants can be removed
179
+ # and the files can be removed from $LOADED_FEAUTRES
180
+ if self.files_loaded[file]
181
+ self.files_loaded[file].each do |fl|
182
+ next if fl == file
183
+ $LOADED_FEATURES.delete(fl) if dependency_file?(fl)
150
184
  end
185
+ end
186
+
187
+ # Now reload the file ignoring any syntax errors
188
+ $LOADED_FEATURES.delete(file)
151
189
 
152
- # Now reload the file ignoring any syntax errors
153
- $LOADED_FEATURES.delete(file)
154
-
155
- # Duplicate objects and loaded features in the file
156
- klasses = ObjectSpace.classes.dup
157
- files_loaded = $LOADED_FEATURES.dup
158
-
159
- # Start to re-require old dependencies
160
- #
161
- # Why we need to reload the dependencies i.e. of a model?
162
- #
163
- # In some circumstances (i.e. with MongoMapper) reloading a model require:
164
- #
165
- # 1) Clean objectspace
166
- # 2) Reload model dependencies
167
- #
168
- # We need to clean objectspace because for example we don't need to apply two times validations keys etc...
169
- #
170
- # We need to reload MongoMapper dependencies for re-initialize them.
171
- #
172
- # In other cases i.e. in a controller (specially with dependencies that uses autoload) reload stuff like sass
173
- # is not really necessary... but how to distinguish when it is (necessary) since it is not?
174
- #
175
- if FILES_LOADED[file]
176
- FILES_LOADED[file].each do |fl|
177
- next if fl == file
178
- # Swich off for a while warnings expecially "already initialized constant" stuff
179
- begin
180
- verbosity = $-v
181
- $-v = nil
182
- require(fl)
183
- ensure
184
- $-v = verbosity
185
- end
190
+ # Duplicate objects and loaded features in the file
191
+ klasses = ObjectSpace.classes.dup
192
+ already_loaded = $LOADED_FEATURES.dup
193
+
194
+ # Start to re-require old dependencies
195
+ #
196
+ # Why we need to reload the dependencies i.e. of a model?
197
+ #
198
+ # In some circumstances (i.e. with MongoMapper) reloading a model require:
199
+ #
200
+ # 1) Clean objectspace
201
+ # 2) Reload model dependencies
202
+ #
203
+ # We need to clean objectspace because for example we don't need to apply two times validations keys etc...
204
+ #
205
+ # We need to reload MongoMapper dependencies for re-initialize them.
206
+ #
207
+ # In other cases i.e. in a controller (specially with dependencies that uses autoload) reload stuff like sass
208
+ # is not really necessary... but how to distinguish when it is (necessary) since it is not?
209
+ #
210
+ if self.files_loaded[file]
211
+ self.files_loaded[file].each do |fl|
212
+ next if fl == file
213
+ # Swich off for a while warnings expecially "already initialized constant" stuff
214
+ begin
215
+ verbosity = $-v
216
+ $-v = nil
217
+ require(fl)
218
+ ensure
219
+ $-v = verbosity
186
220
  end
187
221
  end
222
+ end
188
223
 
189
- # And finally reload the specified file
190
- begin
191
- require(file)
192
- rescue SyntaxError => ex
193
- logger.error "Cannot require #{file} because of syntax error: #{ex.message}"
194
- ensure
195
- MTIMES[file] = mtime if mtime
196
- end
224
+ # And finally reload the specified file
225
+ begin
226
+ require(file)
227
+ # rescue => ex
228
+ # logger.fatal "Cannot require #{file}\\\\\\\\n#{ex.message}"
229
+ # raise ex
230
+ ensure
231
+ mtimes[file] = mtime if mtime
232
+ end
197
233
 
198
- # Store the file details after successful loading
199
- LOADED_CLASSES[file] = ObjectSpace.classes - klasses
200
- FILES_LOADED[file] = $LOADED_FEATURES - files_loaded
234
+ # Store the file details after successful loading
235
+ loaded_classes[file] = ObjectSpace.classes - klasses
236
+ self.files_loaded[file] = $LOADED_FEATURES - already_loaded
201
237
 
202
- nil
203
- end
238
+ nil
239
+ end
204
240
 
205
- ##
206
- # Removes the specified class and constant.
207
- #
208
- def remove_constant(const)
209
- # return if Padrino::Reloader.exclude_constants.any? { |base| (const.to_s =~ /^#{base}/ || const.superclass.to_s =~ /^#{base}/) } &&
210
- # !Padrino::Reloader.include_constants.any? { |base| (const.to_s =~ /^#{base}/ || const.superclass.to_s =~ /^#{base}/) }
241
+ ##
242
+ # Removes the specified class and constant.
243
+ #
244
+ def remove_constant(const)
245
+ # return if Padrino::Reloader.exclude_constants.any? { |base| (const.to_s =~ /^#{base}/ || const.superclass.to_s =~ /^#{base}/) } &&
246
+ # !Padrino::Reloader.include_constants.any? { |base| (const.to_s =~ /^#{base}/ || const.superclass.to_s =~ /^#{base}/) }
211
247
 
212
- Spontaneous::Schema.delete(const)
248
+ Spontaneous.schema.delete(const)
213
249
 
214
- parts = const.to_s.split("::")
250
+ parts = const.to_s.split("::")
251
+ begin
215
252
  base = parts.size == 1 ? Object : Object.full_const_get(parts[0..-2].join("::"))
216
253
  object = parts[-1].to_s
217
- begin
218
- base.send(:remove_const, object)
219
- rescue NameError
220
- end
221
-
222
- nil
254
+ base.send(:remove_const, object)
255
+ rescue NameError => e
256
+ # logger.warn(e)
223
257
  end
224
258
 
225
- ##
226
- # Searches Ruby files in your +Padrino.root+ and monitors them for any changes.
227
- #
228
- def rotation
229
- paths = []
230
- # paths = Dir[Padrino.root("*")].unshift(Padrino.root).
231
- # reject { |path| Padrino::Reloader.exclude.include?(path) || !File.directory?(path) }
232
- # files = paths.map { |path| Dir["#{path}/**/*.rb"] }.flatten.uniq
259
+ nil
260
+ end
261
+
262
+ ##
263
+ # Searches Ruby files in your load_paths and monitors them for any changes.
264
+ #
265
+ def rotation
266
+ paths = []
267
+
268
+ files = load_paths.map do |glob|
269
+ Dir[glob]
270
+ end.flatten.uniq
271
+
272
+ files.map { |file|
273
+ found, stat = figure_path(file, paths)
274
+ next unless found && stat && mtime = stat.mtime
233
275
 
234
- files = Spontaneous.load_paths.map do |glob|
235
- Dir[glob]
236
- end.flatten.uniq
276
+ cache[file] = found
237
277
 
238
- files.map { |file|
239
- # next if Padrino::Reloader.exclude.any? { |base| file =~ /^#{base}/ }
278
+ yield(found, mtime)
279
+ }.compact
280
+ end
240
281
 
241
- found, stat = figure_path(file, paths)
242
- next unless found && stat && mtime = stat.mtime
282
+ ##
283
+ # Takes a relative or absolute +file+ name and a couple possible +paths+ that
284
+ # the +file+ might reside in. Returns the full path and File::Stat for that path.
285
+ #
286
+ def figure_path(file, paths)
287
+ found = cache[file]
288
+ found = file if !found and Pathname.new(file).absolute?
289
+ found, stat = safe_stat(found)
290
+ return found, stat if found
291
+
292
+ # paths.find do |possible_path|
293
+ # path = ::File.join(possible_path, file)
294
+ # found, stat = safe_stat(path)
295
+ # return ::File.expand_path(found), stat if found
296
+ # end
297
+
298
+ return false, false
299
+ end
243
300
 
244
- CACHE[file] = found
301
+ def safe_stat(file)
302
+ return unless file
303
+ stat = ::File.stat(file)
304
+ return file, stat if stat.file?
305
+ rescue Errno::ENOENT, Errno::ENOTDIR
306
+ cache.delete(file) and false
307
+ end
308
+ end
245
309
 
246
- yield(found, mtime)
247
- }.compact
310
+ end # Loader
311
+
312
+ class SchemaLoader < Loader
313
+ def reloader
314
+ @reloader ||= SchemaReloader.new(load_paths)
315
+ end
316
+ class SchemaReloader < Loader::Reloader
317
+ def schema_classes_for_file(file)
318
+ if klasses = classes_for_file(file)
319
+ klasses.select { |c| is_schema_class?(c) }
320
+ else
321
+ []
248
322
  end
323
+ end
249
324
 
250
- ##
251
- # Takes a relative or absolute +file+ name and a couple possible +paths+ that
252
- # the +file+ might reside in. Returns the full path and File::Stat for that path.
253
- #
254
- def figure_path(file, paths)
255
- found = CACHE[file]
256
- found = file if !found and Pathname.new(file).absolute?
257
- found, stat = safe_stat(found)
258
- return found, stat if found
259
-
260
- # paths.find do |possible_path|
261
- # path = ::File.join(possible_path, file)
262
- # found, stat = safe_stat(path)
263
- # return ::File.expand_path(found), stat if found
264
- # end
265
-
266
- return false, false
325
+ # Because the schema classes are so tied up together reloading
326
+ # must be done in the right order.
327
+ # This method works by finding all the modified files then
328
+ # mapping those to modified classes. Using the Schema, we find
329
+ # all the subclasses affected by the modified file and map those
330
+ # to files.
331
+ # To reload these we must first reload the superclass, otherwise
332
+ # the subclasses will re-load but load schema definitions from the
333
+ # already loaded but out-of-date superclass and we'll end up
334
+ # with orphaned box and field definitions
335
+ def reload!
336
+ changed_files = []
337
+ rotation do |file, mtime|
338
+ # Retrive the last modified time
339
+ new_file = mtimes[file].nil?
340
+ previous_mtime = mtimes[file] ||= mtime
341
+ logger.debug "Detected a new file #{file}" if new_file
342
+ # We skip to next file if it is not new and not modified
343
+ changed_files << [file, mtime] if new_file || mtime > previous_mtime
344
+ # Now we can reload our file
267
345
  end
346
+ all_classes = schema.classes.dup
347
+
348
+ modified_classes = changed_files.map do |file, mtime|
349
+ schema_classes_for_file(file)
350
+ end.flatten
351
+
352
+ affected_subclasses = modified_classes.map do |modified_class|
353
+ all_classes.select { |schema_class|
354
+ schema_class < modified_class
355
+ }
356
+ end.flatten
357
+
268
358
 
269
- def safe_stat(file)
270
- return unless file
271
- stat = ::File.stat(file)
272
- return file, stat if stat.file?
273
- rescue Errno::ENOENT, Errno::ENOTDIR
274
- CACHE.delete(file) and false
359
+ subclass_files_to_reload = affected_subclasses.map do |subclass|
360
+ file_for_class(subclass)
361
+ end.flatten
362
+
363
+ changed_files.each { |file, mtime|
364
+ begin
365
+ safe_load(file, mtime)
366
+ rescue => boom
367
+ # remove the file so that it gets reloaded next time, this stops errors in the schema files
368
+ # from resulting in a schema map change...
369
+ mtimes.delete(file)
370
+ raise boom
371
+ end
372
+ }
373
+ subclass_files_to_reload.each { |file| safe_load(file) }
374
+ end
375
+
376
+ def schema
377
+ Spontaneous.schema
378
+ end
379
+
380
+ def is_schema_class?(klass)
381
+ (klass < Spontaneous::Page or klass < Spontaneous::Piece or klass < Spontaneous::Box)
382
+ end
383
+
384
+ def remove_constant(const)
385
+ if is_schema_class?(const)
386
+ super
275
387
  end
276
- end # self
388
+ end
277
389
 
390
+ def dependency_file?(file)
391
+ path = ::File.expand_path(file)
392
+ tests = load_paths.map { |load_path| ::File.fnmatch?(load_path, path) }
393
+ tests.any? { |t| t }
394
+ end
278
395
  end
279
- end # Loader
396
+ end
280
397
  end # Spontaneous