spontaneous 0.2.0.alpha7 → 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (247) hide show
  1. data/Gemfile +10 -4
  2. data/Readme.markdown +1 -1
  3. data/application/css/definitions.css.scss +5 -0
  4. data/application/css/dialogue.css.scss +62 -0
  5. data/application/js/content.js +1 -1
  6. data/application/js/dom.js +1 -1
  7. data/application/js/event_source.js +3 -0
  8. data/application/js/{field_types/date_field.js → field/date.js} +2 -2
  9. data/application/js/{field_types/file_field.js → field/file.js} +2 -2
  10. data/application/js/{field_types/image_field.js → field/image.js} +54 -20
  11. data/application/js/{field_types/long_string_field.js → field/long_string.js} +2 -2
  12. data/application/js/{field_types/markdown_field.js → field/markdown.js} +2 -2
  13. data/application/js/{field_types/select_field.js → field/select.js} +2 -2
  14. data/application/js/{field_types/string_field.js → field/string.js} +21 -7
  15. data/application/js/{field_types/webvideo_field.js → field/webvideo.js} +2 -2
  16. data/application/js/field.js +2 -2
  17. data/application/js/publish.js +99 -19
  18. data/application/js/spontaneous.js +8 -8
  19. data/application/js/top_bar.js +6 -4
  20. data/db/migrations/20130109125023_add_page_publish_lock.rb +17 -0
  21. data/db/migrations/20130111161934_convert_bcrypt_passwords.rb +22 -0
  22. data/db/migrations/20130114120000_create_revision_tables.rb +106 -0
  23. data/db/migrations/20130116220423_add_index_to_archive.rb +9 -0
  24. data/lib/spontaneous/box.rb +53 -18
  25. data/lib/spontaneous/box_style.rb +2 -3
  26. data/lib/spontaneous/change.rb +39 -13
  27. data/lib/spontaneous/cli/fields.rb +29 -0
  28. data/lib/spontaneous/cli/init.rb +2 -2
  29. data/lib/spontaneous/cli/migrate.rb +0 -1
  30. data/lib/spontaneous/cli/server.rb +14 -10
  31. data/lib/spontaneous/cli/site.rb +20 -9
  32. data/lib/spontaneous/cli.rb +8 -6
  33. data/lib/spontaneous/collections/box_set.rb +11 -0
  34. data/lib/spontaneous/collections/field_set.rb +24 -1
  35. data/lib/spontaneous/concern.rb +37 -0
  36. data/lib/spontaneous/config.rb +3 -4
  37. data/lib/spontaneous/crypt/version.rb +130 -0
  38. data/lib/spontaneous/crypt.rb +84 -0
  39. data/lib/spontaneous/data_mapper/content_model/associations.rb +199 -0
  40. data/lib/spontaneous/data_mapper/content_model/column_accessors.rb +52 -0
  41. data/lib/spontaneous/data_mapper/content_model/instance_hooks.rb +34 -0
  42. data/lib/spontaneous/data_mapper/content_model/serialization.rb +54 -0
  43. data/lib/spontaneous/data_mapper/content_model/timestamps.rb +39 -0
  44. data/lib/spontaneous/data_mapper/content_model.rb +343 -0
  45. data/lib/spontaneous/data_mapper/content_table.rb +103 -0
  46. data/lib/spontaneous/data_mapper/dataset.rb +194 -0
  47. data/lib/spontaneous/data_mapper/scope.rb +195 -0
  48. data/lib/spontaneous/data_mapper.rb +161 -0
  49. data/lib/spontaneous/facet.rb +2 -2
  50. data/lib/spontaneous/field/base.rb +418 -0
  51. data/lib/spontaneous/field/date.rb +54 -0
  52. data/lib/spontaneous/{field_version.rb → field/field_version.rb} +1 -1
  53. data/lib/spontaneous/field/file.rb +100 -0
  54. data/lib/spontaneous/{field_types/image_field.rb → field/image.rb} +33 -33
  55. data/lib/spontaneous/{field_types/location_field.rb → field/location.rb} +2 -2
  56. data/lib/spontaneous/{field_types/long_string_field.rb → field/long_string.rb} +3 -3
  57. data/lib/spontaneous/field/markdown.rb +36 -0
  58. data/lib/spontaneous/{field_types/select_field.rb → field/select.rb} +4 -5
  59. data/lib/spontaneous/field/string.rb +17 -0
  60. data/lib/spontaneous/field/update.rb +156 -0
  61. data/lib/spontaneous/field/webvideo.rb +310 -0
  62. data/lib/spontaneous/field.rb +80 -0
  63. data/lib/spontaneous/generators/site/Gemfile.tt +2 -2
  64. data/lib/spontaneous/generators/site/config/environments/development.rb.tt +1 -1
  65. data/lib/spontaneous/generators/site/config/environments/production.rb.tt +1 -1
  66. data/lib/spontaneous/generators/site/lib/content.rb.tt +6 -0
  67. data/lib/spontaneous/generators/site/schema/box.rb.tt +3 -2
  68. data/lib/spontaneous/generators/site/schema/page.rb.tt +3 -1
  69. data/lib/spontaneous/generators/site/schema/piece.rb.tt +3 -1
  70. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +3 -1
  71. data/lib/spontaneous/generators/site.rb +4 -3
  72. data/lib/spontaneous/image_size.rb +8 -1
  73. data/lib/spontaneous/layout.rb +5 -1
  74. data/lib/spontaneous/loader.rb +2 -5
  75. data/lib/spontaneous/media/file.rb +11 -2
  76. data/lib/spontaneous/media/temp_file.rb +23 -0
  77. data/lib/spontaneous/media.rb +20 -39
  78. data/lib/spontaneous/{plugins → model/box}/allowed_types.rb +38 -17
  79. data/lib/spontaneous/model/box.rb +18 -0
  80. data/lib/spontaneous/{plugins → model/core}/aliases.rb +10 -14
  81. data/lib/spontaneous/{plugins → model/core}/boxes.rb +2 -2
  82. data/lib/spontaneous/{plugins → model/core}/content_groups.rb +2 -2
  83. data/lib/spontaneous/model/core/editor_class.rb +4 -0
  84. data/lib/spontaneous/{plugins → model/core}/entries.rb +19 -7
  85. data/lib/spontaneous/{plugins → model/core}/entry.rb +3 -3
  86. data/lib/spontaneous/{plugins → model/core}/fields.rb +38 -5
  87. data/lib/spontaneous/{plugins → model/core}/instance_code.rb +2 -2
  88. data/lib/spontaneous/{plugins → model/core}/media.rb +2 -12
  89. data/lib/spontaneous/{plugins → model/core}/modifications.rb +7 -6
  90. data/lib/spontaneous/model/core/page_search.rb +36 -0
  91. data/lib/spontaneous/{plugins → model/core}/permissions.rb +4 -4
  92. data/lib/spontaneous/{plugins → model/core}/prototypes.rb +4 -4
  93. data/lib/spontaneous/{plugins → model/core}/publishing.rb +93 -115
  94. data/lib/spontaneous/{plugins → model/core}/render.rb +2 -2
  95. data/lib/spontaneous/{plugins → model/core}/schema_hierarchy.rb +7 -11
  96. data/lib/spontaneous/model/core/schema_id.rb +65 -0
  97. data/lib/spontaneous/{plugins → model/core}/schema_title.rb +2 -2
  98. data/lib/spontaneous/{plugins → model/core}/serialisation.rb +2 -2
  99. data/lib/spontaneous/{plugins → model/core}/styles.rb +2 -2
  100. data/lib/spontaneous/{plugins → model/core}/supertype.rb +2 -2
  101. data/lib/spontaneous/{plugins → model/core}/visibility.rb +7 -48
  102. data/lib/spontaneous/model/core.rb +143 -0
  103. data/lib/spontaneous/{plugins → model/page}/controllers.rb +3 -3
  104. data/lib/spontaneous/{plugins → model}/page/formats.rb +2 -2
  105. data/lib/spontaneous/{plugins → model/page}/layouts.rb +2 -2
  106. data/lib/spontaneous/model/page/locks.rb +14 -0
  107. data/lib/spontaneous/{plugins → model/page}/page_tree.rb +3 -3
  108. data/lib/spontaneous/{plugins → model/page}/paths.rb +30 -12
  109. data/lib/spontaneous/{plugins → model}/page/request.rb +2 -2
  110. data/lib/spontaneous/{plugins → model/page}/site_map.rb +2 -2
  111. data/lib/spontaneous/model/page/site_timestamps.rb +44 -0
  112. data/lib/spontaneous/{page.rb → model/page.rb} +49 -28
  113. data/lib/spontaneous/{piece.rb → model/piece.rb} +7 -6
  114. data/lib/spontaneous/model.rb +97 -0
  115. data/lib/spontaneous/output/context.rb +1 -1
  116. data/lib/spontaneous/output/format.rb +4 -0
  117. data/lib/spontaneous/output/template/renderer.rb +2 -2
  118. data/lib/spontaneous/output.rb +2 -2
  119. data/lib/spontaneous/page_lock.rb +62 -0
  120. data/lib/spontaneous/page_piece.rb +1 -1
  121. data/lib/spontaneous/permissions/access_key.rb +9 -4
  122. data/lib/spontaneous/permissions/user.rb +19 -9
  123. data/lib/spontaneous/permissions.rb +2 -5
  124. data/lib/spontaneous/plugins/application/facets.rb +1 -2
  125. data/lib/spontaneous/plugins/application/features.rb +1 -1
  126. data/lib/spontaneous/plugins/application/paths.rb +1 -1
  127. data/lib/spontaneous/plugins/application/render.rb +1 -1
  128. data/lib/spontaneous/plugins/application/serialisation.rb +1 -1
  129. data/lib/spontaneous/plugins/application/state.rb +30 -1
  130. data/lib/spontaneous/plugins/application/system.rb +12 -12
  131. data/lib/spontaneous/prototypes/box_prototype.rb +1 -1
  132. data/lib/spontaneous/prototypes/field_prototype.rb +3 -6
  133. data/lib/spontaneous/prototypes/style_prototype.rb +1 -1
  134. data/lib/spontaneous/publishing/immediate.rb +77 -49
  135. data/lib/spontaneous/publishing/revision.rb +355 -0
  136. data/lib/spontaneous/publishing/simultaneous.rb +10 -49
  137. data/lib/spontaneous/publishing.rb +1 -0
  138. data/lib/spontaneous/rack/around_back.rb +1 -1
  139. data/lib/spontaneous/rack/around_front.rb +2 -4
  140. data/lib/spontaneous/rack/around_preview.rb +1 -1
  141. data/lib/spontaneous/rack/back.rb +80 -63
  142. data/lib/spontaneous/rack/cacheable_file.rb +2 -2
  143. data/lib/spontaneous/rack/cookie_authentication.rb +1 -1
  144. data/lib/spontaneous/rack/front.rb +1 -1
  145. data/lib/spontaneous/rack/helpers.rb +8 -9
  146. data/lib/spontaneous/{page_controller.rb → rack/page_controller.rb} +1 -1
  147. data/lib/spontaneous/rack/public.rb +3 -3
  148. data/lib/spontaneous/rack.rb +15 -15
  149. data/lib/spontaneous/schema/uid.rb +4 -1
  150. data/lib/spontaneous/schema.rb +57 -24
  151. data/lib/spontaneous/search/database.rb +12 -1
  152. data/lib/spontaneous/search/index.rb +34 -6
  153. data/lib/spontaneous/search/results.rb +1 -1
  154. data/lib/spontaneous/server.rb +3 -3
  155. data/lib/spontaneous/simultaneous.rb +53 -0
  156. data/lib/spontaneous/{plugins/site → site}/features.rb +2 -2
  157. data/lib/spontaneous/{plugins/site → site}/helpers.rb +2 -3
  158. data/lib/spontaneous/{plugins/site → site}/hooks.rb +2 -2
  159. data/lib/spontaneous/{plugins/site → site}/instance.rb +4 -6
  160. data/lib/spontaneous/{plugins/site → site}/level.rb +2 -2
  161. data/lib/spontaneous/{plugins/site → site}/map.rb +4 -4
  162. data/lib/spontaneous/{plugins/site → site}/paths.rb +2 -2
  163. data/lib/spontaneous/site/publishing.rb +89 -0
  164. data/lib/spontaneous/{plugins/site → site}/schema.rb +4 -4
  165. data/lib/spontaneous/{plugins/site → site}/search.rb +2 -2
  166. data/lib/spontaneous/{plugins/site → site}/selectors.rb +15 -7
  167. data/lib/spontaneous/{plugins/site → site}/state.rb +2 -2
  168. data/lib/spontaneous/{plugins/site → site}/storage.rb +2 -2
  169. data/lib/spontaneous/{plugins/site → site}/url.rb +2 -2
  170. data/lib/spontaneous/site.rb +31 -14
  171. data/lib/spontaneous/state.rb +5 -6
  172. data/lib/spontaneous/style.rb +3 -2
  173. data/lib/spontaneous/utils/database/mysql_dumper.rb +13 -0
  174. data/lib/spontaneous/utils/database/postgres_dumper.rb +5 -0
  175. data/lib/spontaneous/version.rb +1 -1
  176. data/lib/spontaneous.rb +34 -89
  177. data/spontaneous.gemspec +112 -114
  178. data/test/experimental/test_crypt.rb +158 -0
  179. data/test/experimental/test_features.rb +3 -3
  180. data/test/fixtures/example_application/config/environments/development.rb +1 -1
  181. data/test/fixtures/example_application/lib/content.rb +5 -0
  182. data/test/fixtures/example_application/schema/page.rb +2 -1
  183. data/test/fixtures/example_application/schema/piece.rb +3 -2
  184. data/test/fixtures/serialisation/class_hash.yaml.erb +5 -5
  185. data/test/fixtures/serialisation/root_hash.yaml.erb +8 -0
  186. data/test/functional/test_application.rb +12 -1
  187. data/test/functional/test_back.rb +80 -48
  188. data/test/functional/test_front.rb +39 -46
  189. data/test/functional/test_user_manager.rb +3 -9
  190. data/test/javascript/test_markdown.rb +2 -2
  191. data/test/test_helper.rb +78 -23
  192. data/test/unit/test_alias.rb +21 -15
  193. data/test/unit/test_asset_bundler.rb +3 -3
  194. data/test/unit/test_assets.rb +2 -2
  195. data/test/unit/test_async.rb +7 -6
  196. data/test/unit/test_authentication.rb +43 -37
  197. data/test/unit/test_boxes.rb +46 -21
  198. data/test/unit/test_changesets.rb +65 -20
  199. data/test/unit/test_config.rb +9 -9
  200. data/test/unit/test_content.rb +50 -51
  201. data/test/unit/test_content_inheritance.rb +6 -20
  202. data/test/unit/test_datamapper.rb +1330 -0
  203. data/test/unit/test_datamapper_content.rb +214 -0
  204. data/test/unit/test_fields.rb +543 -54
  205. data/test/unit/test_formats.rb +2 -3
  206. data/test/unit/test_generators.rb +6 -6
  207. data/test/unit/test_helpers.rb +1 -1
  208. data/test/unit/test_image_size.rb +10 -5
  209. data/test/unit/test_images.rb +17 -18
  210. data/test/unit/test_layouts.rb +18 -3
  211. data/test/unit/test_media.rb +74 -49
  212. data/test/unit/test_modifications.rb +43 -43
  213. data/test/unit/test_page.rb +7 -10
  214. data/test/unit/test_permissions.rb +3 -10
  215. data/test/unit/test_piece.rb +5 -6
  216. data/test/unit/test_plugins.rb +7 -14
  217. data/test/unit/test_prototypes.rb +3 -3
  218. data/test/unit/test_publishing.rb +49 -27
  219. data/test/unit/test_render.rb +46 -15
  220. data/test/unit/test_revisions.rb +124 -127
  221. data/test/unit/test_schema.rb +53 -58
  222. data/test/unit/test_search.rb +64 -16
  223. data/test/unit/test_serialisation.rb +4 -11
  224. data/test/unit/test_site.rb +11 -12
  225. data/test/unit/test_structure.rb +2 -5
  226. data/test/unit/test_styles.rb +22 -24
  227. data/test/unit/test_type_hierarchy.rb +7 -5
  228. data/test/unit/test_visibility.rb +79 -55
  229. metadata +128 -102
  230. data/lib/sequel/plugins/content_table_inheritance.rb +0 -203
  231. data/lib/sequel/plugins/scoped_table_name.rb +0 -54
  232. data/lib/spontaneous/content.rb +0 -129
  233. data/lib/spontaneous/field_types/date_field.rb +0 -56
  234. data/lib/spontaneous/field_types/field.rb +0 -302
  235. data/lib/spontaneous/field_types/file_field.rb +0 -68
  236. data/lib/spontaneous/field_types/markdown_field.rb +0 -38
  237. data/lib/spontaneous/field_types/string_field.rb +0 -19
  238. data/lib/spontaneous/field_types/webvideo_field.rb +0 -313
  239. data/lib/spontaneous/field_types.rb +0 -38
  240. data/lib/spontaneous/generators/site/lib/site.rb.tt +0 -4
  241. data/lib/spontaneous/plugins/field/editor_class.rb +0 -13
  242. data/lib/spontaneous/plugins/page/site_timestamps.rb +0 -28
  243. data/lib/spontaneous/plugins/page_search.rb +0 -63
  244. data/lib/spontaneous/plugins/schema_id.rb +0 -68
  245. data/lib/spontaneous/plugins/site/publishing.rb +0 -75
  246. data/lib/spontaneous/rack/fiber_pool.rb +0 -26
  247. data/test/unit/test_table_scoping.rb +0 -80
@@ -6,7 +6,10 @@ class FieldsTest < MiniTest::Spec
6
6
 
7
7
  def setup
8
8
  @site = setup_site
9
- Site.publishing_method = :immediate
9
+ @now = Time.now
10
+ stub_time(@now)
11
+ Spontaneous::State.delete
12
+ Site.background_mode = :immediate
10
13
  end
11
14
 
12
15
  def teardown
@@ -14,17 +17,7 @@ class FieldsTest < MiniTest::Spec
14
17
  end
15
18
 
16
19
  context "Fields" do
17
- setup do
18
- class ::Page < Spontaneous::Page; end
19
- class ::Piece < Spontaneous::Piece; end
20
- end
21
-
22
- teardown do
23
- Object.send(:remove_const, :Page)
24
- Object.send(:remove_const, :Piece)
25
- end
26
-
27
- context "New content instances" do
20
+ context "New content instances xxx" do
28
21
  setup do
29
22
  @content_class = Class.new(Piece) do
30
23
  field :title, :default => "Magic"
@@ -35,7 +28,7 @@ class FieldsTest < MiniTest::Spec
35
28
 
36
29
  should "have fields with values defined by prototypes" do
37
30
  f = @instance.fields[:title]
38
- assert f.class < Spontaneous::FieldTypes::StringField
31
+ assert f.class < Spontaneous::Field::String
39
32
  f.value.should == "Magic"
40
33
  end
41
34
 
@@ -54,6 +47,14 @@ class FieldsTest < MiniTest::Spec
54
47
  @instance.title = "Boing!"
55
48
  @instance.fields[:title].value.should == "Boing!"
56
49
  end
50
+
51
+ # TODO: I want to allow this but don't like overwriting the ::fields
52
+ # method like this.
53
+ # should "allow the definition of multiple fields at once" do
54
+ # content_class = Class.new(Piece) do
55
+ # fields :title, :photo, :date
56
+ # end
57
+ # end
57
58
  end
58
59
 
59
60
  context "Overwriting fields" do
@@ -80,7 +81,7 @@ class FieldsTest < MiniTest::Spec
80
81
  @class2.fields.title.title.should == "Two"
81
82
  @class3.fields.date.title.should == "Three"
82
83
  @class3.fields.date.schema_id.should == @class1.fields.date.schema_id
83
- assert @instance.title.class < Spontaneous::FieldTypes::ImageField
84
+ assert @instance.title.class < Spontaneous::Field::Image
84
85
  @instance.title.value.to_s.should == "Two"
85
86
  instance1 = @class1.new
86
87
  instance3 = @class3.new
@@ -109,11 +110,11 @@ class FieldsTest < MiniTest::Spec
109
110
  end
110
111
 
111
112
  should "default to basic string class" do
112
- assert @content_class.field_prototypes[:title].instance_class < Spontaneous::FieldTypes::StringField
113
+ assert @content_class.field_prototypes[:title].instance_class < Spontaneous::Field::String
113
114
  end
114
115
 
115
- should "map :string type to FieldTypes::StringField" do
116
- assert @content_class.field_prototypes[:synopsis].instance_class < Spontaneous::FieldTypes::StringField
116
+ should "map :string type to Field::String" do
117
+ assert @content_class.field_prototypes[:synopsis].instance_class < Spontaneous::Field::String
117
118
  end
118
119
 
119
120
  should "be listable" do
@@ -162,9 +163,9 @@ class FieldsTest < MiniTest::Spec
162
163
  field :chunky
163
164
  end
164
165
 
165
- assert content_class.field_prototypes[:image].field_class < Spontaneous::FieldTypes::ImageField
166
- assert content_class.field_prototypes[:date].field_class < Spontaneous::FieldTypes::DateField
167
- assert content_class.field_prototypes[:chunky].field_class < Spontaneous::FieldTypes::StringField
166
+ assert content_class.field_prototypes[:image].field_class < Spontaneous::Field::Image
167
+ assert content_class.field_prototypes[:date].field_class < Spontaneous::Field::Date
168
+ assert content_class.field_prototypes[:chunky].field_class < Spontaneous::Field::String
168
169
  end
169
170
  end
170
171
 
@@ -195,7 +196,7 @@ class FieldsTest < MiniTest::Spec
195
196
  end
196
197
 
197
198
  should "parse field class" do
198
- assert @prototype.field_class < Spontaneous::FieldTypes::ImageField
199
+ assert @prototype.field_class < Spontaneous::Field::Image
199
200
  end
200
201
 
201
202
  should "parse default value" do
@@ -233,7 +234,7 @@ class FieldsTest < MiniTest::Spec
233
234
 
234
235
  context "Values" do
235
236
  setup do
236
- @field_class = Class.new(FieldTypes::Field) do
237
+ @field_class = Class.new(S::Field::Base) do
237
238
  def outputs
238
239
  [:html, :plain, :fancy]
239
240
  end
@@ -256,6 +257,17 @@ class FieldsTest < MiniTest::Spec
256
257
  @field = @field_class.new
257
258
  end
258
259
 
260
+ should "be used as the comparator" do
261
+ f1 = @field_class.new
262
+ f1.value = "a"
263
+ f2 = @field_class.new
264
+ f2.value = "b"
265
+ (f1 <=> f2).should == -1
266
+ (f2 <=> f1).should == 1
267
+ (f1 <=> f1).should == 0
268
+ [f2, f1].sort.map(&:value).should == ["<a>", "<b>"]
269
+ end
270
+
259
271
  should "be transformed by the update method" do
260
272
  @field.value = "Hello"
261
273
  @field.value.should == "<Hello>"
@@ -273,7 +285,7 @@ class FieldsTest < MiniTest::Spec
273
285
  end
274
286
 
275
287
  should "escape ampersands by default" do
276
- field_class = Class.new(FieldTypes::StringField) do
288
+ field_class = Class.new(S::Field::String) do
277
289
  end
278
290
  field = field_class.new
279
291
  field.value = "Hello & Welcome"
@@ -282,7 +294,7 @@ class FieldsTest < MiniTest::Spec
282
294
  end
283
295
 
284
296
  should "educate quotes" do
285
- field_class = Class.new(FieldTypes::StringField)
297
+ field_class = Class.new(S::Field::String)
286
298
  field = field_class.new
287
299
  field.value = %("John's first... example")
288
300
  field.value(:html).should == "“John’s first… example”"
@@ -432,7 +444,7 @@ class FieldsTest < MiniTest::Spec
432
444
 
433
445
  context "Available output formats" do
434
446
  should "include HTML & PDF and default to default value" do
435
- f = FieldTypes::Field.new
447
+ f = S::Field::Base.new
436
448
  f.value = "Value"
437
449
  f.to_html.should == "Value"
438
450
  f.to_pdf.should == "Value"
@@ -452,10 +464,10 @@ class FieldsTest < MiniTest::Spec
452
464
  end
453
465
 
454
466
  should "be available as the :markdown type" do
455
- assert MarkdownContent.field_prototypes[:text1].field_class < Spontaneous::FieldTypes::MarkdownField
467
+ assert MarkdownContent.field_prototypes[:text1].field_class < Spontaneous::Field::Markdown
456
468
  end
457
469
  should "be available as the :text type" do
458
- assert MarkdownContent.field_prototypes[:text2].field_class < Spontaneous::FieldTypes::MarkdownField
470
+ assert MarkdownContent.field_prototypes[:text2].field_class < Spontaneous::Field::Markdown
459
471
  end
460
472
 
461
473
  should "process input into HTML" do
@@ -473,18 +485,18 @@ class FieldsTest < MiniTest::Spec
473
485
 
474
486
  context "Editor classes" do
475
487
  should "be defined in base types" do
476
- base_class = Spontaneous::FieldTypes::ImageField
477
- base_class.editor_class.should == "Spontaneous.FieldTypes.ImageField"
478
- base_class = Spontaneous::FieldTypes::DateField
479
- base_class.editor_class.should == "Spontaneous.FieldTypes.DateField"
480
- base_class = Spontaneous::FieldTypes::MarkdownField
481
- base_class.editor_class.should == "Spontaneous.FieldTypes.MarkdownField"
482
- base_class = Spontaneous::FieldTypes::StringField
483
- base_class.editor_class.should == "Spontaneous.FieldTypes.StringField"
488
+ base_class = Spontaneous::Field::Image
489
+ base_class.editor_class.should == "Spontaneous.Field.Image"
490
+ base_class = Spontaneous::Field::Date
491
+ base_class.editor_class.should == "Spontaneous.Field.Date"
492
+ base_class = Spontaneous::Field::Markdown
493
+ base_class.editor_class.should == "Spontaneous.Field.Markdown"
494
+ base_class = Spontaneous::Field::String
495
+ base_class.editor_class.should == "Spontaneous.Field.String"
484
496
  end
485
497
 
486
498
  should "be inherited in subclasses" do
487
- base_class = Spontaneous::FieldTypes::ImageField
499
+ base_class = Spontaneous::Field::Image
488
500
  @field_class = Class.new(base_class)
489
501
  @field_class.stubs(:name).returns("CustomField")
490
502
  @field_class.editor_class.should == base_class.editor_class
@@ -493,17 +505,17 @@ class FieldsTest < MiniTest::Spec
493
505
  @field_class2.editor_class.should == base_class.editor_class
494
506
  end
495
507
  should "correctly defined by field prototypes" do
496
- base_class = Spontaneous::FieldTypes::ImageField
497
- class ::CustomField < Spontaneous::FieldTypes::ImageField
508
+ base_class = Spontaneous::Field::Image
509
+ class ::CustomField < Spontaneous::Field::Image
498
510
  self.register(:custom)
499
511
  end
500
512
 
501
- class ::CustomContent < Spontaneous::Piece
513
+ class ::CustomContent < ::Piece
502
514
  field :custom
503
515
  end
504
516
  assert CustomContent.fields.custom.instance_class < CustomField
505
517
 
506
- CustomContent.fields.custom.instance_class.editor_class.should == Spontaneous::FieldTypes::ImageField.editor_class
518
+ CustomContent.fields.custom.instance_class.editor_class.should == Spontaneous::Field::Image.editor_class
507
519
 
508
520
  Object.send(:remove_const, :CustomContent)
509
521
  Object.send(:remove_const, :CustomField)
@@ -525,8 +537,8 @@ class FieldsTest < MiniTest::Spec
525
537
  teardown do
526
538
  # Object.send(:remove_const, :Piece) rescue nil
527
539
  Spontaneous::Permissions::User.delete
528
- S::Content.delete
529
- S::FieldVersion.delete
540
+ ::Content.delete
541
+ S::Field::FieldVersion.delete
530
542
  end
531
543
 
532
544
  should "start out as empty" do
@@ -548,20 +560,23 @@ class FieldsTest < MiniTest::Spec
548
560
  @instance.reload
549
561
  vv = @instance.title.versions
550
562
  v = vv.first
551
- v.created_at.should == now
563
+ v.created_at.to_i.should == now.to_i
552
564
  end
553
565
 
554
566
  should "save the previous value" do
567
+ stub_time(@now)
555
568
  @instance.title.value = "one"
556
569
  @instance.save.reload
557
570
  vv = @instance.title.versions
558
571
  v = vv.first
559
572
  v.value.should == ""
573
+ stub_time(@now+10)
560
574
  @instance.title.value = "two"
561
575
  @instance.save.reload
562
576
  vv = @instance.title.versions
563
577
  v = vv.first
564
578
  v.value.should == "one"
579
+ stub_time(@now+20)
565
580
  @instance.title.value = "three"
566
581
  @instance.save.reload
567
582
  vv = @instance.title.versions
@@ -570,11 +585,13 @@ class FieldsTest < MiniTest::Spec
570
585
  end
571
586
 
572
587
  should "keep a track of the version number" do
588
+ stub_time(@now)
573
589
  @instance.title.value = "one"
574
590
  @instance.save.reload
575
591
  vv = @instance.title.versions
576
592
  v = vv.first
577
593
  v.version.should == 1
594
+ stub_time(@now+10)
578
595
  @instance.title.value = "two"
579
596
  @instance.save.reload
580
597
  vv = @instance.title.versions
@@ -593,11 +610,13 @@ class FieldsTest < MiniTest::Spec
593
610
  end
594
611
 
595
612
  should "have quick access to the last version" do
613
+ stub_time(@now)
596
614
  @instance.title.value = "one"
597
615
  @instance.save.reload
598
616
  vv = @instance.title.versions
599
617
  v = vv.first
600
618
  v.value.should == ""
619
+ stub_time(@now+10)
601
620
  @instance.title.value = "two"
602
621
  @instance.save.reload
603
622
  vv = @instance.title.versions
@@ -614,7 +633,7 @@ class FieldsTest < MiniTest::Spec
614
633
  field :something, :title
615
634
  end
616
635
  instance = @content_class.new
617
- assert instance.fields.title.class.ancestors.include?(Spontaneous::FieldTypes::StringField), ":title type should inherit from StringField"
636
+ assert instance.fields.title.class.ancestors.include?(Spontaneous::Field::String), ":title type should inherit from StringField"
618
637
  instance.title.value.should == "Right"
619
638
  end
620
639
  end
@@ -629,7 +648,7 @@ class FieldsTest < MiniTest::Spec
629
648
  end
630
649
 
631
650
  should "have their own editor type" do
632
- @content_class.fields.video.export(nil)[:type].should == "Spontaneous.FieldTypes.WebVideoField"
651
+ @content_class.fields.video.export(nil)[:type].should == "Spontaneous.Field.WebVideo"
633
652
  @instance.video = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
634
653
  fields = @instance.export(nil)[:fields]
635
654
  fields[0][:processed_value].should == @instance.video.src
@@ -705,7 +724,7 @@ class FieldsTest < MiniTest::Spec
705
724
 
706
725
  should "provide a version of the YouTube player params in JSON/JS format" do
707
726
  @field.value = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
708
- json = JSON.parse(@field.render(:json))
727
+ json = Spontaneous::JSON.parse(@field.render(:json))
709
728
  json[:"tagname"].should == "iframe"
710
729
  json[:"tag"].should == "<iframe/>"
711
730
  attr = json[:"attr"]
@@ -726,7 +745,7 @@ class FieldsTest < MiniTest::Spec
726
745
 
727
746
  should "provide a version of the Vimeo player params in JSON/JS format" do
728
747
  @field.value = "http://vimeo.com/31836285"
729
- json = JSON.parse(@field.render(:json))
748
+ json = Spontaneous::JSON.parse(@field.render(:json))
730
749
  json[:"tagname"].should == "iframe"
731
750
  json[:"tag"].should == "<iframe/>"
732
751
  attr = json[:"attr"]
@@ -781,7 +800,7 @@ class FieldsTest < MiniTest::Spec
781
800
  end
782
801
 
783
802
  should "use a standard string editor" do
784
- @content_class.fields.location.export(nil)[:type].should == "Spontaneous.FieldTypes.StringField"
803
+ @content_class.fields.location.export(nil)[:type].should == "Spontaneous.Field.String"
785
804
  end
786
805
 
787
806
  should "successfullt geolocate an address" do
@@ -816,12 +835,12 @@ class FieldsTest < MiniTest::Spec
816
835
  end
817
836
 
818
837
  should "use a specific editor class" do
819
- @content_class.fields.options.export(nil)[:type].should == "Spontaneous.FieldTypes.SelectField"
838
+ @content_class.fields.options.export(nil)[:type].should == "Spontaneous.Field.Select"
820
839
  end
821
840
 
822
841
  should "select the options class for fields named options" do
823
842
  @content_class.field :type, :select, :options => [["a", "A"]]
824
- assert @content_class.fields.options.instance_class.ancestors.include?(Spontaneous::FieldTypes::SelectField)
843
+ assert @content_class.fields.options.instance_class.ancestors.include?(Spontaneous::Field::Select)
825
844
  end
826
845
 
827
846
  should "accept a list of strings as options" do
@@ -849,11 +868,11 @@ class FieldsTest < MiniTest::Spec
849
868
  end
850
869
 
851
870
  should "have a distinct editor class" do
852
- @prototype.instance_class.editor_class.should == "Spontaneous.FieldTypes.FileField"
871
+ @prototype.instance_class.editor_class.should == "Spontaneous.Field.File"
853
872
  end
854
873
 
855
874
  should "adopt any field called 'file'" do
856
- assert @field.is_a?(Spontaneous::FieldTypes::FileField), "Field should be an instance of FileField but instead has the following ancestors #{ @prototype.instance_class.ancestors }"
875
+ assert @field.is_a?(Spontaneous::Field::File), "Field should be an instance of FileField but instead has the following ancestors #{ @prototype.instance_class.ancestors }"
857
876
  end
858
877
 
859
878
  should "copy files to the media folder" do
@@ -915,11 +934,11 @@ class FieldsTest < MiniTest::Spec
915
934
  end
916
935
 
917
936
  should "have a distinct editor class" do
918
- @prototype.instance_class.editor_class.should == "Spontaneous.FieldTypes.DateField"
937
+ @prototype.instance_class.editor_class.should == "Spontaneous.Field.Date"
919
938
  end
920
939
 
921
940
  should "adopt any field called 'date'" do
922
- assert @field.is_a?(Spontaneous::FieldTypes::DateField), "Field should be an instance of DateField but instead has the following ancestors #{ @prototype.instance_class.ancestors }"
941
+ assert @field.is_a?(Spontaneous::Field::Date), "Field should be an instance of DateField but instead has the following ancestors #{ @prototype.instance_class.ancestors }"
923
942
  end
924
943
 
925
944
  should "default to an empty string" do
@@ -943,5 +962,475 @@ class FieldsTest < MiniTest::Spec
943
962
  field.value(:plain).should == %(08 Jun 2012, Fri)
944
963
  end
945
964
  end
965
+
966
+ context "Asynchronous processing" do
967
+ setup do
968
+ S::Site.background_mode = :simultaneous
969
+ @image = File.expand_path("../../fixtures/images/rose.jpg", __FILE__)
970
+ @model = (::Piece)
971
+ @model.field :title
972
+ @model.field :image
973
+ @model.field :description, :markdown
974
+ @model.box :items do
975
+ field :title
976
+ field :image
977
+ end
978
+ @instance = @model.create
979
+ end
980
+
981
+ # should "be disabled if the background mode is set to immediate" do
982
+ # S::Site.background_mode = :immediate
983
+ # S::Field::Update.asynchronous_update_class.should == S::Field::Update::Immediate
984
+ # end
985
+
986
+ # should "be enabled if the background mode is set to simultaneous" do
987
+ # S::Site.background_mode = :simultaneous
988
+ # S::Field::Update.asynchronous_update_class.should == S::Field::Update::Simultaneous
989
+ # end
990
+
991
+ should "be able to resolve fields id" do
992
+ S::Field.find(@instance.image.id, @instance.items.title.id).should == [
993
+ @instance.image, @instance.items.title
994
+ ]
995
+ end
996
+
997
+ should "not raise errors for invalid fields" do
998
+ S::Field.find("0", "#{@instance.id}/xxx/#{@instance.items.title.schema_id}", "#{@instance.items.id}/nnn", @instance.items.title.id).should == [ @instance.items.title ]
999
+ end
1000
+
1001
+ should "return a single field if given a single id" do
1002
+ S::Field.find(@instance.image.id).should == @instance.image
1003
+ end
1004
+
1005
+ should "be disabled for Date fields" do
1006
+ f = S::Field::Date.new
1007
+ f.asynchronous?.should be_false
1008
+ end
1009
+
1010
+ should "be disabled for Location fields" do
1011
+ f = S::Field::Location.new
1012
+ f.asynchronous?.should be_false
1013
+ end
1014
+
1015
+ should "be disabled for LongString fields" do
1016
+ f = S::Field::LongString.new
1017
+ f.asynchronous?.should be_false
1018
+ end
1019
+
1020
+ should "be disabled for Markdown fields" do
1021
+ f = S::Field::Markdown.new
1022
+ f.asynchronous?.should be_false
1023
+ end
1024
+
1025
+ should "be disabled for Select fields" do
1026
+ f = S::Field::Select.new
1027
+ f.asynchronous?.should be_false
1028
+ end
1029
+
1030
+ should "be disabled for String fields" do
1031
+ f = S::Field::String.new
1032
+ f.asynchronous?.should be_false
1033
+ end
1034
+
1035
+ should "be disabled for WebVideo fields" do
1036
+ f = S::Field::WebVideo.new
1037
+ f.asynchronous?.should be_false
1038
+ end
1039
+
1040
+ should "be enabled for File fields" do
1041
+ f = S::Field::File.new
1042
+ f.asynchronous?.should be_true
1043
+ end
1044
+
1045
+ should "be enabled for Image fields" do
1046
+ f = S::Field::Image.new
1047
+ f.asynchronous?.should be_true
1048
+ end
1049
+
1050
+ should "immediately update a group of fields passed in parameter format" do
1051
+ File.open(@image, "r") do |file|
1052
+ fields = {
1053
+ @instance.title.schema_id.to_s => "Updated title",
1054
+ @instance.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"},
1055
+ @instance.description.schema_id.to_s => "Updated description"
1056
+ }
1057
+ Spontaneous::Field.update(@instance, fields, nil, false)
1058
+ @instance.reload
1059
+ @instance.title.value.should == "Updated title"
1060
+ @instance.description.value.should == "<p>Updated description</p>\n"
1061
+ @instance.image.value.should == "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1062
+ end
1063
+ end
1064
+
1065
+ should "asynchronously update a group of fields passed in parameter format" do
1066
+ Spontaneous::Simultaneous.expects(:fire).with(:update_fields, {
1067
+ "fields" => [@instance.image.id]
1068
+ })
1069
+
1070
+ File.open(@image, "r") do |file|
1071
+ fields = {
1072
+ @instance.title.schema_id.to_s => "Updated title",
1073
+ @instance.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"},
1074
+ @instance.description.schema_id.to_s => "Updated description"
1075
+ }
1076
+ @instance.expects(:save).at_least_once
1077
+
1078
+ Spontaneous::Field.update(@instance, fields, nil, true)
1079
+
1080
+ @instance.title.value.should == "Updated title"
1081
+ @instance.description.value.should == "<p>Updated description</p>\n"
1082
+ @instance.image.value.should == ""
1083
+ @instance.image.pending_value.should == {
1084
+ :timestamp => S::Field.timestamp(@now),
1085
+ :version => 1,
1086
+ :value => {
1087
+ :width=>400, :height=>533, :filesize=>54746,
1088
+ :type=>"image/gif",
1089
+ :tempfile=>"#{@site.root}/cache/media/tmp/#{S::Media.pad_id(@instance.id)}/something.gif",
1090
+ :filename=>"something.gif",
1091
+ :src => "/media/tmp/#{S::Media.pad_id(@instance.id)}/something.gif"
1092
+ }
1093
+ }
1094
+ @instance.image.process_pending_value
1095
+ @instance.image.value.should == "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1096
+ @instance.image.pending_value.should be_nil
1097
+ end
1098
+ end
1099
+
1100
+ should "asynchronously update a single field value" do
1101
+ Spontaneous::Simultaneous.expects(:fire).with(:update_fields, {
1102
+ "fields" => [@instance.image.id]
1103
+ })
1104
+ File.open(@image, "r") do |file|
1105
+ @instance.image.pending_version.should == 0
1106
+ Spontaneous::Field.set(@instance.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1107
+ @instance.image.value.should == ""
1108
+ @instance.image.pending_value.should == {
1109
+ :timestamp => S::Field.timestamp(@now),
1110
+ :version => 1,
1111
+ :value => {
1112
+ :width=>400, :height=>533, :filesize=>54746,
1113
+ :type=>"image/gif",
1114
+ :tempfile=>"#{@site.root}/cache/media/tmp/#{S::Media.pad_id(@instance.id)}/something.gif",
1115
+ :filename=>"something.gif",
1116
+ :src => "/media/tmp/#{S::Media.pad_id(@instance.id)}/something.gif"
1117
+ }
1118
+ }
1119
+ @instance.image.pending_version.should == 1
1120
+ @instance.image.process_pending_value
1121
+ @instance.image.value.should == "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1122
+ end
1123
+ end
1124
+
1125
+ should "synchronously update box fields" do
1126
+ box = @instance.items
1127
+ File.open(@image, "r") do |file|
1128
+ fields = {
1129
+ box.title.schema_id.to_s => "Updated title",
1130
+ box.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"},
1131
+ }
1132
+ Spontaneous::Field.update(box, fields, nil, false)
1133
+ box.title.value.should == "Updated title"
1134
+ box.image.value.should == "/media/#{S::Media.pad_id(@instance.id)}/#{box.schema_id}/0001/something.gif"
1135
+ box.image.pending_version.should == 1
1136
+ end
1137
+ end
1138
+
1139
+ should "asynchronously update box fields" do
1140
+ box = @instance.items
1141
+ Spontaneous::Simultaneous.expects(:fire).with(:update_fields, {
1142
+ "fields" => [box.image.id]
1143
+ })
1144
+ File.open(@image, "r") do |file|
1145
+ fields = {
1146
+ box.title.schema_id.to_s => "Updated title",
1147
+ box.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"},
1148
+ }
1149
+ Spontaneous::Field.update(box, fields, nil, true)
1150
+ box.title.value.should == "Updated title"
1151
+ box.image.value.should == ""
1152
+ box.image.pending_value.should == {
1153
+ :timestamp => S::Field.timestamp(@now),
1154
+ :version => 1,
1155
+ :value => {
1156
+ :width=>400, :height=>533, :filesize=>54746,
1157
+ :type=>"image/gif",
1158
+ :tempfile=>"#{@site.root}/cache/media/tmp/#{S::Media.pad_id(@instance.id)}/#{box.schema_id}/something.gif",
1159
+ :filename=>"something.gif",
1160
+ :src => "/media/tmp/#{S::Media.pad_id(@instance.id)}/#{box.schema_id}/something.gif"
1161
+ }
1162
+ }
1163
+ end
1164
+ end
1165
+
1166
+ should "immediately update asynchronous fields if background mode is :immediate" do
1167
+ S::Site.background_mode = :immediate
1168
+ File.open(@image, "r") do |file|
1169
+ fields = {
1170
+ @instance.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"}
1171
+ }
1172
+ Spontaneous::Simultaneous.expects(:fire).never
1173
+ Spontaneous::Field.update(@instance, fields, nil, true)
1174
+ @instance.image.value.should == "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1175
+ end
1176
+ end
1177
+
1178
+ should "not update a field if user does not have necessary permissions" do
1179
+ user = mock()
1180
+ @instance.title.expects(:writable?).with(user).at_least_once.returns(false)
1181
+ fields = {
1182
+ @instance.title.schema_id.to_s => "Updated title"
1183
+ }
1184
+ Spontaneous::Field.update(@instance, fields, user, true)
1185
+ @instance.title.value.should == ""
1186
+ end
1187
+
1188
+ should "call Fields::Update::Immediate from the cli" do
1189
+ immediate = mock()
1190
+ immediate.expects(:pages).returns([])
1191
+ immediate.expects(:run)
1192
+ Spontaneous::Field::Update::Immediate.expects(:new).with([@instance.image, @instance.items.title]).returns(immediate)
1193
+ # Thor generates a warning about creating a task with no 'desc'
1194
+ silence_logger {
1195
+ Spontaneous::Cli::Fields.any_instance.stubs(:prepare!)
1196
+ }
1197
+ Spontaneous::Cli::Fields.start(["update", "--fields", @instance.image.id, @instance.items.title.id])
1198
+ end
1199
+
1200
+ should "call Fields::Update::Immediate from the cli with a single field" do
1201
+ silence_logger {
1202
+ Spontaneous::Cli::Fields.any_instance.stubs(:prepare!)
1203
+ }
1204
+ Spontaneous::Cli::Fields.start(["update", "--fields", @instance.image.id])
1205
+ end
1206
+
1207
+ should "revert to immediate updating if connection to simultaneous fails" do
1208
+ File.open(@image, "r") do |file|
1209
+ Spontaneous::Field.set(@instance.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1210
+ @instance.image.value.should == "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1211
+ @instance.image.pending_value.should be_nil
1212
+ end
1213
+ end
1214
+
1215
+ context "page locks" do
1216
+ setup do
1217
+ @now = Time.now
1218
+ stub_time(@now)
1219
+ LockedPage = Class.new(::Page)
1220
+ LockedPage.field :image
1221
+ LockedPage.box :instances do
1222
+ field :image
1223
+ field :title
1224
+ end
1225
+ LockedPiece = @model
1226
+ @page = LockedPage.create
1227
+ @instance = LockedPiece.create
1228
+ @page.instances << @instance
1229
+ @page.save.reload
1230
+ @instance.save.reload
1231
+ # The PageLock associations cache the Content model
1232
+ # but since this changes every time later tests
1233
+ # use an old version of Content with an old schema
1234
+ S::PageLock.all_association_reflections.each do |r|
1235
+ # Clear the cached class
1236
+ r[:cache] = {}
1237
+ end
1238
+ end
1239
+
1240
+ teardown do
1241
+ Spontaneous::PageLock.delete
1242
+ self.class.send :remove_const, :LockedPage rescue nil
1243
+ self.class.send :remove_const, :LockedPiece rescue nil
1244
+ end
1245
+
1246
+ should "be created when scheduling a page field for async updating" do
1247
+ Spontaneous::Simultaneous.expects(:fire).with(:update_fields, {
1248
+ "fields" => [@page.image.id]
1249
+ })
1250
+ File.open(@image, "r") do |file|
1251
+ Spontaneous::Field.set(@page.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1252
+ @page.image.value.should == ""
1253
+ @page.update_locks.length.should == 1
1254
+ lock = @page.update_locks.first
1255
+ lock.field.should == @page.image
1256
+ lock.content.should == @page
1257
+ lock.page.should == @page
1258
+ lock.description.should =~ /something\.gif/
1259
+ lock.created_at.should == @now
1260
+ lock.location.should == "Field ‘image’"
1261
+ @page.locked_for_update?.should be_true
1262
+ end
1263
+ end
1264
+
1265
+ should "not create locks for fields processed immediately" do
1266
+ field = @instance.title
1267
+ Spontaneous::Field.set(field, "Updated Title", nil, true)
1268
+ field.value.should == "Updated Title"
1269
+ @page.update_locks.length.should == 0
1270
+ @page.locked_for_update?.should be_false
1271
+ end
1272
+
1273
+ should "be created when scheduling a box field for async updating" do
1274
+ field = @page.instances.image
1275
+ Spontaneous::Simultaneous.expects(:fire).with(:update_fields, {
1276
+ "fields" => [field.id]
1277
+ })
1278
+ File.open(@image, "r") do |file|
1279
+ Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1280
+ field.value.should == ""
1281
+ @page.update_locks.length.should == 1
1282
+ lock = @page.update_locks.first
1283
+ lock.field.should == field.reload
1284
+ lock.content.should == @page.reload
1285
+ lock.page.should == @page
1286
+ lock.description.should =~ /something\.gif/
1287
+ lock.created_at.should == @now
1288
+ lock.location.should == "Field ‘image’ of box ‘instances’"
1289
+ @page.locked_for_update?.should be_true
1290
+ end
1291
+ end
1292
+
1293
+ should "be created when scheduling a piece field for async updating" do
1294
+ field = @instance.image
1295
+ Spontaneous::Simultaneous.expects(:fire).with(:update_fields, {
1296
+ "fields" => [field.id]
1297
+ })
1298
+ File.open(@image, "r") do |file|
1299
+ Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1300
+ field.value.should == ""
1301
+ @page.update_locks.length.should == 1
1302
+ lock = @page.update_locks.first
1303
+ lock.field.should == field
1304
+ lock.content.should == @instance
1305
+ lock.page.should == @page
1306
+ lock.description.should =~ /something\.gif/
1307
+ lock.created_at.should == @now
1308
+ lock.location.should == "Field ‘image’ of entry 1 in box ‘instances’"
1309
+ @page.locked_for_update?.should be_true
1310
+ end
1311
+ end
1312
+
1313
+ should "be removed when the field has been processed" do
1314
+ Spontaneous::Simultaneous.expects(:fire).with(:update_fields, {
1315
+ "fields" => [@page.image.id]
1316
+ })
1317
+ File.open(@image, "r") do |file|
1318
+ Spontaneous::Field.set(@page.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1319
+ @page.image.value.should == ""
1320
+ @page.update_locks.length.should == 1
1321
+ @page.locked_for_update?.should be_true
1322
+ # The lock manipulation is done by the updater
1323
+ # so calling update_pending_value on the field
1324
+ # won't clear any locks
1325
+ Spontaneous::Field::Update::Immediate.process([@page.image])
1326
+ @page.image.value.should == "/media/#{@page.id.to_s.rjust(5, "0")}/0001/something.gif"
1327
+ @page.reload.locked_for_update?.should be_false
1328
+ end
1329
+ end
1330
+
1331
+ should "send a completion event that includes a list of unlocked pages" do
1332
+ field = @instance.image
1333
+ Spontaneous::Simultaneous.expects(:fire).with(:update_fields, {
1334
+ "fields" => [field.id]
1335
+ })
1336
+ Simultaneous.expects(:send_event).with('page_lock_status', "[#{@page.id}]")
1337
+
1338
+ File.open(@image, "r") do |file|
1339
+ Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1340
+ @page.locked_for_update?.should be_true
1341
+ silence_logger {
1342
+ Spontaneous::Cli::Fields.any_instance.stubs(:prepare!)
1343
+ }
1344
+ Spontaneous::Cli::Fields.start(["update", "--fields", field.id])
1345
+ end
1346
+ end
1347
+
1348
+ should "ignore an update that has been superceded" do
1349
+ # user uploads an image and then changes their mind and uploads another
1350
+ # before the first one has been processed.
1351
+ # Pending value might have changed between the start of the update and the end
1352
+ # especially in the case of video processing or file upload
1353
+ # Before we update the value of a field or
1354
+ # clear pending values we need to be sure that they aren't still needed
1355
+ #
1356
+
1357
+ field = @instance.image
1358
+ Spontaneous::Simultaneous.expects(:fire).at_least_once.with(:update_fields, {
1359
+ "fields" => [field.id]
1360
+ })
1361
+ File.open(@image, "r") do |file|
1362
+ Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1363
+ end
1364
+ update = Spontaneous::Field::Update::Immediate.new(field)
1365
+ old, field = field, field.reload
1366
+ later = @now + 1
1367
+ t = S::Field.timestamp(later)
1368
+ S::Field.stubs(:timestamp).returns(t)
1369
+ File.open(@image, "r") do |file|
1370
+ Spontaneous::Field.set(field, {:tempfile => file, :filename => "else.gif", :type => "image/jpeg"}, nil, true)
1371
+ end
1372
+ update.run
1373
+
1374
+ pending = field.pending_value
1375
+ pending[:value][:filename].should == "else.gif"
1376
+ end
1377
+
1378
+ should "merge async updates with synchronous ones effected during processing" do
1379
+ # Scenario:
1380
+ # - User uploads file to content item which gets scheduled for async processing
1381
+ # - User modifies synchronous field of same content item that gets immediately updated
1382
+ # - Async process completes and...
1383
+ # SHOULD
1384
+ # Keep the updated values from the immediate change
1385
+ # Merge in the results of the async change
1386
+ field = @instance.image
1387
+ Spontaneous::Simultaneous.expects(:fire).at_least_once.with(:update_fields, {
1388
+ "fields" => [field.id]
1389
+ })
1390
+ File.open(@image, "r") do |file|
1391
+ Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1392
+ end
1393
+ # Create update but don't run it
1394
+ update = Spontaneous::Field::Update::Immediate.new(field)
1395
+ # Someone updates a field before the async update is run...
1396
+ content = ::Content.get(@instance.id)
1397
+ content.title = "Updated Title"
1398
+ content.save
1399
+
1400
+ # Now run the update with a field that's out of sync with the version in the db
1401
+ update.run
1402
+
1403
+ content = ::Content.get(@instance.id)
1404
+ content.title.value.should == "Updated Title"
1405
+ content.image.value.should == "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1406
+ end
1407
+
1408
+ should "merge async updates to box fields with synchronous ones effected during processing" do
1409
+ # The scenario for boxes is more complex because their fields are stored by their owner
1410
+ # not directly by themselves
1411
+ field = @page.instances.image
1412
+ Spontaneous::Simultaneous.expects(:fire).at_least_once.with(:update_fields, {
1413
+ "fields" => [field.id]
1414
+ })
1415
+ File.open(@image, "r") do |file|
1416
+ Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1417
+ end
1418
+ # Create update but don't run it
1419
+ update = Spontaneous::Field::Update::Immediate.new(field)
1420
+ # Someone updates a field before the async update is run...
1421
+ content = ::Content.get(@page.id)
1422
+ content.instances.title = "Updated Title"
1423
+ content.save
1424
+
1425
+ # Now run the update with a field that's out of sync with the version in the db
1426
+ update.run
1427
+
1428
+
1429
+ content = ::Content.get(@page.id)
1430
+ content.instances.title.value.should == "Updated Title"
1431
+ content.instances.image.value.should == "/media/#{S::Media.pad_id(@page.id)}/#{@page.instances.schema_id}/0001/something.gif"
1432
+ end
1433
+ end
1434
+ end
946
1435
  end
947
1436
  end