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
@@ -44,10 +44,12 @@ describe "Front" do
44
44
  end
45
45
 
46
46
  class ::DynamicRequestParams < ::Page
47
+ singleton
47
48
  layout(:html) { "{{ params[:horse] }}*{{ equine }}" }
48
49
  end
49
50
 
50
51
  class ::DynamicRenderParams < ::Page
52
+ singleton
51
53
  add_output :mobile
52
54
  add_output :session
53
55
  add_output :params
@@ -101,6 +103,8 @@ describe "Front" do
101
103
 
102
104
  Content.delete_revision(1) rescue nil
103
105
 
106
+ site.publish_steps = Spontaneous::Publishing::Steps.default
107
+
104
108
  Spontaneous.logger.silent! {
105
109
  site.publish_all
106
110
  }
@@ -271,7 +275,7 @@ describe "Front" do
271
275
  get { render "caterpillars" }
272
276
  end
273
277
  get '/dynamic-render-params'
274
- assert last_response.status == 404
278
+ assert last_response.status == 404, "Expected 404 but got #{last_response.status}"
275
279
  end
276
280
 
277
281
  it "return the right status code" do
@@ -325,7 +329,7 @@ describe "Front" do
325
329
 
326
330
  it "allows setting status code and passing parameters to the show call" do
327
331
  DynamicRenderParams.controller do
328
- get { render "$dynamic_render_params", 401, :teeth => "white" }
332
+ get { render DynamicRenderParams, 401, :teeth => "white" }
329
333
  end
330
334
  get '/dynamic-render-params'
331
335
  assert last_response.status == 401
@@ -334,7 +338,7 @@ describe "Front" do
334
338
 
335
339
  it "allows passing parameters to the render call" do
336
340
  DynamicRenderParams.controller do
337
- get { render "$dynamic_render_params", :teeth => "white" }
341
+ get { render DynamicRenderParams, :teeth => "white" }
338
342
  end
339
343
  get '/dynamic-render-params'
340
344
  assert last_response.status == 200
data/test/test_helper.rb CHANGED
@@ -17,6 +17,7 @@ $:.unshift(test_path) if File.directory?(test_path) && !$:.include?(test_path)
17
17
  $:.unshift(spot_path) if File.directory?(spot_path) && !$:.include?(spot_path)
18
18
 
19
19
  require 'rack'
20
+ require 'timecop'
20
21
  require 'logger'
21
22
 
22
23
  Sequel.extension :migration
@@ -25,38 +26,46 @@ Sequel.extension :migration
25
26
  # The scissors plugin adds class methods for update, delete, and destroy
26
27
  Sequel::Model.plugin :scissors
27
28
 
28
- # for future integration with travis
29
- ENV["SPOT_ADAPTER"] ||= "postgres"
29
+ ENV["SPOT_ADAPTER"] ||= "sqlite"
30
30
 
31
31
  jruby = case RUBY_PLATFORM
32
- when "java"
33
- true
34
- else
35
- false
36
- end
32
+ when "java"
33
+ true
34
+ else
35
+ false
36
+ end
37
37
 
38
38
 
39
39
  connection_string = \
40
- case ENV["SPOT_ADAPTER"]
41
- when "postgres"
42
- if jruby
43
- require 'jdbc/postgres'
44
- Jdbc::Postgres.load_driver
45
- "jdbc:postgresql:///spontaneous2_test"
46
- else
47
- require 'pg'
48
- "postgres:///spontaneous2_test"
49
- end
50
- when "mysql"
51
- if jruby
52
- require 'jdbc/mysql'
53
- Jdbc::MySQL.load_driver
54
- "jdbc:mysql://localhost/spontaneous2_test?user=root"
55
- else
56
- require 'mysql2'
57
- "mysql2://root@localhost/spontaneous2_test"
58
- end
40
+ case ENV["SPOT_ADAPTER"]
41
+ when "postgres"
42
+ if jruby
43
+ require 'jdbc/postgres'
44
+ Jdbc::Postgres.load_driver
45
+ "jdbc:postgresql:///spontaneous2_test"
46
+ else
47
+ require 'pg'
48
+ "postgres:///spontaneous2_test"
49
+ end
50
+ when "mysql"
51
+ if jruby
52
+ require 'jdbc/mysql'
53
+ Jdbc::MySQL.load_driver
54
+ "jdbc:mysql://localhost/spontaneous2_test?user=root"
55
+ else
56
+ require 'mysql2'
57
+ "mysql2://root@localhost/spontaneous2_test"
58
+ end
59
+ when "sqlite"
60
+ if jruby
61
+ require 'jdbc/sqlite3'
62
+ Jdbc::SQLite3.load_driver
63
+ "jdbc:sqlite::memory:"
64
+ else
65
+ require 'sqlite3'
66
+ "sqlite:/" # in-memory
59
67
  end
68
+ end
60
69
 
61
70
  puts "SPOT_ADAPTER=#{ENV["SPOT_ADAPTER"]} => #{connection_string}"
62
71
 
@@ -240,5 +249,3 @@ class MiniTest::Spec
240
249
  end
241
250
 
242
251
  require 'minitest/autorun'
243
-
244
-
@@ -321,9 +321,6 @@ describe "Alias" do
321
321
  end
322
322
 
323
323
 
324
- # TODO
325
- it "reference the aliases fields before the targets"
326
-
327
324
  it "present their target's fields as their own" do
328
325
  assert @a_alias.field?(:a_field1)
329
326
  @a_alias.a_field1.value.must_equal a.a_field1.value
@@ -333,6 +330,14 @@ describe "Alias" do
333
330
  @a_alias.target.a_field1.value.must_equal a.a_field1.value
334
331
  end
335
332
 
333
+ it "provide transparent access target's fields" do
334
+ @a_alias.a_field1.value.must_equal a.a_field1.value
335
+ end
336
+
337
+ it "provide transparent access target's fields with hash notation" do
338
+ @a_alias.fields[:a_field1].value.must_equal a.a_field1.value
339
+ end
340
+
336
341
  it "have their own styles" do
337
342
  assert_correct_template(@a_alias, template_root / 'a_alias/a_alias_style', renderer)
338
343
  end
@@ -477,6 +482,18 @@ describe "Alias" do
477
482
  bl.path.must_equal "/aliases/newb/newb"
478
483
  end
479
484
 
485
+ it "include target values in serialisation" do
486
+ al = BAlias.create(:target => b)
487
+ al.export[:target].must_equal b.shallow_export(nil)
488
+ end
489
+
490
+ it "include target values in entry serialisation" do
491
+ al = BAlias.create(:target => b)
492
+ aliases.box1 << al
493
+ aliases.save
494
+ al.entry.export[:target].must_equal b.shallow_export(nil)
495
+ end
496
+
480
497
  it "update their path if their parent's path changes" do
481
498
  al = BAlias.create(:target => b, :slug => "balias")
482
499
  bl = BAlias.create(:target => b, :slug => "balias")
@@ -93,6 +93,8 @@ describe "Assets" do
93
93
  end
94
94
 
95
95
  after do
96
+ tmp = site.path('assets/tmp')
97
+ FileUtils.rm_r(tmp) if tmp.exist?
96
98
  Content.delete
97
99
  end
98
100
 
@@ -221,7 +223,7 @@ describe "Assets" do
221
223
  result = last_response.body
222
224
  result.must_match /var a = 1/
223
225
  result.must_match /var b = 2/
224
- result.must_match /alert\("I knew it!"\);/
226
+ result.must_match %r{alert\("I knew it!"\);}
225
227
  end
226
228
 
227
229
  it "should preprocess coffeescript" do
@@ -235,7 +237,7 @@ describe "Assets" do
235
237
  get "/assets/x.js"
236
238
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
237
239
  result = last_response.body
238
- result.must_match /var x = 1;/
240
+ result.must_match %r{var x = 1;}
239
241
  end
240
242
 
241
243
  it "should use absolute URLs when encountered" do
@@ -273,15 +275,15 @@ describe "Assets" do
273
275
  get "/assets/css/all.css"
274
276
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
275
277
  result = last_response.body
276
- result.must_match /height: 42px;/
277
- result.must_match /width: 8px;/
278
+ result.must_match %r{height: 42px;}
279
+ result.must_match %r{width: 8px;}
278
280
  end
279
281
 
280
282
  it "should compile sass" do
281
283
  get "/assets/css/b.css"
282
284
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
283
285
  result = last_response.body
284
- result.must_match /height: 42px;/
286
+ result.must_match %r{height: 42px;}
285
287
  end
286
288
 
287
289
  it "links to images" do
@@ -295,7 +297,7 @@ describe "Assets" do
295
297
  get "/assets/css/missing.css"
296
298
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
297
299
  result = last_response.body
298
- result.must_match /background: url\(i\/missing\.png\)/
300
+ result.must_match %r{background: url\(i\/missing\.png\)}
299
301
  end
300
302
 
301
303
  it "can understand urls with hashes" do
@@ -337,7 +339,7 @@ describe "Assets" do
337
339
  describe "publishing" do
338
340
  let(:app) { Spontaneous::Rack::Front.application(site) }
339
341
  let(:context) { live_context }
340
- let(:revision) { S::Revision.new(context.revision) }
342
+ let(:revision) { site.revision(context.revision) }
341
343
 
342
344
  before do
343
345
  FileUtils.rm_f(Spontaneous.revision_dir) if File.exist?(Spontaneous.revision_dir)
@@ -350,25 +352,27 @@ describe "Assets" do
350
352
  end
351
353
 
352
354
  describe "javascript" do
355
+ let(:all_sha) { "b1d3d85feff4d68e16d2b4da97717aa0" }
356
+ let(:x_sha) { "cf638f60cff3ce84f156cb4621a914b4" }
353
357
  it "bundles & fingerprints local scripts" do
354
358
  result = context.scripts('js/all', 'js/m.js', 'js/c.js', 'x')
355
359
  result.must_equal [
356
- '<script type="text/javascript" src="/assets/js/all-ceb12af81123a85bc4d15f23c2cf1069.js"></script>',
357
- '<script type="text/javascript" src="/assets/js/m-262ca357844c1eeaee7688fe5bafa4ca.js"></script>',
358
- '<script type="text/javascript" src="/assets/js/c-e405b6a6ea9a29a46905bd10627a53a8.js"></script>',
359
- '<script type="text/javascript" src="/assets/x-e613ff07e2bd683e0c9f519c94abcb00.js"></script>'
360
+ %(<script type="text/javascript" src="/assets/js/all-#{all_sha}.js"></script>),
361
+ '<script type="text/javascript" src="/assets/js/m-424bd92768589875beac31e333399631.js"></script>',
362
+ '<script type="text/javascript" src="/assets/js/c-ac0d40982cc84fc656234ef2a57e09e8.js"></script>',
363
+ %(<script type="text/javascript" src="/assets/x-#{x_sha}.js"></script>)
360
364
  ].join("\n")
361
365
  end
362
366
 
363
367
  it "writes bundled assets to the revision directory" do
364
368
  result = context.scripts('js/all')
365
- asset_path = revision.path("assets/js/all-ceb12af81123a85bc4d15f23c2cf1069.js")
369
+ asset_path = revision.path("assets/js/all-#{all_sha}.js")
366
370
  assert asset_path.exist?
367
371
  end
368
372
 
369
373
  it "compresses local scripts" do
370
374
  result = context.scripts('js/all')
371
- asset_path = revision.path("assets/js/all-ceb12af81123a85bc4d15f23c2cf1069.js")
375
+ asset_path = revision.path("assets/js/all-#{all_sha}.js")
372
376
  js = asset_path.read
373
377
  js.index("\n").must_be_nil
374
378
  end
@@ -376,23 +380,23 @@ describe "Assets" do
376
380
  it "bundles locals scripts and includes remote ones" do
377
381
  result = context.scripts('js/all', '//use.typekit.com/abcde', 'http://cdn.google.com/jquery.js', 'x')
378
382
  result.must_equal [
379
- '<script type="text/javascript" src="/assets/js/all-ceb12af81123a85bc4d15f23c2cf1069.js"></script>',
383
+ %(<script type="text/javascript" src="/assets/js/all-#{all_sha}.js"></script>),
380
384
  '<script type="text/javascript" src="//use.typekit.com/abcde"></script>',
381
385
  '<script type="text/javascript" src="http://cdn.google.com/jquery.js"></script>',
382
- '<script type="text/javascript" src="/assets/x-e613ff07e2bd683e0c9f519c94abcb00.js"></script>'
386
+ %(<script type="text/javascript" src="/assets/x-#{x_sha}.js"></script>)
383
387
  ].join("\n")
384
388
  end
385
389
 
386
390
  it "makes bundled scripts available under /assets" do
387
391
  context.scripts('js/all')
388
- get "/assets/js/all-ceb12af81123a85bc4d15f23c2cf1069.js"
389
- asset_path = revision.path("assets/js/all-ceb12af81123a85bc4d15f23c2cf1069.js")
392
+ get "/assets/js/all-#{all_sha}.js"
393
+ asset_path = revision.path("assets/js/all-#{all_sha}.js")
390
394
  last_response.body.must_equal asset_path.read
391
395
  end
392
396
 
393
397
  it "only bundles & compresses once" do
394
398
  context.scripts('js/all')
395
- asset_path = revision.path("assets/js/all-ceb12af81123a85bc4d15f23c2cf1069.js")
399
+ asset_path = revision.path("assets/js/all-#{all_sha}.js")
396
400
  assert asset_path.exist?
397
401
  asset_path.open("w") do |file|
398
402
  file.write("var cached = true;")
@@ -400,22 +404,44 @@ describe "Assets" do
400
404
  context.scripts('js/all')
401
405
  asset_path.read.must_equal "var cached = true;"
402
406
  end
407
+ describe "re-use" do
408
+ before do
409
+ @result = context.scripts('js/all', 'x')
410
+ end
411
+
412
+ it "uses assets from a previous publish if present" do
413
+ context = live_context
414
+ def context.revision; 100 end
415
+ revision = site.revision(context.revision)
416
+ manifest = Spontaneous::JSON.parse File.read(site.path("assets/tmp") + "manifest.json")
417
+ compiled = manifest[:assets][:"js/all.js"]
418
+ ::File.open(site.path("assets/tmp")+compiled, 'w') do |file|
419
+ file.write("var reused = true;")
420
+ end
421
+ result = context.scripts('js/all', 'x')
422
+ rev = revision.path("assets") + compiled
423
+ File.read(rev).must_equal "var reused = true;"
424
+ end
425
+ end
403
426
  end
404
427
 
405
428
  describe "css" do
429
+ let(:all_sha) { "2468ffc5102b6bfcaf69a4fc8db59fdd" }
430
+ let(:x_sha) { "ae3ee1dc79a34d24e28456118c1b9623" }
431
+
406
432
  it "bundles & fingerprints local stylesheets" do
407
433
  result = context.stylesheets('css/all', 'css/a.css', 'x')
408
434
  result.must_equal [
409
- '<link rel="stylesheet" href="/assets/css/all-c80e50bd55af79647f771d421c0e732a.css" />',
410
- '<link rel="stylesheet" href="/assets/css/a-2709bcba844511c3afc19fa1805e5834.css" />',
411
- '<link rel="stylesheet" href="/assets/x-6c1e7ede5bbe3a4ff80314f43fd6ef57.css" />'
435
+ %(<link rel="stylesheet" href="/assets/css/all-#{all_sha}.css" />),
436
+ '<link rel="stylesheet" href="/assets/css/a-35b26d0cd9c7ebff494c8627e0d4ed14.css" />',
437
+ %(<link rel="stylesheet" href="/assets/x-#{x_sha}.css" />)
412
438
  ].join("\n")
413
439
  end
414
440
 
415
441
  it "ignores missing stylesheets" do
416
442
  result = context.stylesheets('css/all', '/css/notfound', 'css/notfound')
417
443
  result.must_equal [
418
- '<link rel="stylesheet" href="/assets/css/all-c80e50bd55af79647f771d421c0e732a.css" />',
444
+ %(<link rel="stylesheet" href="/assets/css/all-#{all_sha}.css" />),
419
445
  '<link rel="stylesheet" href="/css/notfound" />',
420
446
  '<link rel="stylesheet" href="css/notfound" />'
421
447
  ].join("\n")
@@ -424,10 +450,10 @@ describe "Assets" do
424
450
  it "bundles locals scripts and includes remote ones" do
425
451
  result = context.stylesheets('css/all.css', '//stylesheet.com/responsive', 'http://cdn.google.com/normalize.css', 'x')
426
452
  result.must_equal [
427
- '<link rel="stylesheet" href="/assets/css/all-c80e50bd55af79647f771d421c0e732a.css" />',
453
+ %(<link rel="stylesheet" href="/assets/css/all-#{all_sha}.css" />),
428
454
  '<link rel="stylesheet" href="//stylesheet.com/responsive" />',
429
455
  '<link rel="stylesheet" href="http://cdn.google.com/normalize.css" />',
430
- '<link rel="stylesheet" href="/assets/x-6c1e7ede5bbe3a4ff80314f43fd6ef57.css" />'
456
+ %(<link rel="stylesheet" href="/assets/x-#{x_sha}.css" />)
431
457
  ].join("\n")
432
458
  end
433
459
 
@@ -473,15 +499,17 @@ describe "Assets" do
473
499
  end
474
500
  end
475
501
 
502
+ let(:y_sha) { "e2b6a69468b467c7414ae0e12124a66e" }
503
+
476
504
  describe "images" do
477
505
  it "bundles images and links using fingerprinted asset url" do
478
506
  path = context.stylesheet_urls('css/image1').first
479
507
  get path
480
508
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
481
509
  result = last_response.body
482
- result.must_match %r{background:url\(/assets/i/y-e75e0b52344f0e16c0564a3a7b0b8ace\.png\)}
510
+ result.must_match %r{background:url\(/assets/i/y-#{y_sha}\.png\)}
483
511
 
484
- asset_path = revision.path("/assets/i/y-e75e0b52344f0e16c0564a3a7b0b8ace.png")
512
+ asset_path = revision.path("/assets/i/y-#{y_sha}.png")
485
513
  assert asset_path.exist?
486
514
  end
487
515
 
@@ -498,8 +526,8 @@ describe "Assets" do
498
526
  get path
499
527
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
500
528
  result = last_response.body
501
- result.must_match %r{background:url\(/assets/i/y-e75e0b52344f0e16c0564a3a7b0b8ace\.png\?query=true#hash\)}
502
- asset_path = revision.path("/assets/i/y-e75e0b52344f0e16c0564a3a7b0b8ace.png")
529
+ result.must_match %r{background:url\(/assets/i/y-#{y_sha}\.png\?query=true#hash\)}
530
+ asset_path = revision.path("/assets/i/y-#{y_sha}.png")
503
531
  assert asset_path.exist?
504
532
  end
505
533
  end
@@ -509,11 +537,11 @@ describe "Assets" do
509
537
 
510
538
  it "should allow for embedding asset images into templates" do
511
539
  result = renderer.render_string("${ asset_path 'i/y.png' }", @page.output(:html))
512
- result.must_equal "/assets/i/y-e75e0b52344f0e16c0564a3a7b0b8ace.png"
540
+ result.must_equal "/assets/i/y-#{y_sha}.png"
513
541
  end
514
542
  it "should allow for embedding asset urls into templates" do
515
543
  result = renderer.render_string("${ asset_url 'i/y.png' }", @page.output(:html))
516
- result.must_equal "url(/assets/i/y-e75e0b52344f0e16c0564a3a7b0b8ace.png)"
544
+ result.must_equal "url(/assets/i/y-#{y_sha}.png)"
517
545
  end
518
546
  end
519
547
  end
@@ -9,10 +9,11 @@ describe "Change" do
9
9
  @now = Time.now
10
10
  @site = setup_site
11
11
 
12
- stub_time(@now)
12
+ Timecop.freeze(@now)
13
13
  @revision = 1
14
14
 
15
15
  Content.delete
16
+ Spontaneous::State.delete
16
17
 
17
18
  class ::Page
18
19
  field :title, :string, :default => "New Page"
@@ -25,6 +26,7 @@ describe "Change" do
25
26
  end
26
27
 
27
28
  after do
29
+ Timecop.return
28
30
  Content.delete_revision(@revision) rescue nil
29
31
  Content.delete_revision(@revision+1) rescue nil
30
32
  Content.delete
@@ -55,8 +57,9 @@ describe "Change" do
55
57
 
56
58
  it "list all newly created pages" do
57
59
  root = Page.create(:title => "root")
58
- root[:first_published_at] = root[:last_published_at] = root.modified_at + 1000
59
60
  root.save
61
+ Content.publish(@revision)
62
+
60
63
 
61
64
  5.times { |i| root.things << Page.create(:title => "Page #{i+1}") }
62
65
 
@@ -75,9 +78,6 @@ describe "Change" do
75
78
  it "not list new pieces as available for publish" do
76
79
  root = Page.create(:title => "root")
77
80
  Content.publish(@revision)
78
- # force root to appear in the modified lists -- need this because otherwise the changes happen
79
- # more quickly than the resolution of the timestamps can register
80
- root[:first_published_at] = root[:last_published_at] = root.modified_at - 1000
81
81
  root.things << Piece.new
82
82
  root.save.reload
83
83
  result = outstanding_changes[:changes]
@@ -219,11 +219,14 @@ describe "Change" do
219
219
  root.reload
220
220
  page1 = Page.new(:title => "Page 1")
221
221
  root.things << page1
222
+ root.save.reload
223
+ page1.save.reload
224
+ last = Time.now + 100
225
+ Timecop.travel(last)
222
226
  new_child1 = Page.new(:title => "New Child 1")
223
227
  page1.things << new_child1
224
- root.save
225
- last = Time.now + 100
226
- ::Content.filter(:id => new_child1.id).update(:modified_at => last)
228
+ page1.save
229
+ new_child1.save
227
230
  result = outstanding_changes[:changes]
228
231
  assert result.first.modified_at > result.last.modified_at, "Change list in incorrect order"
229
232
  end
@@ -238,18 +241,21 @@ describe "Change" do
238
241
  new_child1 = Page.new(:title => "New Child 1")
239
242
  page1.things << new_child1
240
243
  root.save
244
+ new_child1.save
241
245
 
242
246
  Content.publish(@revision)
247
+ page1.reload
243
248
 
244
249
  later = @now + 10
245
- stub_time(later)
250
+ Timecop.travel(later)
246
251
  old_slug = page1.slug
247
252
  page1.slug = "changed"
248
- page1.save
253
+ page1.save.reload
249
254
 
250
255
  result = outstanding_changes[:changes]
251
256
 
252
257
  change = result.detect { |change| change.page.id == page1.id }
258
+ refute change.nil?
253
259
  change.export[:side_effects].must_equal({
254
260
  :slug => [{ :count => 1, :created_at => later.httpdate, :old_value => old_slug, :new_value => "changed"}]
255
261
  })
@@ -267,14 +273,16 @@ describe "Change" do
267
273
  root.save
268
274
 
269
275
  Content.publish(@revision)
276
+ page1.reload
270
277
 
271
278
  later = @now + 10
272
- stub_time(later)
279
+ Timecop.travel(later)
273
280
  page1.hide!
274
281
 
275
- page1.reload
282
+
276
283
  result = outstanding_changes[:changes]
277
284
  change = result.detect { |change| change.page.id == page1.id }
285
+ refute change.nil?
278
286
  change.export[:side_effects].must_equal({
279
287
  :visibility => [{ :count => 1, :created_at => later.httpdate, :old_value => false, :new_value => true}]
280
288
  })