spontaneous 0.2.0.beta5 → 0.2.0.beta6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (227) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +39 -0
  4. data/Gemfile +2 -0
  5. data/Readme.markdown +4 -4
  6. data/application/css/core.css.scss +144 -43
  7. data/application/css/definitions.css.scss +50 -16
  8. data/application/css/dialogue.css.scss +5 -2
  9. data/application/css/editing.css.scss +7 -7
  10. data/application/css/font.css.scss +1 -1
  11. data/application/css/meta.css.scss +6 -6
  12. data/application/css/popover.css.scss +6 -6
  13. data/application/css/top.css.scss +8 -1
  14. data/application/js/add_alias_dialogue.js +137 -36
  15. data/application/js/add_home_dialogue.js +10 -10
  16. data/application/js/ajax.js +26 -26
  17. data/application/js/authentication.js +2 -2
  18. data/application/js/box.js +21 -10
  19. data/application/js/box_container.js +13 -7
  20. data/application/js/compatibility.js +19 -17
  21. data/application/js/conflicted_field_dialogue.js +5 -5
  22. data/application/js/content.js +22 -16
  23. data/application/js/content_area.js +62 -33
  24. data/application/js/dialogue.js +16 -16
  25. data/application/js/dom.js +9 -10
  26. data/application/js/edit_panel.js +25 -20
  27. data/application/js/editing.js +21 -8
  28. data/application/js/entry.js +1 -1
  29. data/application/js/extensions.js +11 -11
  30. data/application/js/field/boolean.js +6 -6
  31. data/application/js/field/date.js +1 -1
  32. data/application/js/field/file.js +17 -17
  33. data/application/js/field/image.js +27 -27
  34. data/application/js/field/markdown.js +72 -71
  35. data/application/js/field/select.js +9 -9
  36. data/application/js/field/string.js +3 -3
  37. data/application/js/field/webvideo.js +2 -2
  38. data/application/js/field_preview.js +3 -0
  39. data/application/js/init.js +3 -2
  40. data/application/js/jquery-selection-position.js +13 -13
  41. data/application/js/location.js +17 -12
  42. data/application/js/login.js +2 -2
  43. data/application/js/meta_view/user_admin.js +101 -101
  44. data/application/js/metadata.js +1 -1
  45. data/application/js/page.js +2 -2
  46. data/application/js/page_browser.js +13 -13
  47. data/application/js/page_entry.js +1 -1
  48. data/application/js/panel/root_menu.js +10 -10
  49. data/application/js/popover.js +6 -5
  50. data/application/js/popover_view.js +5 -5
  51. data/application/js/preview.js +10 -4
  52. data/application/js/progress.js +6 -6
  53. data/application/js/properties.js +35 -6
  54. data/application/js/publish.js +43 -43
  55. data/application/js/require.js +14 -14
  56. data/application/js/services.js +3 -3
  57. data/application/js/sharded_upload.js +9 -8
  58. data/application/js/side_bar.js +5 -5
  59. data/application/js/state.js +2 -2
  60. data/application/js/status_bar.js +6 -6
  61. data/application/js/top_bar.js +97 -65
  62. data/application/js/types.js +9 -6
  63. data/application/js/upload.js +4 -4
  64. data/application/js/upload_manager.js +21 -21
  65. data/application/js/user.js +1 -1
  66. data/application/js/vendor/jquery.velocity.min.js +7 -0
  67. data/application/js/views.js +32 -8
  68. data/application/js/views/box_view.js +51 -31
  69. data/application/js/views/page_piece_view.js +17 -15
  70. data/application/js/views/page_view.js +54 -43
  71. data/application/js/views/piece_view.js +44 -37
  72. data/application/static/font/fontawesome-webfont-4f0022f25672c7f501c339cbf98d9117.ttf +0 -0
  73. data/application/views/index.erb +1 -0
  74. data/db/migrations/20130114120000_create_revision_tables.rb +2 -1
  75. data/db/migrations/20130813111009_increase_path_length.rb +11 -2
  76. data/db/migrations/20140506171823_add_index_to_target_id.rb +11 -0
  77. data/db/migrations/20140514090204_add_content_hash.rb +59 -0
  78. data/db/migrations/20140519150253_add_content_hash_timestamp.rb +20 -0
  79. data/lib/spontaneous.rb +0 -1
  80. data/lib/spontaneous/asset/environment.rb +77 -15
  81. data/lib/spontaneous/box.rb +21 -0
  82. data/lib/spontaneous/capistrano/deploy.rb +1 -1
  83. data/lib/spontaneous/capistrano/sync.rb +8 -7
  84. data/lib/spontaneous/change.rb +4 -2
  85. data/lib/spontaneous/cli/fields.rb +7 -3
  86. data/lib/spontaneous/cli/generate.rb +2 -0
  87. data/lib/spontaneous/cli/init.rb +24 -93
  88. data/lib/spontaneous/cli/init/db.rb +94 -0
  89. data/lib/spontaneous/cli/init/mysql.rb +17 -0
  90. data/lib/spontaneous/cli/init/postgresql.rb +33 -0
  91. data/lib/spontaneous/cli/init/sqlite.rb +14 -0
  92. data/lib/spontaneous/cli/site.rb +45 -20
  93. data/lib/spontaneous/collections/box_set.rb +3 -0
  94. data/lib/spontaneous/collections/entry_set.rb +43 -4
  95. data/lib/spontaneous/collections/field_set.rb +14 -2
  96. data/lib/spontaneous/data_mapper.rb +40 -7
  97. data/lib/spontaneous/data_mapper/content_model.rb +1 -1
  98. data/lib/spontaneous/data_mapper/content_model/associations.rb +63 -12
  99. data/lib/spontaneous/data_mapper/content_model/timestamps.rb +9 -14
  100. data/lib/spontaneous/data_mapper/content_table.rb +4 -2
  101. data/lib/spontaneous/data_mapper/dataset.rb +31 -2
  102. data/lib/spontaneous/data_mapper/scope.rb +37 -20
  103. data/lib/spontaneous/errors.rb +6 -0
  104. data/lib/spontaneous/facet.rb +20 -10
  105. data/lib/spontaneous/field/base.rb +8 -1
  106. data/lib/spontaneous/field/file.rb +28 -3
  107. data/lib/spontaneous/field/image.rb +2 -0
  108. data/lib/spontaneous/field/update.rb +6 -0
  109. data/lib/spontaneous/field/webvideo/vimeo.rb +6 -1
  110. data/lib/spontaneous/field/webvideo/vine.rb +1 -1
  111. data/lib/spontaneous/field/webvideo/youtube.rb +1 -1
  112. data/lib/spontaneous/generators/site.rb +6 -4
  113. data/lib/spontaneous/generators/site/.gitignore +1 -0
  114. data/lib/spontaneous/generators/site/Gemfile.tt +3 -3
  115. data/lib/spontaneous/generators/site/config/{indexes.rb.tt → initializers/indexes.rb.tt} +0 -0
  116. data/lib/spontaneous/generators/site/config/initializers/publishing.rb.tt +78 -0
  117. data/lib/spontaneous/generators/site/{config/database.yml.tt → db/mysql2.yml.tt} +7 -6
  118. data/lib/spontaneous/generators/site/db/postgres.yml.tt +25 -0
  119. data/lib/spontaneous/generators/site/db/sqlite3.yml.tt +18 -0
  120. data/lib/spontaneous/generators/site/public/humans.txt.tt +14 -0
  121. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +51 -0
  122. data/lib/spontaneous/loader.rb +1 -1
  123. data/lib/spontaneous/logger.rb +1 -1
  124. data/lib/spontaneous/media/image/optimizer.rb +1 -1
  125. data/lib/spontaneous/media/image/processor.rb +11 -2
  126. data/lib/spontaneous/media/image/renderable.rb +2 -0
  127. data/lib/spontaneous/model.rb +3 -0
  128. data/lib/spontaneous/model/box/allowed_types.rb +17 -4
  129. data/lib/spontaneous/model/core.rb +36 -3
  130. data/lib/spontaneous/model/core/aliases.rb +5 -2
  131. data/lib/spontaneous/model/core/boxes.rb +6 -0
  132. data/lib/spontaneous/model/core/cascading_change.rb +38 -0
  133. data/lib/spontaneous/model/core/content_hash.rb +171 -0
  134. data/lib/spontaneous/model/core/entries.rb +0 -19
  135. data/lib/spontaneous/model/core/fields.rb +11 -0
  136. data/lib/spontaneous/model/core/modifications.rb +22 -21
  137. data/lib/spontaneous/model/core/render.rb +3 -0
  138. data/lib/spontaneous/model/core/serialisation.rb +18 -17
  139. data/lib/spontaneous/model/page.rb +35 -8
  140. data/lib/spontaneous/model/page/page_tree.rb +20 -8
  141. data/lib/spontaneous/model/page/paths.rb +79 -50
  142. data/lib/spontaneous/model/page/singleton.rb +71 -0
  143. data/lib/spontaneous/model/page/site_map.rb +2 -1
  144. data/lib/spontaneous/model/page/site_timestamps.rb +2 -2
  145. data/lib/spontaneous/model/piece.rb +10 -0
  146. data/lib/spontaneous/output/context.rb +13 -6
  147. data/lib/spontaneous/output/format.rb +30 -5
  148. data/lib/spontaneous/output/helpers/script_helper.rb +8 -0
  149. data/lib/spontaneous/output/helpers/stylesheet_helper.rb +7 -0
  150. data/lib/spontaneous/output/renderable.rb +16 -0
  151. data/lib/spontaneous/output/store.rb +1 -1
  152. data/lib/spontaneous/output/template/renderer.rb +2 -2
  153. data/lib/spontaneous/page_piece.rb +25 -1
  154. data/lib/spontaneous/prototypes/box_prototype.rb +13 -0
  155. data/lib/spontaneous/prototypes/field_prototype.rb +7 -4
  156. data/lib/spontaneous/publishing.rb +10 -5
  157. data/lib/spontaneous/publishing/immediate.rb +32 -349
  158. data/lib/spontaneous/publishing/pipeline.rb +43 -0
  159. data/lib/spontaneous/publishing/progress.rb +186 -0
  160. data/lib/spontaneous/publishing/publish.rb +107 -0
  161. data/lib/spontaneous/publishing/rerender.rb +17 -0
  162. data/lib/spontaneous/publishing/revision.rb +53 -18
  163. data/lib/spontaneous/publishing/simultaneous.rb +1 -1
  164. data/lib/spontaneous/publishing/steps.rb +154 -0
  165. data/lib/spontaneous/publishing/steps/activate_revision.rb +45 -0
  166. data/lib/spontaneous/publishing/steps/archive_old_revisions.rb +22 -0
  167. data/lib/spontaneous/publishing/steps/base_step.rb +49 -0
  168. data/lib/spontaneous/publishing/steps/copy_static_files.rb +74 -0
  169. data/lib/spontaneous/publishing/steps/create_revision_directory.rb +24 -0
  170. data/lib/spontaneous/publishing/steps/generate_rackup_file.rb +51 -0
  171. data/lib/spontaneous/publishing/steps/generate_search_indexes.rb +24 -0
  172. data/lib/spontaneous/publishing/steps/render_revision.rb +69 -0
  173. data/lib/spontaneous/publishing/steps/write_revision_file.rb +43 -0
  174. data/lib/spontaneous/rack/back.rb +3 -1
  175. data/lib/spontaneous/rack/back/alias.rb +9 -8
  176. data/lib/spontaneous/rack/front.rb +1 -1
  177. data/lib/spontaneous/rack/middleware.rb +7 -4
  178. data/lib/spontaneous/rack/middleware/transaction.rb +14 -0
  179. data/lib/spontaneous/rack/page_controller.rb +23 -8
  180. data/lib/spontaneous/revision.rb +5 -10
  181. data/lib/spontaneous/schema.rb +5 -0
  182. data/lib/spontaneous/server.rb +3 -1
  183. data/lib/spontaneous/site.rb +17 -10
  184. data/lib/spontaneous/site/publishing.rb +25 -3
  185. data/lib/spontaneous/site/state.rb +7 -3
  186. data/lib/spontaneous/tasks/database.rake +5 -10
  187. data/lib/spontaneous/utils/database/mysql_dumper.rb +5 -1
  188. data/lib/spontaneous/version.rb +1 -1
  189. data/spontaneous.gemspec +4 -3
  190. data/test/fixtures/example_application/config/initializers/initializer1.rb +1 -0
  191. data/test/fixtures/example_application/config/initializers/initializer2.rb +1 -0
  192. data/test/fixtures/example_application/config/initializers/publishing.rb +13 -0
  193. data/test/fixtures/search/config/{indexes.rb → initializers/indexes.rb} +0 -0
  194. data/test/fixtures/serialisation/root_hash.yaml.erb +10 -4
  195. data/test/functional/test_application.rb +10 -0
  196. data/test/functional/test_back.rb +23 -5
  197. data/test/functional/test_cli.rb +98 -34
  198. data/test/functional/test_front.rb +7 -3
  199. data/test/test_helper.rb +35 -28
  200. data/test/unit/test_alias.rb +20 -3
  201. data/test/unit/test_assets.rb +58 -30
  202. data/test/unit/test_changesets.rb +20 -12
  203. data/test/unit/test_content_hash.rb +496 -0
  204. data/test/unit/test_context.rb +28 -1
  205. data/test/unit/test_controllers.rb +96 -61
  206. data/test/unit/test_crypt.rb +1 -8
  207. data/test/unit/test_datamapper.rb +95 -19
  208. data/test/unit/test_features.rb +1 -4
  209. data/test/unit/test_fields.rb +61 -12
  210. data/test/unit/test_generators.rb +39 -2
  211. data/test/unit/test_images.rb +3 -1
  212. data/test/unit/test_modifications.rb +224 -219
  213. data/test/unit/test_output_store.rb +10 -0
  214. data/test/unit/{test_formats.rb → test_outputs.rb} +75 -6
  215. data/test/unit/test_page.rb +61 -15
  216. data/test/unit/test_plugins.rb +2 -42
  217. data/test/unit/test_publishing_pipeline.rb +1050 -0
  218. data/test/unit/test_render.rb +30 -0
  219. data/test/unit/test_revisions.rb +110 -2
  220. data/test/unit/test_schema.rb +4 -0
  221. data/test/unit/test_search.rb +1 -1
  222. data/test/unit/test_serialisation.rb +6 -1
  223. data/test/unit/test_singletons.rb +159 -0
  224. data/test/unit/test_site.rb +71 -44
  225. metadata +140 -86
  226. data/application/static/font/fontawesome-webfont-1c66a4738b40ef0f6b1abca0ba9a796d.ttf +0 -0
  227. data/test/unit/test_publishing.rb +0 -330
@@ -0,0 +1,71 @@
1
+ # encoding: UTF-8
2
+
3
+ module Spontaneous::Model::Page
4
+ module Singleton
5
+ extend Spontaneous::Concern
6
+
7
+ module SiteMethods
8
+ def singletons
9
+ @singletons ||= {}
10
+ end
11
+
12
+ def singleton?(label)
13
+ singletons.key?(label.to_s)
14
+ end
15
+
16
+ def add_singleton_class(type, labels)
17
+ ([type.name.demodulize.underscore] + labels).map(&:to_sym).each do |label|
18
+ singletons[label.to_s] = true
19
+ define_singleton_method(label) { type.instance }
20
+ end
21
+ end
22
+ end
23
+
24
+ module AllowedTypeMethods
25
+ def exclude_type?(type)
26
+ return true if (type.singleton? && type.exists?)
27
+ super
28
+ end
29
+ end
30
+
31
+ module ContentClassMethods
32
+ def singleton?
33
+ @is_singleton || false
34
+ end
35
+ end
36
+
37
+ module ClassMethods
38
+ def singleton(*labels)
39
+ @is_singleton = true
40
+ extend SingletonClassMethods
41
+ include SingletonInstanceMethods
42
+ set_singleton_aliases(labels)
43
+ end
44
+ end
45
+
46
+ module SingletonClassMethods
47
+ def set_singleton_aliases(labels)
48
+ schema.site.add_singleton_class(self, labels)
49
+ end
50
+
51
+ def instance
52
+ mapper.with_cache("#{self}_singleton_instance") { first }
53
+ end
54
+
55
+ def exists?
56
+ !instance.nil?
57
+ end
58
+
59
+ def export(user = nil)
60
+ super.merge(is_singleton: true)
61
+ end
62
+ end
63
+
64
+ module SingletonInstanceMethods
65
+ def before_save
66
+ raise Spontaneous::SingletonException.new(self) if (new? && model.exists?)
67
+ super
68
+ end
69
+ end
70
+ end
71
+ end
@@ -35,7 +35,8 @@ module Spontaneous::Model::Page
35
35
  :type => self.class.ui_class,
36
36
  :type_id => self.class.schema_id,
37
37
  :children => self.children.length,
38
- :depth => depth
38
+ :depth => depth,
39
+ :private => in_private_tree?
39
40
  }
40
41
  end
41
42
  end
@@ -7,7 +7,7 @@ module Spontaneous::Model::Page
7
7
  extend Spontaneous::Concern
8
8
 
9
9
  def before_save_field(field)
10
- mark_site_modified if ((field.name == title_field) && field.modified?)
10
+ mark_site_modified if ((field.name == title_field_name) && field.modified?)
11
11
  super
12
12
  end
13
13
 
@@ -19,7 +19,7 @@ module Spontaneous::Model::Page
19
19
  end
20
20
 
21
21
  def after_update
22
- mark_site_modified if field?(title_field) && fields[title_field].modified?
22
+ mark_site_modified if field?(title_field_name) && fields[title_field_name].modified?
23
23
  super
24
24
  end
25
25
 
@@ -32,5 +32,15 @@ module Spontaneous::Model
32
32
  })
33
33
  end
34
34
  end
35
+
36
+ # Ensure that we map #render* to #render_inline* as this version of a page
37
+ # has no non-inline version
38
+ def render(format = :html, params = {}, parent_context = nil)
39
+ render_inline(format, params, parent_context)
40
+ end
41
+
42
+ def render_using(renderer, format = :html, params = {}, parent_context = nil)
43
+ render_inline_using(renderer, format, params, parent_context)
44
+ end
35
45
  end
36
46
  end
@@ -30,8 +30,8 @@ module Spontaneous::Output::Context
30
30
  site.home
31
31
  end
32
32
 
33
- def site_page(path)
34
- site[path]
33
+ def home
34
+ site.home
35
35
  end
36
36
 
37
37
  def asset_environment
@@ -107,6 +107,7 @@ module Spontaneous::Output::Context
107
107
  __loader.template(template_path).convert(Spontaneous::Output::Template::RequestSyntax)
108
108
  end
109
109
 
110
+ # 'defer' is a useful semantic way of calling 'template'
110
111
  alias_method :defer, :template
111
112
 
112
113
  def __format
@@ -121,13 +122,19 @@ module Spontaneous::Output::Context
121
122
  param.to_s
122
123
  end
123
124
 
125
+ RENDER_METHODS = [:render_inline_using, :render_using, :render_inline, :render].freeze
126
+
124
127
  # Has to be routed through the top-level renderer so as to make
125
128
  # use of shared caches that are held by it.
126
129
  def __render_content(content)
127
- if content.respond_to?(:render_using)
128
- content.render_using(_renderer, __format, {}, self)
129
- else
130
- content.render(__format, {}, self)
130
+ case (method = RENDER_METHODS.detect { |m| content.respond_to?(m) })
131
+ # use #__send__ to ensure that the method goes to any Renderable proxy object directly
132
+ when :render_inline_using, :render_using
133
+ content.__send__(method, _renderer, __format, {}, self)
134
+ when :render_inline, :render
135
+ content.__send__(method, __format, {}, self)
136
+ else # fallback to showing nothing
137
+ ""
131
138
  end
132
139
  end
133
140
  end
@@ -59,8 +59,6 @@ module Spontaneous::Output
59
59
  @name
60
60
  end
61
61
 
62
- alias_method :extension, :name
63
-
64
62
  def to_sym
65
63
  @name
66
64
  end
@@ -103,6 +101,10 @@ module Spontaneous::Output
103
101
  @options[:postprocess]
104
102
  end
105
103
 
104
+ def options
105
+ @options
106
+ end
107
+
106
108
  def context(site = Spontaneous.instance)
107
109
  site.context (helper_formats + [name]).uniq.compact
108
110
  end
@@ -114,7 +116,7 @@ module Spontaneous::Output
114
116
  attr_reader :page
115
117
  attr_accessor :content
116
118
 
117
- def_delegators "self.class", :format, :dynamic?, :extension, :to_sym, :mimetype, :mime_type, :public?, :private?, :context, :name, :extension
119
+ def_delegators "self.class", :format, :dynamic?, :extension, :to_sym, :mimetype, :mime_type, :public?, :private?, :context, :name, :extension, :options
118
120
 
119
121
  def initialize(page, content = nil)
120
122
  @page, @content = page, content
@@ -125,13 +127,18 @@ module Spontaneous::Output
125
127
  content.model
126
128
  end
127
129
 
130
+ # Hook into Pages' ability to re-define the object that they render as
131
+ def renderable_content
132
+ content.renderable
133
+ end
134
+
128
135
  def render(params = {}, parent_context = nil)
129
136
  render_using(default_renderer, params, parent_context)
130
137
  end
131
138
 
132
139
  def render_using(renderer, params = {}, parent_context = nil)
133
140
  before_render
134
- output = render_page(renderer, params, parent_context)
141
+ output = render_content(renderer, params, parent_context)
135
142
  output = postprocess(output)
136
143
  after_render(output)
137
144
  output
@@ -154,7 +161,7 @@ module Spontaneous::Output
154
161
  output
155
162
  end
156
163
 
157
- def render_page(renderer, params = {}, parent_context = nil)
164
+ def render_content(renderer, params = {}, parent_context = nil)
158
165
  renderer.render(self, params, parent_context)
159
166
  end
160
167
 
@@ -172,6 +179,24 @@ module Spontaneous::Output
172
179
  FileUtils.mkdir_p(dir) unless File.exist?(dir)
173
180
  path
174
181
  end
182
+
183
+ def ==(other)
184
+ eql?(other)
185
+ end
186
+
187
+ def eql?(other)
188
+ other.class == self.class && other.page == self.page
189
+ end
190
+
191
+ def hash
192
+ [self.class, page, options].hash
193
+ end
194
+
195
+ def url_path
196
+ path = page.path
197
+ path = "/index" if path == "/"
198
+ [path, name].join(".")
199
+ end
175
200
  end
176
201
  end
177
202
 
@@ -1,6 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  require 'coffee-script'
4
+ require 'simultaneous'
4
5
 
5
6
  module Spontaneous::Output::Helpers
6
7
  module ScriptHelper
@@ -13,6 +14,13 @@ module Spontaneous::Output::Helpers
13
14
  end
14
15
 
15
16
  def script_urls(*args)
17
+ unless site.model.mapper.editable?
18
+ begin
19
+ ::Simultaneous.send_event('publish_progress', {:state => "compiling assets", :progress => "*"}.to_json)
20
+ rescue Errno::ECONNREFUSED
21
+ rescue Errno::ENOENT
22
+ end
23
+ end
16
24
  options = args.extract_options!
17
25
  options.update(:development => development?)
18
26
  asset_environment.js(args.flatten, options)
@@ -13,6 +13,13 @@ module Spontaneous::Output::Helpers
13
13
  end
14
14
 
15
15
  def stylesheet_urls(*args)
16
+ unless site.model.mapper.editable?
17
+ begin
18
+ ::Simultaneous.send_event('publish_progress', {:state => "compiling assets", :progress => "*"}.to_json)
19
+ rescue Errno::ECONNREFUSED
20
+ rescue Errno::ENOENT
21
+ end
22
+ end
16
23
  options = args.extract_options!
17
24
  options.update(:development => development?)
18
25
  asset_environment.css(args.flatten, options)
@@ -80,6 +80,22 @@ module Spontaneous::Output
80
80
  @target.render_using(renderer, format, __render_locals__(locals), parent_context)
81
81
  end
82
82
 
83
+ def render_inline(format = :html, locals = {}, parent_context = nil)
84
+ @target.render_inline(format, __render_locals__(locals), parent_context)
85
+ end
86
+
87
+ def render_inline_using(renderer, format = :html, locals = {}, parent_context = nil)
88
+ @target.render_inline_using(renderer, format, __render_locals__(locals), parent_context)
89
+ end
90
+
91
+ def to_ary
92
+ @target.to_ary
93
+ end
94
+
95
+ def renderable
96
+ ::Spontaneous::Output::Renderable(@target.renderable, locals)
97
+ end
98
+
83
99
  private
84
100
 
85
101
  def __render_locals__(locals)
@@ -14,7 +14,7 @@ module Spontaneous::Output
14
14
  def self.new(backend_class, options = {})
15
15
  backend = case backend_class
16
16
  when :File
17
- File.new(options[:root])
17
+ File.new(options[:root] || options[:dir])
18
18
  else
19
19
  Moneta.new(backend_class, options)
20
20
  end
@@ -19,7 +19,7 @@ module Spontaneous::Output::Template
19
19
 
20
20
  def render(output, params = {}, parent_context = nil)
21
21
  output.model.with_visible do
22
- engine.render(output.content, context(output, params, parent_context), output.name)
22
+ engine.render(output.renderable_content, context(output, params, parent_context), output.name)
23
23
  end
24
24
  end
25
25
 
@@ -30,7 +30,7 @@ module Spontaneous::Output::Template
30
30
  end
31
31
 
32
32
  def context(output, params, parent)
33
- context_class(output).new(Spontaneous::Output::Renderable.new(output.content), params, parent).tap do |context|
33
+ context_class(output).new(Spontaneous::Output::Renderable.new(output.renderable_content), params, parent).tap do |context|
34
34
  context.site = @site
35
35
  context._renderer = renderer_for_context
36
36
  end
@@ -5,6 +5,7 @@ require 'delegate'
5
5
  module Spontaneous
6
6
  class PagePiece < DelegateClass(Page)
7
7
  include Spontaneous::Model::Core::Render
8
+ include Spontaneous::Model::Core::ContentHash::PagePieceMethods
8
9
 
9
10
  attr_accessor :owner
10
11
  attr_reader :style_id
@@ -26,6 +27,11 @@ module Spontaneous
26
27
  owner.page
27
28
  end
28
29
 
30
+ # This is used to unwrap pages from their entries within boxes
31
+ def to_page
32
+ target
33
+ end
34
+
29
35
  def id
30
36
  target.id
31
37
  end
@@ -42,7 +48,7 @@ module Spontaneous
42
48
  self
43
49
  end
44
50
 
45
- def export(user)
51
+ def export(user = nil)
46
52
  target.shallow_export(user).merge(export_styles).merge({
47
53
  :depth => self.depth
48
54
  })
@@ -75,5 +81,23 @@ module Spontaneous
75
81
  def template(format = :html, renderer = Spontaneous::Output.default_renderer)
76
82
  style.template(format, renderer)
77
83
  end
84
+
85
+ def inspect
86
+ %(#<PagePiece target=#{target.inspect}>)
87
+ end
88
+
89
+ def renderable
90
+ self
91
+ end
92
+
93
+ # Ensure that we map #render* to #render_inline* as this version of a page
94
+ # has no non-inline version
95
+ def render(format = :html, params = {}, parent_context = nil)
96
+ render_inline(format, params, parent_context)
97
+ end
98
+
99
+ def render_using(renderer, format = :html, params = {}, parent_context = nil)
100
+ render_inline_using(renderer, format, params, parent_context)
101
+ end
78
102
  end
79
103
  end
@@ -84,6 +84,7 @@ module Spontaneous::Prototypes
84
84
  owner.const_set("#{name.to_s.camelize}Box", instance_class)
85
85
  box_owner = owner
86
86
  box_name = name
87
+ marked_as_generated = generated?
87
88
  instance_class.instance_eval do
88
89
  singleton_class.__send__(:define_method, :schema_name) do
89
90
  "box/#{box_owner.schema_id}/#{box_name}"
@@ -94,6 +95,11 @@ module Spontaneous::Prototypes
94
95
  singleton_class.__send__(:define_method, :owner_sid) do
95
96
  box_owner.schema_id
96
97
  end
98
+ singleton_class.__send__(:define_method, :method_added) do |method|
99
+ if [:contents].include?(method) # maybe need to expand the list of 'dangerous' methods
100
+ logger.warn("#{box_owner} box '#{box_name}': redefining the #contents method. You should set 'generated: true' in the box options unless the box contents are entirely under user control")
101
+ end
102
+ end unless marked_as_generated
97
103
  end
98
104
  @extend.each { |block|
99
105
  instance_class.class_eval(&block) if block
@@ -134,6 +140,13 @@ module Spontaneous::Prototypes
134
140
  @options[:style]
135
141
  end
136
142
 
143
+ # If a box is marked as 'generated' then its contents
144
+ # are not under user control & it should be skipped when
145
+ # calculating the owner's content hash
146
+ def generated?
147
+ @options[:generated] || false
148
+ end
149
+
137
150
  def default_title
138
151
  name.to_s.titleize.gsub(/\band\b/i, '&')
139
152
  end
@@ -129,15 +129,19 @@ module Spontaneous::Prototypes
129
129
  instance_class
130
130
  end
131
131
 
132
- def default(_instance = nil)
133
- case default = @options[:default]
132
+ def default(instance = nil)
133
+ case (default = @options[:default])
134
134
  when Proc
135
- default[_instance]
135
+ default[instance]
136
136
  else
137
137
  default
138
138
  end
139
139
  end
140
140
 
141
+ def dynamic_default?
142
+ @options[:default].is_a?(Proc)
143
+ end
144
+
141
145
  def comment
142
146
  @options[:comment]
143
147
  end
@@ -183,7 +187,6 @@ module Spontaneous::Prototypes
183
187
 
184
188
  def inherit_schema_id(schema_id)
185
189
  @_inherited_schema_id = schema_id.to_s
186
- # instance_class.schema_id = schema_id
187
190
  end
188
191
 
189
192
  def merge(subclass_owner, field_type, subclass_options, &subclass_block)