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,94 @@
1
+ # encoding: UTF-8
2
+
3
+ module Spontaneous::Cli
4
+ class Init
5
+ class Db
6
+
7
+ attr_reader :site_connection_params, :admin_connection_params, :database, :config
8
+
9
+ def initialize(connection, cli)
10
+ @cli = cli
11
+ @connection = connection
12
+ # setup_connection_params(connection_settings, @cli.options)
13
+ # @admin_connection_params[:database] = "postgres"
14
+ end
15
+
16
+ def run
17
+ databases.each do |site_connection_params, admin_connection_params|
18
+ # config = site_connection_params.merge(:database => db)
19
+ create(admin_connection_params, site_connection_params)
20
+ migrate(site_connection_params)
21
+ end
22
+ end
23
+
24
+ def create(admin_config, site_config)
25
+ Sequel.connect(admin_config) do |connection|
26
+ begin
27
+ @cli.say " >> Creating database `#{site_config[:database]}`", :green
28
+ create_database(connection, site_config)
29
+ rescue => e
30
+ @cli.say " >>> Unable to create #{admin_config[:adapter]} database `#{site_config[:database]}`:\n > #{e}", :red
31
+ end
32
+ end
33
+ end
34
+
35
+ def migrate(site_config)
36
+ Sequel.connect(site_config) do |connection|
37
+ begin
38
+ connection.logger = nil
39
+ @cli.say " >> Running migrations..."
40
+ Sequel::Migrator.apply(connection, ::Spontaneous.gem_dir('db/migrations'))
41
+ @cli.say " >> Done"
42
+ rescue => e
43
+ @cli.say " >>> Error running migrations on database `#{site_config[:database]}`:\n > #{e}", :red
44
+ raise e
45
+ end
46
+ end
47
+ end
48
+
49
+ def create_database(connection, config)
50
+ commands = create_database_commands(config)
51
+ commands.each do |command, raise_error|
52
+ begin
53
+ connection.run(command)
54
+ rescue => e
55
+ raise e if raise_error
56
+ end
57
+ end
58
+ end
59
+
60
+ def create_database_commands(config)
61
+ [["", false]]
62
+ end
63
+
64
+ def setup_connection_params(connection_settings, options)
65
+ @options = options
66
+ @site_connection_params = connection_settings
67
+ @admin_connection_params = @site_connection_params.dup
68
+ @admin_connection_params[:user] = @options.user unless @options.user.blank?
69
+ @admin_connection_params[:password] = @options.password unless @options.password.blank?
70
+
71
+ # @database = @admin_connection_params.delete(:database)
72
+ end
73
+
74
+ def databases
75
+ environments.map { |env|
76
+ config_for_environment(env)
77
+ }
78
+ end
79
+
80
+ def config_for_environment(env)
81
+ site_config = @connection[env].dup
82
+ admin_config = site_config.dup
83
+ admin_config.delete(:database)
84
+ admin_config[:user] = @cli.options.user unless @cli.options.user.blank?
85
+ admin_config[:password] = @cli.options.password unless @cli.options.password.blank?
86
+ [site_config, admin_config]
87
+ end
88
+
89
+ def environments
90
+ [:development, :test]
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: UTF-8
2
+
3
+ module Spontaneous::Cli
4
+ class Init
5
+ class MySQL < Db
6
+
7
+ def create_database_commands(config)
8
+ host = config[:host].blank? ? "" : "@#{config[:host]}"
9
+ cmds = [ ["CREATE DATABASE `#{config[:database]}` CHARACTER SET UTF8", true] ]
10
+ unless config[:user] == "root"
11
+ cmds << ["GRANT ALL ON `#{config[:database]}`.* TO `#{config[:user]}`#{host} IDENTIFIED BY '#{config[:password]}'", false]
12
+ end
13
+ cmds
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: UTF-8
2
+
3
+ module Spontaneous::Cli
4
+ class Init
5
+ class Postgresql < Db
6
+
7
+ def config_for_environment(env)
8
+ site_config, admin_config = super
9
+ admin_config[:database] = "postgres"
10
+ [site_config, admin_config]
11
+ end
12
+
13
+ # On some machines the db creation fails due to incompabilities between the UTF8 encoding
14
+ # and the configured locale.
15
+ # You can force a locale for the db by adding LC_COLLATE & LC_CTYPE params
16
+ # to the CREATE command:
17
+ #
18
+ # LC_COLLATE='C.UTF-8' LC_CTYPE='C.UTF-8'
19
+ #
20
+ # but I don't know a good/the best way to determine the most appropriate UTF-8 locale
21
+ # C.UTF-8 doesn't exist on OS X.
22
+ def create_database_commands(config)
23
+ create_cmd = %(CREATE DATABASE "#{config[:database]}" WITH TEMPLATE=template0 ENCODING='UTF8')
24
+ cmds = []
25
+ unless config[:user].blank?
26
+ create_cmd << %( OWNER="#{config[:user]}")
27
+ cmds << [%(CREATE ROLE "#{config[:user]}" LOGIN PASSWORD '#{config[:password]}'), false]
28
+ end
29
+ cmds << [create_cmd, true]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'fileutils'
4
+
5
+ module Spontaneous::Cli
6
+ class Init
7
+ class Sqlite < Db
8
+
9
+ def create_database(connection, config)
10
+ FileUtils.mkdir_p(File.join(@cli.options.site, 'db'))
11
+ end
12
+ end
13
+ end
14
+ end
@@ -102,25 +102,58 @@ module Spontaneous
102
102
  end
103
103
 
104
104
  desc "dump", "Dumps the current site to an archive on the local machine"
105
- def dump
106
- prepare! :dump
107
- Dump.start
105
+ def dump(*args)
106
+ dump_site_data
108
107
  end
109
108
 
110
109
  desc "load", "Uploads a dump of the current site to a remote server"
111
- def load
112
- prepare! :load
113
- Load.start
110
+ def load(*args)
111
+ load_site_data
114
112
  end
115
113
 
116
114
  desc "publish", "Publishes the site"
117
115
  method_option :pages, :type => :array, :desc => "List of pages to publish"
118
116
  method_option :logfile, :type => :string, :desc => "Location of logfile"
119
- def publish
117
+ def publish(*args)
118
+ publish_site
119
+ rescue => e
120
+ $stderr.puts(e.message)
121
+ send_error_notification(e)
122
+ end
123
+
124
+ desc "render", "Re-renders the current content"
125
+ def render(*args)
126
+ render_site
127
+ end
128
+
129
+ desc "revision", "Shows the site status"
130
+ def revision(*args)
131
+ show_site_revision
132
+ end
133
+
134
+ desc "browse", "Launches a browser pointing to the current development CMS"
135
+ def browse(*args)
136
+ browse_site
137
+ end
138
+
139
+ private
140
+
141
+ def dump_site_data
142
+ prepare! :dump
143
+ Dump.start
144
+ end
145
+
146
+
147
+ def load_site_data
148
+ prepare! :load
149
+ Load.start
150
+ end
151
+
152
+ def publish_site
120
153
  site = prepare! :publish
121
154
  site.background_mode = :immediate
122
155
  ::Spontaneous::Logger.setup(:logfile => options.logfile) if options.logfile
123
- say "Creating revision #{site.revision}", :green, true
156
+ say "Creating revision #{site.working_revision}", :green, true
124
157
  if options.pages
125
158
  say "> Publishing pages #{options.pages.inspect}", :green, true
126
159
  site.publish_pages(options.pages)
@@ -129,34 +162,26 @@ module Spontaneous
129
162
  site.publish_all
130
163
  end
131
164
  # Rescue all errors to feed back to the UI
132
- rescue => e
133
- $stderr.puts(e.message)
134
- send_error_notification(e)
135
165
  end
136
166
 
137
- desc "render", "Re-renders the current content"
138
- def render
167
+ def render_site
139
168
  site = prepare! :render
140
169
  site.background_mode = :immediate
141
170
  site.rerender
142
171
  end
143
172
 
144
- desc "revision", "Shows the site status"
145
- def revision
173
+ def show_site_revision
146
174
  site = prepare! :revision
147
- say "Site is at revision #{site.revision}", :green
175
+ say "Site is at revision #{site.working_revision}", :green
148
176
  end
149
177
 
150
- desc "browse", "Launches a browser pointing to the current development CMS"
151
- def browse
178
+ def browse_site
152
179
  site = prepare :browse
153
180
  require 'launchy'
154
181
  boot!
155
182
  ::Launchy.open("http://localhost:#{site.config.port}/@spontaneous")
156
183
  end
157
184
 
158
- private
159
-
160
185
  def send_error_notification(error)
161
186
  simultaneous_event('publish_progress', {:state => "error", :progress => error}.to_json)
162
187
  end
@@ -48,6 +48,9 @@ module Spontaneous::Collections
48
48
  map { |box| box.render_using(renderer, format, locals, parent_context) }.join("\n")
49
49
  end
50
50
 
51
+ alias_method :render_inline, :render
52
+ alias_method :render_inline_using, :render_using
53
+
51
54
  protected
52
55
 
53
56
  def get_single(index)
@@ -9,8 +9,19 @@ module Spontaneous::Collections
9
9
 
10
10
  def initialize(owner, piece_store = [])
11
11
  @owner = owner
12
- @store = Hash.new { |hash, key| hash[key] = [] }
13
- (piece_store || []).each do |data|
12
+ @loaded = false
13
+ @piece_store = piece_store || []
14
+ end
15
+
16
+ def store
17
+ @store ||= initialize_store
18
+ end
19
+
20
+ # Lazily load the entries as it's not unlikely that we'll be loading instances
21
+ # without ever wanting to access their contents
22
+ def initialize_store
23
+ store = Hash.new { |hash, key| hash[key] = [] }
24
+ @piece_store.each do |data|
14
25
  id = data[0]
15
26
  entry = if (content = @owner._pieces.detect { |piece| piece.id == id })
16
27
  content.page? ? Spontaneous::PagePiece.new(@owner, content, data[1]) : content
@@ -19,9 +30,35 @@ module Spontaneous::Collections
19
30
  # then we just want to silently skip it
20
31
  if entry
21
32
  box_id = entry.box_sid.to_s
22
- @store[box_id] << entry
33
+ store[box_id] << entry
23
34
  end
24
35
  end
36
+ @loaded = true
37
+ store
38
+ end
39
+
40
+ def wrap_page(page)
41
+ case @loaded
42
+ when true
43
+ find { |e| e.id == page.id }
44
+ else
45
+ quick_wrap_page(page)
46
+ end
47
+ end
48
+
49
+ # Wrap a page with an entry without loading the owning item's content
50
+ # association see Page#render_inline for a use case.
51
+ #
52
+ # We might be loading an individual page using its id and then trying to
53
+ # render it immediately, in which case having to load the entire '_pieces'
54
+ # association just to render a single page entry would be insane.
55
+ #
56
+ def quick_wrap_page(page)
57
+ data = @piece_store.detect { |data|
58
+ data[0] == page.id
59
+ }
60
+ return nil if data.nil?
61
+ Spontaneous::PagePiece.new(@owner, page, data[1])
25
62
  end
26
63
 
27
64
  def each(&block)
@@ -94,8 +131,10 @@ module Spontaneous::Collections
94
131
  end
95
132
 
96
133
  def freeze
97
- super
134
+ # freeze the entries first in order to ensure that we've loaded our entries
135
+ # before freezing ourself
98
136
  store.values.each { |entries| entries.freeze }
137
+ super
99
138
  self
100
139
  end
101
140
 
@@ -5,8 +5,8 @@ module Spontaneous::Collections
5
5
 
6
6
  attr_reader :owner
7
7
 
8
- def initialize(owner, initial_values)
9
- super()
8
+ def initialize(owner, initial_values, superobject = nil, superset_name = nil)
9
+ super(superobject, superset_name)
10
10
  @owner = owner
11
11
  @field_data = initial_values
12
12
  initialize_from_prototypes
@@ -27,6 +27,14 @@ module Spontaneous::Collections
27
27
  self.each { |field| field.mark_unmodified }
28
28
  end
29
29
 
30
+ def with_dynamic_default_values
31
+ select { |field| field.prototype.dynamic_default? }
32
+ end
33
+
34
+ def prototypes
35
+ map(&:prototype)
36
+ end
37
+
30
38
  # Lazily load fields by name
31
39
  def named(name)
32
40
  super || load_field(owner.field_prototypes[name.to_sym])
@@ -40,10 +48,14 @@ module Spontaneous::Collections
40
48
  map { |field| wrap_field_value(field, field.render(format, locals), format) }.join("\n")
41
49
  end
42
50
 
51
+ alias_method :render_inline, :render
52
+
43
53
  def render_using(renderer, format = :html, locals = {}, parent_context = nil)
44
54
  map { |field| wrap_field_value(field, field.render_using(renderer, format, locals), format) }.join("\n")
45
55
  end
46
56
 
57
+ alias_method :render_inline_using, :render_using
58
+
47
59
  protected
48
60
 
49
61
  def wrap_field_value(field, value, format)
@@ -63,6 +63,14 @@ module Spontaneous
63
63
  :revision_history_dataset, :revision_archive_dataset,
64
64
  :quote_identifier
65
65
 
66
+ def prepare(type, name, *values, &block)
67
+ active_scope.prepare(type, prepared_statement_namespace(name), &block)
68
+ end
69
+
70
+ def prepared_statement_namespace(name)
71
+ "#{name}_#{current_revision || "editable"}_#{visible_only?}".to_sym
72
+ end
73
+
66
74
  def visible(visible_only = true, &block)
67
75
  scope(current_revision, visible_only, &block)
68
76
  end
@@ -87,6 +95,18 @@ module Spontaneous
87
95
  revision!(nil, &block)
88
96
  end
89
97
 
98
+ def editable?
99
+ current_revision.nil?
100
+ end
101
+
102
+ def with(dataset, &block)
103
+ with!(dataset, &block)
104
+ end
105
+
106
+ def with!(dataset, &block)
107
+ scope!(nil, false, dataset, &block)
108
+ end
109
+
90
110
  def scope(revision, visible, &block)
91
111
  if use_current_scope?(revision, visible)
92
112
  if block_given?
@@ -99,7 +119,7 @@ module Spontaneous
99
119
  end
100
120
  end
101
121
 
102
- def scope!(revision, visible, &block)
122
+ def scope!(revision, visible, dataset = nil, &block)
103
123
  if block_given?
104
124
  r, v, d = @keys.values_at(:revision, :visible, :active_scope)
105
125
  thread = Thread.current
@@ -107,7 +127,7 @@ module Spontaneous
107
127
  begin
108
128
  thread[r] = to_revision(revision)
109
129
  thread[v] = visible
110
- thread[d] = current_scope
130
+ thread[d] = configured_scope_or_dataset(dataset)
111
131
  yield
112
132
  ensure
113
133
  thread[r], thread[v], thread[d] = state
@@ -118,15 +138,15 @@ module Spontaneous
118
138
  end
119
139
 
120
140
  def active_scope
121
- Thread.current[@keys[:active_scope]] || current_scope
141
+ Thread.current[@keys[:active_scope]] || configured_scope
122
142
  end
123
143
 
124
- def cached_dataset?
144
+ def cached_scope?
125
145
  !Thread.current[@keys[:active_scope]].nil?
126
146
  end
127
147
 
128
148
  def use_current_scope?(revision, visible)
129
- cached_dataset? &&
149
+ cached_scope? &&
130
150
  (current_revision == to_revision(revision)) &&
131
151
  ((visible || false) == visible_only?)
132
152
  end
@@ -153,12 +173,25 @@ module Spontaneous
153
173
 
154
174
  private
155
175
 
156
- def current_scope
176
+ def configured_scope_or_dataset(dataset = nil)
177
+ return configured_scope if dataset.nil?
178
+ scope_with(dataset)
179
+ end
180
+
181
+ def configured_scope
157
182
  scope_for(current_revision, visible_only?)
158
183
  end
159
184
 
160
185
  def scope_for(revision, visibility)
161
- Scope.new(revision, visibility, @table, @schema)
186
+ scope_with(ds(revision, visibility))
187
+ end
188
+
189
+ def scope_with(dataset)
190
+ Scope.new(dataset, @schema)
191
+ end
192
+
193
+ def ds(revision, visibility)
194
+ @table.dataset(revision, visibility)
162
195
  end
163
196
  end
164
197
  end