spontaneous 0.2.0.beta4 → 0.2.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/Gemfile +11 -6
  4. data/Readme.markdown +136 -69
  5. data/application/css/core.css.scss +27 -7
  6. data/application/css/editing.css.scss +4 -26
  7. data/application/css/schema_error.css.scss +22 -0
  8. data/application/js/content.js +11 -3
  9. data/application/js/edit_panel.js +1 -4
  10. data/application/js/field/file.js +17 -0
  11. data/application/js/field/image.js +30 -21
  12. data/application/js/field/string.js +4 -1
  13. data/application/js/field_preview.js +21 -16
  14. data/application/js/publish.js +6 -6
  15. data/application/js/types.js +5 -13
  16. data/application/js/views.js +2 -2
  17. data/application/js/views/box_view.js +3 -2
  18. data/application/js/views/page_piece_view.js +1 -1
  19. data/application/js/views/piece_view.js +1 -1
  20. data/application/views/schema_modification_error.html.erb +13 -3
  21. data/db/migrations/20131104101935_site_must_publish_all.rb +14 -0
  22. data/lib/spontaneous.rb +0 -1
  23. data/lib/spontaneous/box_style.rb +15 -9
  24. data/lib/spontaneous/capistrano/deploy.rb +13 -1
  25. data/lib/spontaneous/change.rb +11 -13
  26. data/lib/spontaneous/cli.rb +5 -2
  27. data/lib/spontaneous/cli/assets.rb +7 -1
  28. data/lib/spontaneous/cli/console.rb +7 -1
  29. data/lib/spontaneous/cli/content.rb +35 -0
  30. data/lib/spontaneous/cli/fields.rb +3 -2
  31. data/lib/spontaneous/cli/generate.rb +5 -2
  32. data/lib/spontaneous/cli/server.rb +12 -8
  33. data/lib/spontaneous/cli/site.rb +12 -12
  34. data/lib/spontaneous/cli/user.rb +28 -14
  35. data/lib/spontaneous/collections/box_set.rb +4 -4
  36. data/lib/spontaneous/collections/field_set.rb +4 -4
  37. data/lib/spontaneous/collections/prototype_set.rb +12 -4
  38. data/lib/spontaneous/data_mapper.rb +11 -7
  39. data/lib/spontaneous/data_mapper/content_model.rb +8 -0
  40. data/lib/spontaneous/data_mapper/content_model/associations.rb +1 -1
  41. data/lib/spontaneous/data_mapper/dataset.rb +14 -2
  42. data/lib/spontaneous/data_mapper/scope.rb +33 -13
  43. data/lib/spontaneous/facet.rb +4 -0
  44. data/lib/spontaneous/field.rb +12 -12
  45. data/lib/spontaneous/field/base.rb +27 -22
  46. data/lib/spontaneous/field/boolean.rb +4 -4
  47. data/lib/spontaneous/field/date.rb +2 -2
  48. data/lib/spontaneous/field/file.rb +24 -18
  49. data/lib/spontaneous/field/html.rb +1 -1
  50. data/lib/spontaneous/field/image.rb +6 -19
  51. data/lib/spontaneous/field/location.rb +1 -1
  52. data/lib/spontaneous/field/long_string.rb +3 -3
  53. data/lib/spontaneous/field/markdown.rb +3 -3
  54. data/lib/spontaneous/field/select.rb +2 -2
  55. data/lib/spontaneous/field/string.rb +2 -2
  56. data/lib/spontaneous/field/tags.rb +2 -2
  57. data/lib/spontaneous/field/update.rb +21 -20
  58. data/lib/spontaneous/field/webvideo.rb +6 -6
  59. data/lib/spontaneous/field/webvideo/fallback.rb +2 -2
  60. data/lib/spontaneous/field/webvideo/vimeo.rb +7 -7
  61. data/lib/spontaneous/generators/site.rb +2 -2
  62. data/lib/spontaneous/generators/site/Gemfile.tt +5 -1
  63. data/lib/spontaneous/layout.rb +2 -2
  64. data/lib/spontaneous/media.rb +1 -0
  65. data/lib/spontaneous/media/file.rb +6 -5
  66. data/lib/spontaneous/media/image/attributes.rb +4 -0
  67. data/lib/spontaneous/media/image/renderable.rb +4 -4
  68. data/lib/spontaneous/media/store.rb +22 -0
  69. data/lib/spontaneous/{storage → media/store}/backend.rb +1 -1
  70. data/lib/spontaneous/{storage → media/store}/cloud.rb +1 -1
  71. data/lib/spontaneous/{storage → media/store}/local.rb +1 -1
  72. data/lib/spontaneous/media/temp_file.rb +1 -1
  73. data/lib/spontaneous/model.rb +10 -7
  74. data/lib/spontaneous/model/action.rb +7 -0
  75. data/lib/spontaneous/model/action/clean.rb +87 -0
  76. data/lib/spontaneous/model/box/allowed_types.rb +15 -1
  77. data/lib/spontaneous/model/core.rb +10 -0
  78. data/lib/spontaneous/model/core/aliases.rb +1 -1
  79. data/lib/spontaneous/model/core/content_groups.rb +1 -1
  80. data/lib/spontaneous/model/core/fields.rb +1 -1
  81. data/lib/spontaneous/model/core/modifications.rb +2 -2
  82. data/lib/spontaneous/model/core/page_search.rb +4 -0
  83. data/lib/spontaneous/model/core/publishing.rb +4 -17
  84. data/lib/spontaneous/model/core/render.rb +4 -4
  85. data/lib/spontaneous/model/core/styles.rb +2 -2
  86. data/lib/spontaneous/model/core/visibility.rb +6 -2
  87. data/lib/spontaneous/model/page.rb +6 -2
  88. data/lib/spontaneous/model/page/controllers.rb +55 -17
  89. data/lib/spontaneous/model/page/formats.rb +12 -7
  90. data/lib/spontaneous/model/page/layouts.rb +2 -2
  91. data/lib/spontaneous/model/page/locks.rb +4 -1
  92. data/lib/spontaneous/model/page/page_tree.rb +40 -6
  93. data/lib/spontaneous/output.rb +14 -52
  94. data/lib/spontaneous/output/context.rb +11 -39
  95. data/lib/spontaneous/output/context/navigation.rb +31 -0
  96. data/lib/spontaneous/output/format.rb +15 -19
  97. data/lib/spontaneous/output/renderable.rb +99 -0
  98. data/lib/spontaneous/output/store.rb +24 -0
  99. data/lib/spontaneous/output/store/backend.rb +52 -0
  100. data/lib/spontaneous/output/store/file.rb +77 -0
  101. data/lib/spontaneous/output/store/moneta.rb +117 -0
  102. data/lib/spontaneous/output/store/revision.rb +34 -0
  103. data/lib/spontaneous/output/store/store.rb +15 -0
  104. data/lib/spontaneous/output/store/transaction.rb +44 -0
  105. data/lib/spontaneous/output/template/engine.rb +17 -7
  106. data/lib/spontaneous/output/template/renderer.rb +66 -40
  107. data/lib/spontaneous/page_lock.rb +5 -7
  108. data/lib/spontaneous/page_piece.rb +2 -2
  109. data/lib/spontaneous/permissions/user.rb +14 -7
  110. data/lib/spontaneous/plugins/application/features.rb +8 -4
  111. data/lib/spontaneous/plugins/application/state.rb +12 -6
  112. data/lib/spontaneous/prototypes/box_prototype.rb +9 -10
  113. data/lib/spontaneous/prototypes/field_prototype.rb +66 -15
  114. data/lib/spontaneous/publishing/immediate.rb +30 -26
  115. data/lib/spontaneous/rack.rb +12 -7
  116. data/lib/spontaneous/rack/back.rb +43 -37
  117. data/lib/spontaneous/rack/back/base.rb +4 -4
  118. data/lib/spontaneous/rack/back/changes.rb +2 -2
  119. data/lib/spontaneous/rack/back/file.rb +16 -24
  120. data/lib/spontaneous/rack/back/map.rb +5 -5
  121. data/lib/spontaneous/rack/back/preview.rb +3 -4
  122. data/lib/spontaneous/rack/back/schema.rb +1 -1
  123. data/lib/spontaneous/rack/back/site.rb +6 -7
  124. data/lib/spontaneous/rack/front.rb +19 -16
  125. data/lib/spontaneous/rack/middleware/authenticate.rb +3 -3
  126. data/lib/spontaneous/rack/middleware/reloader.rb +3 -2
  127. data/lib/spontaneous/rack/middleware/scope.rb +25 -19
  128. data/lib/spontaneous/rack/page_controller.rb +164 -13
  129. data/lib/spontaneous/rack/public.rb +23 -62
  130. data/lib/spontaneous/rack/static.rb +2 -3
  131. data/lib/spontaneous/schema.rb +27 -8
  132. data/lib/spontaneous/schema/schema_modification.rb +9 -1
  133. data/lib/spontaneous/schema/uid.rb +2 -2
  134. data/lib/spontaneous/schema/uid_map.rb +3 -2
  135. data/lib/spontaneous/search/database.rb +2 -2
  136. data/lib/spontaneous/search/field.rb +5 -3
  137. data/lib/spontaneous/search/index.rb +12 -7
  138. data/lib/spontaneous/search/results.rb +5 -3
  139. data/lib/spontaneous/server.rb +2 -2
  140. data/lib/spontaneous/site.rb +10 -3
  141. data/lib/spontaneous/site/features.rb +26 -6
  142. data/lib/spontaneous/site/helpers.rb +9 -12
  143. data/lib/spontaneous/site/level.rb +7 -9
  144. data/lib/spontaneous/site/map.rb +9 -11
  145. data/lib/spontaneous/site/paths.rb +5 -5
  146. data/lib/spontaneous/site/publishing.rb +83 -80
  147. data/lib/spontaneous/site/schema.rb +1 -7
  148. data/lib/spontaneous/site/search.rb +8 -18
  149. data/lib/spontaneous/site/selectors.rb +60 -54
  150. data/lib/spontaneous/site/state.rb +36 -30
  151. data/lib/spontaneous/site/storage.rb +10 -16
  152. data/lib/spontaneous/state.rb +8 -0
  153. data/lib/spontaneous/style.rb +32 -33
  154. data/lib/spontaneous/version.rb +1 -1
  155. data/spontaneous.gemspec +22 -21
  156. data/test/fixtures/public/templates/layouts/default.html.cut +1 -1
  157. data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -1
  158. data/test/fixtures/public/templates/layouts/default.rss.cut +1 -1
  159. data/test/fixtures/search/config/indexes.rb +1 -1
  160. data/test/fixtures/serialisation/class_hash.yaml.erb +13 -1
  161. data/test/fixtures/serialisation/root_hash.yaml.erb +10 -0
  162. data/test/functional/test_application.rb +20 -24
  163. data/test/functional/test_back.rb +26 -27
  164. data/test/functional/test_cli.rb +146 -0
  165. data/test/functional/test_front.rb +287 -216
  166. data/test/functional/test_user_manager.rb +1 -1
  167. data/test/test_helper.rb +15 -11
  168. data/test/unit/test_alias.rb +32 -25
  169. data/test/unit/test_asset_bundler.rb +1 -1
  170. data/test/unit/test_assets.rb +34 -33
  171. data/test/unit/test_authentication.rb +1 -1
  172. data/test/unit/test_boxes.rb +16 -2
  173. data/test/unit/test_changesets.rb +23 -11
  174. data/test/unit/test_content.rb +15 -0
  175. data/test/unit/test_context.rb +139 -0
  176. data/test/unit/test_controllers.rb +374 -0
  177. data/test/{experimental → unit}/test_crypt.rb +0 -0
  178. data/test/unit/test_datamapper.rb +260 -237
  179. data/test/unit/test_datamapper_content.rb +42 -12
  180. data/test/{experimental → unit}/test_features.rb +85 -3
  181. data/test/unit/test_fields.rb +117 -42
  182. data/test/unit/test_formats.rb +11 -1
  183. data/test/unit/test_generators.rb +2 -2
  184. data/test/unit/test_helpers.rb +7 -8
  185. data/test/unit/test_images.rb +39 -2
  186. data/test/unit/test_layouts.rb +14 -12
  187. data/test/unit/test_media.rb +32 -23
  188. data/test/unit/test_output_store.rb +342 -0
  189. data/test/unit/test_page.rb +8 -1
  190. data/test/unit/test_permissions.rb +11 -7
  191. data/test/unit/test_plugins.rb +3 -3
  192. data/test/unit/test_prototype_set.rb +8 -1
  193. data/test/unit/test_publishing.rb +67 -54
  194. data/test/unit/test_render.rb +91 -38
  195. data/test/unit/test_revisions.rb +4 -4
  196. data/test/unit/test_schema.rb +109 -84
  197. data/test/unit/test_search.rb +42 -42
  198. data/test/unit/test_serialisation.rb +3 -2
  199. data/test/unit/test_site.rb +39 -27
  200. data/test/unit/test_storage.rb +9 -6
  201. data/test/unit/test_styles.rb +25 -32
  202. data/test/unit/test_templates.rb +8 -4
  203. metadata +89 -54
  204. data/lib/spontaneous/model/page/request.rb +0 -105
  205. data/lib/spontaneous/storage.rb +0 -22
@@ -191,7 +191,7 @@ describe "Formats" do
191
191
  module CustomHelper1
192
192
  def here_is_my_custom_helper1; end
193
193
  end
194
- Site.register_helper CustomHelper1, :newhtml
194
+ @site.register_helper CustomHelper1, :newhtml
195
195
 
196
196
  class NewHTMLFormat < S::Output::HTML
197
197
  provides_format :newhtml
@@ -242,6 +242,16 @@ describe "Formats" do
242
242
  refute @page.provides_output?(:xyz)
243
243
  end
244
244
 
245
+ it "recognises its own output" do
246
+ assert @page.provides_output?(@page.output(:html))
247
+ end
248
+
249
+ it "doesn't recognise another page's output" do
250
+ other = FPage.new(slug: "other")
251
+ refute @page == other
252
+ refute @page.provides_output?(other.output(:html))
253
+ end
254
+
245
255
  it "provide a symbol version of the output name" do
246
256
  @page.output(:html).to_sym.must_equal :html
247
257
  end
@@ -122,7 +122,7 @@ describe "Generators" do
122
122
  it "configure the correct gem" do
123
123
  generate(:site, "example.com", "--root=#{@tmp}", "--database=postgresql")
124
124
  gemfile = File.read(File.join(@site_root, "Gemfile"))
125
- gemfile.must_match /^gem 'pg'/
125
+ gemfile.must_match /^gem 'sequel_pg'.+require: 'sequel'/
126
126
  end
127
127
 
128
128
  it "setup the right db parameters" do
@@ -188,7 +188,7 @@ describe "Generators" do
188
188
  site_root = File.join(@tmp, 'example_com')
189
189
  generate(:site, "example.com", "--root=#{@tmp}", "--database=postgres", "--user=spontaneous", "--password=s3cret")
190
190
  gemfile = File.read(File.join(site_root, "Gemfile"))
191
- gemfile.must_match /^gem 'pg'/
191
+ gemfile.must_match /^gem 'sequel_pg'.+require: 'sequel'/
192
192
  config = database_config("example_com")
193
193
  [:development, :test].each do |environment|
194
194
  config[environment][:user].must_equal "spontaneous"
@@ -8,7 +8,6 @@ describe "ContextHelpers" do
8
8
  @site = setup_site
9
9
  @site.paths.add :templates, File.expand_path("../../fixtures/helpers/templates", __FILE__)
10
10
  @renderer = S::Output::Template::Renderer.new(false)
11
- S::Output.renderer = @renderer
12
11
  end
13
12
 
14
13
  after do
@@ -16,7 +15,7 @@ describe "ContextHelpers" do
16
15
  end
17
16
 
18
17
  it "be assignable to a particular format" do
19
- CustomHelper1 = Site.helper :html do
18
+ CustomHelper1 = @site.helper :html do
20
19
  def here_is_my_custom_helper1; end
21
20
  end
22
21
 
@@ -27,24 +26,24 @@ describe "ContextHelpers" do
27
26
  Spontaneous::Output::Helpers::ScriptHelper,
28
27
  Spontaneous::Output::Helpers::StylesheetHelper
29
28
  ]
30
- helper_module = Site.context :html
29
+ helper_module = @site.context :html
31
30
  join = included_helpers & helper_module.ancestors
32
31
  Set.new(join).must_equal Set.new(included_helpers)
33
32
  assert helper_module.respond_to?(:here_is_my_custom_helper1)
34
33
  end
35
34
 
36
35
  it "be assigned to all formats if none given" do
37
- CustomHelper2 = Site.helper do
36
+ CustomHelper2 = @site.helper do
38
37
  extend self
39
38
  def here_is_my_custom_helper2; end
40
39
  end
41
40
 
42
41
  assert CustomHelper2.respond_to?(:here_is_my_custom_helper2)
43
42
 
44
- helper_module = Site.context :html
43
+ helper_module = @site.context :html
45
44
  assert helper_module.ancestors.include?(CustomHelper2)
46
45
 
47
- helper_module = Site.context :pdf
46
+ helper_module = @site.context :pdf
48
47
  assert helper_module.ancestors.include?(CustomHelper2)
49
48
  end
50
49
 
@@ -53,14 +52,14 @@ describe "ContextHelpers" do
53
52
  add_output :mobile
54
53
  end
55
54
 
56
- Site.helper :html do
55
+ @site.helper :html do
57
56
  def here_is_my_custom_helper3
58
57
  "here_is_my_custom_helper3"
59
58
  end
60
59
  extend self
61
60
  end
62
61
 
63
- Site.helper :mobile do
62
+ @site.helper :mobile do
64
63
  def here_is_my_custom_helper4
65
64
  "here_is_my_custom_helper4"
66
65
  end
@@ -49,7 +49,7 @@ describe "Images" do
49
49
  @upload_dir.mkpath
50
50
 
51
51
  @revision = 10
52
- S::Site.stubs(:working_revision).returns(@revision)
52
+ @site.stubs(:working_revision).returns(@revision)
53
53
 
54
54
  @src_image = Pathname.new(File.join(File.dirname(__FILE__), "../fixtures/images/rose.jpg")).realpath
55
55
  @origin_image = @upload_dir + "rose.jpg"
@@ -265,7 +265,7 @@ describe "Images" do
265
265
  :public_host => "http://media.example.com"
266
266
  }
267
267
  ::Fog.mock!
268
- @storage = Spontaneous::Storage::Cloud.new(@aws_credentials, 'media.example.com')
268
+ @storage = Spontaneous::Media::Store::Cloud.new(@aws_credentials, 'media.example.com')
269
269
  @storage.backend.directories.create(:key => @bucket_name)
270
270
  @site.stubs(:storage).with(anything).returns(@storage)
271
271
  @image.value = @origin_image.to_s
@@ -281,4 +281,41 @@ describe "Images" do
281
281
  end
282
282
  end
283
283
  end
284
+
285
+ describe "clearing" do
286
+ def assert_image_field_empty
287
+ @field.value.must_equal ''
288
+ @field.src.must_equal ''
289
+ @field.filesize.must_equal 0
290
+ @field.smaller.value.must_equal ''
291
+ end
292
+
293
+ before do
294
+ class ::ContentWithImage < ::Content::Piece
295
+ field :photo, :image do
296
+ size :smaller do
297
+ fit width: 100, height: 100
298
+ end
299
+ end
300
+ end
301
+
302
+ @instance = ContentWithImage.new
303
+
304
+ @content_id = 234
305
+ @instance.stubs(:id).returns(@content_id)
306
+ @field = @instance.photo
307
+ path = File.expand_path("../../fixtures/images/rose.jpg", __FILE__)
308
+ @field.value = path
309
+ end
310
+
311
+ after do
312
+ Object.send(:remove_const, :ContentWithImage) rescue nil
313
+ end
314
+
315
+ it "clears the value if set to the empty string" do
316
+ @field.value = ''
317
+ assert_image_field_empty
318
+ end
319
+ end
320
+
284
321
  end
@@ -24,9 +24,11 @@ describe "Layouts" do
24
24
  teardown_site
25
25
  end
26
26
 
27
+ let(:renderer) { Spontaneous::Output.default_renderer(@site) }
28
+
27
29
  it "default to layouts/standard.html... if nothing defined" do
28
30
  page = LayoutPage.new
29
- assert_correct_template(page, @template_root / 'layouts/standard')
31
+ assert_correct_template(page, @template_root / 'layouts/standard', renderer)
30
32
  page.render.must_equal "layouts/standard.html.cut\n"
31
33
  end
32
34
 
@@ -37,7 +39,7 @@ describe "Layouts" do
37
39
  file.write("layouts/layout_page.html.cut\n")
38
40
  end
39
41
  page = LayoutPage.new
40
- assert_correct_template(page, @template_root / 'layouts/layout_page')
42
+ assert_correct_template(page, @template_root / 'layouts/layout_page', renderer)
41
43
  page.render.must_equal "layouts/layout_page.html.cut\n"
42
44
  ensure
43
45
  FileUtils.rm(layout)
@@ -48,7 +50,7 @@ describe "Layouts" do
48
50
  LayoutPage.layout :custom1
49
51
  LayoutPage.layout :custom2
50
52
  page = LayoutPage.new
51
- assert_correct_template(page, @template_root / 'layouts/custom1')
53
+ assert_correct_template(page, @template_root / 'layouts/custom1', renderer)
52
54
  page.render.must_equal "layouts/custom1.html.cut\n"
53
55
  end
54
56
 
@@ -56,7 +58,7 @@ describe "Layouts" do
56
58
  LayoutPage.layout :custom1
57
59
  LayoutPage.layout :custom2, :default => true
58
60
  page = LayoutPage.new
59
- assert_correct_template(page, @template_root / 'layouts/custom2')
61
+ assert_correct_template(page, @template_root / 'layouts/custom2', renderer)
60
62
  page.render.must_equal "layouts/custom2.html.cut\n"
61
63
  end
62
64
 
@@ -64,7 +66,7 @@ describe "Layouts" do
64
66
  LayoutPage.layout :custom1
65
67
  LayoutPage.layout :custom2, :default => true
66
68
  page = SubPage.new
67
- assert_correct_template(page, @template_root / 'layouts/custom2')
69
+ assert_correct_template(page, @template_root / 'layouts/custom2', renderer)
68
70
  end
69
71
 
70
72
  it "be able to overwrite inherited templates from superclass" do
@@ -73,17 +75,17 @@ describe "Layouts" do
73
75
  SubPage.layout :custom3
74
76
  page = SubPage.new
75
77
  # page.layout.template.must_equal 'layouts/custom2'
76
- assert_correct_template(page, @template_root / 'layouts/custom2')
78
+ assert_correct_template(page, @template_root / 'layouts/custom2', renderer)
77
79
  end
78
80
 
79
81
  it "allow setting of style used" do
80
82
  LayoutPage.layout :custom1
81
83
  LayoutPage.layout :custom2
82
84
  page = LayoutPage.new
83
- assert_correct_template(page, @template_root / 'layouts/custom1')
85
+ assert_correct_template(page, @template_root / 'layouts/custom1', renderer)
84
86
  # page.layout.template.must_equal 'layouts/custom1'
85
87
  page.layout = :custom2
86
- assert_correct_template(page, @template_root / 'layouts/custom2')
88
+ assert_correct_template(page, @template_root / 'layouts/custom2', renderer)
87
89
  # page.layout.template.must_equal 'layouts/custom2'
88
90
  end
89
91
 
@@ -93,9 +95,9 @@ describe "Layouts" do
93
95
  SubPage.layout :custom3
94
96
  SubPage.layout :custom4
95
97
  page = SubPage.new
96
- assert_correct_template(page, @template_root / 'layouts/custom3')
98
+ assert_correct_template(page, @template_root / 'layouts/custom3', renderer)
97
99
  page.layout = :custom2
98
- assert_correct_template(page, @template_root / 'layouts/custom2')
100
+ assert_correct_template(page, @template_root / 'layouts/custom2', renderer)
99
101
  end
100
102
 
101
103
  it "allow defining of default layout in sub-classes" do
@@ -104,9 +106,9 @@ describe "Layouts" do
104
106
  SubPage.layout :custom3
105
107
  SubPage.layout :custom4, :default => true
106
108
  page = SubPage.new
107
- assert_correct_template(page, @template_root / 'layouts/custom4')
109
+ assert_correct_template(page, @template_root / 'layouts/custom4', renderer)
108
110
  page.layout = :custom3
109
- assert_correct_template(page, @template_root / 'layouts/custom3')
111
+ assert_correct_template(page, @template_root / 'layouts/custom3', renderer)
110
112
  end
111
113
 
112
114
  it "support blocks to set simple templates" do
@@ -13,6 +13,14 @@ describe "Media" do
13
13
  teardown_site
14
14
  end
15
15
 
16
+ def file(*args)
17
+ @site.file(*args)
18
+ end
19
+
20
+ def tempfile(*args)
21
+ @site.tempfile(*args)
22
+ end
23
+
16
24
  describe "Utility methods" do
17
25
  it "be able to sanitise filenames" do
18
26
  filename = "Something with-dodgy 'characters'.many.jpg"
@@ -22,11 +30,11 @@ describe "Media" do
22
30
 
23
31
  describe "All media files" do
24
32
  it "know their mimetype" do
25
- file = Spontaneous::Media::File.new(@content, "file name.txt")
33
+ file = file(@content, "file name.txt")
26
34
  file.mimetype.must_equal "text/plain"
27
- file = Spontaneous::Media::File.new(@content, "file name.jpg")
35
+ file = file(@content, "file name.jpg")
28
36
  file.mimetype.must_equal "image/jpeg"
29
- file = Spontaneous::Media::File.new(@content, "file name.jpg", "text/html")
37
+ file = file(@content, "file name.jpg", "text/html")
30
38
  file.mimetype.must_equal "text/html"
31
39
  end
32
40
 
@@ -42,20 +50,21 @@ describe "Media" do
42
50
  :aws_access_key_id=>"ACCESS_KEY_ID",
43
51
  :public_host => "http://media.example.com"
44
52
  }
45
- @storage = Spontaneous::Storage::Cloud.new(@aws_credentials, 'media.example.com')
53
+ @storage = Spontaneous::Media::Store::Cloud.new(@aws_credentials, 'media.example.com')
46
54
  @storage.backend.directories.create(:key => @bucket_name)
47
55
  @site.stubs(:storage).with(anything).returns(@storage)
48
56
  @content = ::Piece.create
49
57
  @content.stubs(:id).returns(99)
50
58
  Spontaneous::State.stubs(:revision).returns(853)
51
59
  end
60
+
52
61
  it "return an absolute path for the url" do
53
- file = Spontaneous::Media::File.new(@content, "file name.txt")
62
+ file = file(@content, "file name.txt")
54
63
  file.url.must_equal "http://media.example.com/00099/0853/file-name.txt"
55
64
  end
56
65
 
57
66
  it "create a new instance with a different name" do
58
- file1 = Spontaneous::Media::File.new(@content, "file name.txt")
67
+ file1 = file(@content, "file name.txt")
59
68
  file2 = file1.rename("another.jpg")
60
69
  file2.owner.must_equal file1.owner
61
70
  file2.mimetype.must_equal "image/jpeg"
@@ -70,7 +79,7 @@ describe "Media" do
70
79
  }
71
80
  existing_file = File.expand_path("../../fixtures/images/rose.jpg", __FILE__)
72
81
  assert ::File.exist?(existing_file)
73
- file = Spontaneous::Media::File.new(@content, "file name.txt")
82
+ file = file(@content, "file name.txt")
74
83
  file.copy(existing_file)
75
84
  end
76
85
 
@@ -82,7 +91,7 @@ describe "Media" do
82
91
  }
83
92
  existing_file = File.expand_path("../../fixtures/images/rose.jpg", __FILE__)
84
93
  assert ::File.exist?(existing_file)
85
- file = Spontaneous::Media::File.new(@content, "file name.txt")
94
+ file = file(@content, "file name.txt")
86
95
  File.open(existing_file, 'rb') do |f|
87
96
  file.copy(f)
88
97
  end
@@ -95,7 +104,7 @@ describe "Media" do
95
104
  args[:public] == true
96
105
  }
97
106
 
98
- file = Spontaneous::Media::File.new(@content, "file name.txt")
107
+ file = file(@content, "file name.txt")
99
108
  content_string = "Hello"
100
109
  file.open do |f|
101
110
  f.write(content_string)
@@ -106,7 +115,7 @@ describe "Media" do
106
115
  describe "Local media files" do
107
116
  before do
108
117
  @media_dir = Dir.mktmpdir
109
- @storage = Spontaneous::Storage::Local.new(@media_dir, '/media')
118
+ @storage = Spontaneous::Media::Store::Local.new(@media_dir, '/media')
110
119
  @site.stubs(:storage).with(anything).returns(@storage)
111
120
  @content = ::Piece.create
112
121
  @content.stubs(:id).returns(99)
@@ -114,17 +123,17 @@ describe "Media" do
114
123
  end
115
124
 
116
125
  it "return an absolute path for the url" do
117
- file = Spontaneous::Media::File.new(@content, "file name.txt")
126
+ file = file(@content, "file name.txt")
118
127
  file.url.must_equal "/media/00099/0853/file-name.txt"
119
128
  end
120
129
 
121
130
  it "place files into its configured root" do
122
- file = Spontaneous::Media::File.new(@content, "file name.txt")
131
+ file = file(@content, "file name.txt")
123
132
  file.path.must_equal File.join(@media_dir, "/00099/0853/file-name.txt")
124
133
  end
125
134
 
126
135
  it "create a new instance with a different name" do
127
- file1 = Spontaneous::Media::File.new(@content, "file name.txt")
136
+ file1 = file(@content, "file name.txt")
128
137
  file2 = file1.rename("another.jpg")
129
138
  file2.owner.must_equal file1.owner
130
139
  file2.mimetype.must_equal "image/jpeg"
@@ -136,7 +145,7 @@ describe "Media" do
136
145
  existing_file = File.expand_path("../../fixtures/images/rose.jpg", __FILE__)
137
146
  refute ::File.exist?(file_path)
138
147
  assert ::File.exist?(existing_file)
139
- file = Spontaneous::Media::File.new(@content, "file name.txt")
148
+ file = file(@content, "file name.txt")
140
149
  file.copy(existing_file)
141
150
  assert ::File.exist?(file_path)
142
151
  file.source.must_equal existing_file
@@ -147,7 +156,7 @@ describe "Media" do
147
156
  existing_file = File.expand_path("../../fixtures/images/rose.jpg", __FILE__)
148
157
  refute ::File.exist?(file_path)
149
158
  assert ::File.exist?(existing_file)
150
- file = Spontaneous::Media::File.new(@content, "file name.txt")
159
+ file = file(@content, "file name.txt")
151
160
  File.open(existing_file, 'rb') do |f|
152
161
  file.copy(f)
153
162
  end
@@ -158,7 +167,7 @@ describe "Media" do
158
167
  it "provide an open method that writes files to the correct location" do
159
168
  file_path = File.join(@media_dir, "/00099/0853/file-name.txt")
160
169
  refute ::File.exist?(file_path)
161
- file = Spontaneous::Media::File.new(@content, "file name.txt")
170
+ file = file(@content, "file name.txt")
162
171
  content_string = "Hello"
163
172
  file.open do |f|
164
173
  f.write(content_string)
@@ -179,11 +188,11 @@ describe "Media" do
179
188
  :aws_access_key_id=>"ACCESS_KEY_ID",
180
189
  :public_host => "http://media.example.com"
181
190
  }
182
- cloud = Spontaneous::Storage::Cloud.new(@aws_credentials, 'media.example.com')
191
+ cloud = Spontaneous::Media::Store::Cloud.new(@aws_credentials, 'media.example.com')
183
192
  cloud.backend.directories.create(:key => @bucket_name)
184
193
  @site.stubs(:storage).with(anything).returns(cloud)
185
194
  @media_dir = Dir.mktmpdir
186
- @storage = Spontaneous::Storage::Local.new(@media_dir, '/media')
195
+ @storage = Spontaneous::Media::Store::Local.new(@media_dir, '/media')
187
196
  @site.stubs(:local_storage).with(anything).returns(@storage)
188
197
  @site.stubs(:default_storage).with(anything).returns(@storage)
189
198
  @content = ::Piece.create
@@ -191,12 +200,12 @@ describe "Media" do
191
200
  end
192
201
 
193
202
  it "return an absolute path for the url" do
194
- file = Spontaneous::Media::TempFile.new(@content, "file name.txt")
203
+ file = tempfile(@content, "file name.txt")
195
204
  file.url.must_equal "/media/tmp/00099/file-name.txt"
196
205
  end
197
206
 
198
207
  it "place files into its configured root" do
199
- file = Spontaneous::Media::TempFile.new(@content, "file name.txt")
208
+ file = tempfile(@content, "file name.txt")
200
209
  file.path.must_equal File.join(@media_dir, "/tmp/00099/file-name.txt")
201
210
  end
202
211
 
@@ -205,7 +214,7 @@ describe "Media" do
205
214
  existing_file = File.expand_path("../../fixtures/images/rose.jpg", __FILE__)
206
215
  refute ::File.exist?(file_path)
207
216
  assert ::File.exist?(existing_file)
208
- file = Spontaneous::Media::TempFile.new(@content, "file name.txt")
217
+ file = tempfile(@content, "file name.txt")
209
218
  file.copy(existing_file)
210
219
  assert ::File.exist?(file_path)
211
220
  file.source.must_equal existing_file
@@ -216,7 +225,7 @@ describe "Media" do
216
225
  existing_file = File.expand_path("../../fixtures/images/rose.jpg", __FILE__)
217
226
  refute ::File.exist?(file_path)
218
227
  assert ::File.exist?(existing_file)
219
- file = Spontaneous::Media::TempFile.new(@content, "file name.txt")
228
+ file = tempfile(@content, "file name.txt")
220
229
  File.open(existing_file, 'rb') do |f|
221
230
  file.copy(f)
222
231
  end
@@ -227,7 +236,7 @@ describe "Media" do
227
236
  it "provide an open method that writes files to the correct location" do
228
237
  file_path = File.join(@media_dir, "/tmp/00099/file-name.txt")
229
238
  refute ::File.exist?(file_path)
230
- file = Spontaneous::Media::TempFile.new(@content, "file name.txt")
239
+ file = tempfile(@content, "file name.txt")
231
240
  content_string = "Hello"
232
241
  file.open do |f|
233
242
  f.write(content_string)
@@ -0,0 +1,342 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../test_helper', __FILE__)
4
+
5
+ # Store.new(backend_class, options) #=> Store
6
+ # Store#revision(revision_number) #=> Revision
7
+ # Store#revisions #=> [Fixnum]
8
+ # Revision#static_template(output) #=> (String or nil)
9
+ # Revision#dynamic_template(output, request_method = "GET") #=> (String or nil)
10
+ # Revision#transaction #=> Transaction
11
+ # Revision#delete
12
+ # Transaction#write(output, template)
13
+ # Transaction#commit
14
+ # Transaction#rollback
15
+ # Backend#store_static(revision, key, template)
16
+ # Backend#store_protected(revision, key, template)
17
+ # Backend#store_dynamic(revision, key, template)
18
+ # Backend#load_static(revision, key) #=> String
19
+ # Backend#load_protected(revision, key) #=> String
20
+ # Backend#load_dynamic(revision, key) #=> String
21
+ # Backend#delete_revision(revision)
22
+ # Backend#revisions #=> [Fixnum]
23
+
24
+ describe "Output store" do
25
+ start do
26
+ site = setup_site
27
+ let(:site) { site }
28
+
29
+ class CustomPage < Page
30
+ add_output :xml, dynamic: true, language: 'php'
31
+ add_output :json
32
+ box :stuff
33
+ end
34
+
35
+ home = CustomPage.create
36
+ page = CustomPage.new(slug: 'one')
37
+ home.stuff << page
38
+ page.save
39
+ home.save
40
+ let(:home) { home }
41
+ let(:page) { page }
42
+ end
43
+
44
+ finish do
45
+ Content.delete
46
+ Object.send(:remove_const, :CustomPage) rescue nil
47
+ teardown_site
48
+ end
49
+
50
+ describe "File" do
51
+ let(:root) { Dir.mktmpdir }
52
+ let(:store) { Spontaneous::Output::Store::File.new(root) }
53
+ let(:revision) { 100 }
54
+ let(:revision_path) { ::File.join(root, "00100") }
55
+
56
+
57
+ it "puts static files under 'static'" do
58
+ store.store_static(revision, "/one.html", "*template*")
59
+ ::File.read(::File.join(revision_path, 'static', 'one.html')).must_equal "*template*"
60
+ end
61
+
62
+ it "puts protected files under 'protected'" do
63
+ store.store_protected(revision, "/one.html", "*template*")
64
+ ::File.read(::File.join(revision_path, 'protected', 'one.html')).must_equal "*template*"
65
+ end
66
+
67
+ it "puts dynamic files under 'dynamic'" do
68
+ store.store_dynamic(revision, "/one.html", "*template*")
69
+ ::File.read(::File.join(revision_path, 'dynamic', 'one.html')).must_equal "*template*"
70
+ end
71
+
72
+ it "enables the retrieval of available revisions" do
73
+ store.store_static(1, "/one.html", "*template*")
74
+ store.store_protected(2, "/one.html", "*template*")
75
+ store.store_dynamic(3, "/one.html", "*template*")
76
+ store.revisions.must_equal [1, 2, 3]
77
+ end
78
+
79
+ it "enables the retrieval of templates" do
80
+ store.store_static(revision, "/one.html", "*template*")
81
+ store.load_static(revision, "/one.html").read.must_equal "*template*"
82
+ end
83
+
84
+ it "returns a File object for static templates" do
85
+ store.store_static(revision, "/one.html", "*template*")
86
+ result = store.load_static(revision, "/one.html")
87
+ result.must_be_instance_of ::File
88
+ result.to_path.must_equal ::File.join(revision_path, 'static', 'one.html')
89
+ result.read.must_equal "*template*"
90
+ end
91
+
92
+ it "returns a File object for dynamic templates" do
93
+ store.store_dynamic(revision, "/one.html", "*template*")
94
+ result = store.load_dynamic(revision, "/one.html")
95
+ result.must_be_instance_of ::File
96
+ result.to_path.must_equal ::File.join(revision_path, 'dynamic', 'one.html')
97
+ result.read.must_equal "*template*"
98
+ end
99
+
100
+ it "sets the returned template's encoding as UTF-8" do
101
+ store.store_static(revision, "/one.html", "«küßî»")
102
+ result = store.load_static(revision, "/one.html")
103
+ result.external_encoding.must_equal Encoding::UTF_8
104
+ result.read.must_equal "«küßî»"
105
+ end
106
+
107
+ it "returns nil when attempting to retrieve a non-existant template" do
108
+ store.load_static(revision, "/faile.html").must_equal nil
109
+ end
110
+
111
+ it "puts all written keys into a transaction if given" do
112
+ transaction = []
113
+ store.store_static(revision, "/one.html", "*template*", transaction)
114
+ store.store_protected(revision, "/one.html", "*template*", transaction)
115
+ store.store_dynamic(revision, "/one.html", "*template*", transaction)
116
+ transaction.length.must_equal 3
117
+ transaction.must_equal ["00100/static/one.html", "00100/protected/one.html", "00100/dynamic/one.html"]
118
+ end
119
+
120
+ it "enables registration of a revision" do
121
+ store.add_revision(100, ["100/one.html", "100/two.html", "100/three.html"])
122
+ store.add_revision(101, ["101/one.html", "101/two.html", "101/three.html"])
123
+ store.revisions.must_equal [100, 101]
124
+ end
125
+
126
+ it "allows for the deletion of a revision" do
127
+ store.store_static(100, "/one.html", "*template*")
128
+ store.store_static(100, "/another/two.html", "*template*")
129
+ ::File.exist?(::File.join(revision_path, 'static', 'one.html')).must_equal true
130
+ ::File.exist?(::File.join(revision_path, 'static', 'another/two.html')).must_equal true
131
+ store.delete_revision(100)
132
+ ::File.exist?(revision_path).must_equal false
133
+ end
134
+ end
135
+
136
+ describe "Moneta" do
137
+ let(:store) { Spontaneous::Output::Store::Moneta.new(:Memory) }
138
+ let(:r) { 100 }
139
+ let(:revision) { Spontaneous::Output::Store::Revision.new(r, store) }
140
+ let(:transaction) { revision.transaction }
141
+
142
+
143
+ it "allows the storage & retrieval of static templates" do
144
+ store.store_static(r, "/one.html", "*template*")
145
+ result = store.load_static(r, "/one.html")
146
+ result.respond_to?(:read).must_equal true
147
+ result.read.must_equal "*template*"
148
+ result.respond_to?(:path).must_equal true
149
+ result.respond_to?(:to_path).must_equal false
150
+ result.path.must_equal "/#{r}/static/one.html"
151
+ end
152
+
153
+ it "allows the storage & retrieval of protected templates" do
154
+ store.store_protected(r, "/one.html", "*template*")
155
+ result = store.load_protected(r, "/one.html")
156
+ result.respond_to?(:read).must_equal true
157
+ result.read.must_equal "*template*"
158
+ result.respond_to?(:path).must_equal true
159
+ result.respond_to?(:to_path).must_equal false
160
+ result.path.must_equal "/#{r}/protected/one.html"
161
+ end
162
+
163
+ it "allows the storage & retrieval of dynamic templates" do
164
+ store.store_dynamic(r, "/one.html", "*template*")
165
+ result = store.load_dynamic(r, "/one.html")
166
+ result.respond_to?(:read).must_equal true
167
+ result.read.must_equal "*template*"
168
+ result.respond_to?(:path).must_equal true
169
+ result.respond_to?(:to_path).must_equal false
170
+ result.path.must_equal "/#{r}/dynamic/one.html"
171
+ end
172
+
173
+ it "puts all written keys into a transaction if given" do
174
+ transaction = []
175
+ store.store_static(r, "/one.html", "*template*", transaction)
176
+ store.store_protected(r, "/two.html", "*template*", transaction)
177
+ store.store_dynamic(r, "/three.html", "*template*", transaction)
178
+ transaction.length.must_equal 3
179
+ transaction.must_equal ["100:static:/one.html", "100:protected:/two.html", "100:dynamic:/three.html"]
180
+ end
181
+
182
+ it "enables registration of a revision" do
183
+ store.add_revision(100, ["100:static:/one.html", "100:static:/two.html", "100:static:/three.html"])
184
+ store.add_revision(101, ["101:static:/one.html", "101:static:/two.html", "101:static:/three.html"])
185
+ store.revisions.must_equal [100, 101]
186
+ end
187
+
188
+ it "enables removal of a revision" do
189
+ keys = []
190
+ store.store_static(r, "/one.html", "*template*", keys)
191
+ store.store_protected(r, "/two.html", "*template*", keys)
192
+ store.store_dynamic(r, "/three.html", "*template*", keys)
193
+ store.add_revision(100, keys)
194
+ keys.each do |key|
195
+ store.backend.key?(key).must_equal true
196
+ end
197
+ store.add_revision(101, ["101:static:/one.html", "101:protected:/two.html", "101:dynamic:/three.html"])
198
+ store.revisions.must_equal [100, 101]
199
+ store.delete_revision(100)
200
+ store.revisions.must_equal [101]
201
+ hash = store.backend.backend
202
+ keys.each do |key|
203
+ hash.key?(key).must_equal false
204
+ end
205
+ hash.key?(store.revision_key(100)).must_equal false
206
+ end
207
+
208
+
209
+ end
210
+
211
+ describe "Transaction" do
212
+ let(:store) { Spontaneous::Output::Store::Moneta.new(:Memory) }
213
+ let(:r) { 100 }
214
+ let(:transaction) { Spontaneous::Output::Store::Transaction.new(r, store) }
215
+ let(:output_html) { page.output(:html) }
216
+ let(:output_xml) { page.output(:xml) }
217
+ let(:output_json) { page.output(:json) }
218
+
219
+ it "tracks all keys written to it" do
220
+ transaction.store(output_html, true, "*template*")
221
+ transaction.store(output_xml, false, "*template*")
222
+ transaction.store(output_json, false, "*template*")
223
+ transaction.commit
224
+ store.revisions.must_equal [ 100 ]
225
+ end
226
+
227
+ it "clears up the store after a rollback" do
228
+ keys = ["100:static:/one.html"]
229
+ transaction.store(output_html, false, "*template*")
230
+ keys.each do |key|
231
+ store.backend.key?(key).must_equal true
232
+ end
233
+ transaction.rollback
234
+ store.revisions.must_equal []
235
+ keys.each do |key|
236
+ store.backend.key?(key).must_equal false
237
+ end
238
+ end
239
+
240
+ it "ignores a rollback after a commit" do
241
+ transaction.store(output_html, true, "*template*")
242
+ transaction.store(output_xml, false, "*template*")
243
+ transaction.store(output_json, false, "*template*")
244
+ transaction.commit
245
+ transaction.rollback
246
+ store.revisions.must_equal [ 100 ]
247
+ end
248
+
249
+ it "writes to index.xxx when given the site root" do
250
+ store.expects(:store_static).with(r, "/index.html", "*template*", transaction)
251
+ transaction.store(home.output(:html), false, "*template*")
252
+ store.expects(:store_dynamic).with(r, "/index.xml.php", "*template*", transaction)
253
+ transaction.store(home.output(:xml), false, "*template*")
254
+ end
255
+
256
+ describe "template partitioning" do
257
+ it "writes dynamic templates to dynamic partition" do
258
+ store.expects(:store_dynamic).with(r, "/one.html.cut", "*template*", transaction)
259
+ transaction.store(output_html, true, "*template*")
260
+ end
261
+
262
+ it "writes dynamic outputs to dynamic partition" do
263
+ store.expects(:store_dynamic).with(r, "/one.xml.php", "*template*", transaction)
264
+ transaction.store(output_xml, false, "*template*")
265
+ end
266
+
267
+ it "writes protected templates to protected partition" do
268
+ page.expects(:dynamic?).returns(true)
269
+ store.expects(:store_protected).with(r, "/one.html", "*template*", transaction)
270
+ transaction.store(output_html, false, "*template*")
271
+ end
272
+
273
+ it "writes static templates to the static partition" do
274
+ store.expects(:store_static).with(r, "/one.html", "*template*", transaction)
275
+ transaction.store(output_html, false, "*template*")
276
+ end
277
+ end
278
+ end
279
+
280
+ describe "Revision" do
281
+ let(:r) { 100 }
282
+ let(:store) { Spontaneous::Output::Store::Moneta.new(:Memory) }
283
+ let(:revision) { Spontaneous::Output::Store::Revision.new(r, store) }
284
+ let(:output_html) { page.output(:html) }
285
+ let(:output_xml) { page.output(:xml) }
286
+ let(:output_json) { page.output(:json) }
287
+
288
+ before do
289
+ transaction = revision.transaction
290
+ transaction.store(output_html, false, "HTML")
291
+ transaction.store(output_xml, false, "XML")
292
+ transaction.commit
293
+ end
294
+
295
+ it "provides a transaction for writing" do
296
+ revision.transaction.must_be_instance_of Spontaneous::Output::Store::Transaction
297
+ revision.transaction.revision.must_equal r
298
+ end
299
+
300
+ it "allows for reading a static template" do
301
+ template = revision.static_template(output_html)
302
+ template.read.must_equal "HTML"
303
+ end
304
+
305
+ it "allows for reading a dynamic template" do
306
+ template = revision.dynamic_template(output_xml)
307
+ template.read.must_equal "XML"
308
+ end
309
+
310
+ it "doesn't return a dynamic template as a static one" do
311
+ template = revision.static_template(output_xml)
312
+ template.must_equal nil
313
+ end
314
+
315
+ it "allows for deletion of the revision" do
316
+ store.expects(:delete_revision).with(r)
317
+ revision.delete
318
+ end
319
+ end
320
+
321
+ describe "Store" do
322
+ let(:store) { Spontaneous::Output::Store.new(:Memory) }
323
+ let(:output_html) { page.output(:html) }
324
+ let(:output_xml) { page.output(:xml) }
325
+
326
+ before do
327
+ transaction = store.revision(20).transaction
328
+ transaction.store(output_html, false, "HTML")
329
+ transaction.store(output_xml, false, "XML")
330
+ transaction.commit
331
+ end
332
+
333
+ it "will provide a revision instance" do
334
+ store.revision(20).must_be_instance_of Spontaneous::Output::Store::Revision
335
+ store.revision(20).revision.must_equal 20
336
+ end
337
+
338
+ it "can provide a list of revisions" do
339
+ store.revisions.must_equal [20]
340
+ end
341
+ end
342
+ end