spontaneous 0.2.0.beta7 → 0.2.0.beta8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 37102d6cbb9bfed6dc5d0360590d78f569ca3194
4
- data.tar.gz: ae60e964be4ae4a4f9fbfd94154c28c1766be9ce
3
+ metadata.gz: 1fbce2b0e756da55c854fb535ed8454a1aed9aed
4
+ data.tar.gz: 9a3d4faf5726570997d13c3786246768c3c3495e
5
5
  SHA512:
6
- metadata.gz: a05914ff42c526f549a6e0367d9df545e700b233eb0312bb570448eeb0e48434764c7c6f8b83088c34680ce2bc51bb2e9f27665165947ce3b1546532dcf6a044
7
- data.tar.gz: b0ca442ffa7eef22df25a7df9813f6d8c1263ec359fffff1eb4b6e3ad6cc6c73bb4a5d9f2265e949778fd07b639891fb2635dc076fd6bdcbf444cf32514fcc87
6
+ metadata.gz: 71decdd38247bc06e094eec89575a899b8e95a4156f4a8b431548b58a591f3721ee585b134a420faa773beed01be88c9ca70470db1d425d61bd5fdba8d7167d3
7
+ data.tar.gz: 08ddbb0a24ccf19d7b2a76ab72e0d677ade2106d259deb00a3c9884aaa84454ac1073ee29d56eebc78fdca3e4886898c1477f792deb49ad66e5dffb7b823bd42
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.2.0.beta8, released 2014-10-29
2
+
3
+ #### Fixes
4
+
5
+ - Include `-webkit` prefixed versions of all flexbox properties to fix display in Safari
6
+ - Fix image drag & drop in Chrome -- revoking blob URLs immediately now results in a broken image
7
+ - Remove all references to `Page#path` in initialization to avoid trying to resolve the full path before the pages' parents have been assigned
8
+ - Explicitly copy compiled assets to the new published revision using a new core publish step rather than hijack the asset compilation/resolution step. This ensures that dependencies are copied on a second publish.
9
+ - Fix layout of box tabs by using appropriate mixins to apply `-webkit` prefixed styles
10
+
1
11
  ## 0.2.0.beta7, released 2014-09-03
2
12
 
3
13
  #### Fixes
@@ -659,17 +659,17 @@
659
659
  height: 100%;
660
660
 
661
661
  .slot-tabs {
662
+ @include display-box;
663
+ @include box-orient(row);
662
664
  min-height: 32px;
663
665
  vertical-align: top;
664
666
  @include vertical-gradient(#666666, #4d4d4d);
665
667
  padding-left: $container-padding;
666
668
  padding-right: $container-padding;
667
- display: flex;
668
- flex-direction: row;
669
669
  li {
670
670
  @include box-sizing;
671
671
  @include interface;
672
- flex: 1 1 auto;
672
+ @include box-flex(1);
673
673
  font-size: 11px;
674
674
  color: #dddddd;
675
675
  display: block;
@@ -1669,7 +1669,7 @@
1669
1669
 
1670
1670
  @media all and (max-width: 1000px) {
1671
1671
  .fields-preview-text li {
1672
- flex-direction: column;
1672
+ @include box-orient(column);
1673
1673
  }
1674
1674
  }
1675
1675
  @import "top.css.scss";
@@ -115,6 +115,7 @@ $target-name-size: 14px;
115
115
  // display: -webkit-box;
116
116
  // display: -moz-box;
117
117
  display: flex;
118
+ display: -webkit-flex;
118
119
  }
119
120
  @mixin box-sizing($model: border-box) {
120
121
  -webkit-box-sizing: $model;
@@ -124,15 +125,19 @@ $target-name-size: 14px;
124
125
 
125
126
  @mixin box-orient($direction: row) {
126
127
  flex-direction: $direction;
128
+ -webkit-flex-direction: $direction;
127
129
  }
128
130
  @mixin box-pack($packing: center) {
129
131
  justify-content: $packing;
132
+ -webkit-justify-content: $packing;
130
133
  }
131
134
  @mixin box-align($aligning: start) {
132
135
  align-items: $aligning;
136
+ -webkit-align-items: $aligning;
133
137
  }
134
138
  @mixin box-flex($flex: 1) {
135
- flex-grow: $flex;
139
+ flex-grow: $flex;
140
+ -webkit-flex-grow: $flex;
136
141
  }
137
142
 
138
143
  @mixin no-select {
@@ -413,6 +413,7 @@ $dialogue-title-height: 32px;
413
413
  background-color: rgba(33, 33, 33, 0.9);
414
414
  color: #aaaaaa;
415
415
  font-size: 0.7em;
416
+ z-index: 1;
416
417
  .lock-state {
417
418
  overflow: auto;
418
419
  padding: $unit/2;
@@ -4,6 +4,10 @@ Spontaneous.Field.File = (function($, S) {
4
4
  var FileField = new JS.Class(Spontaneous.Field.String, {
5
5
  selected_files: false,
6
6
 
7
+ initialize: function(dialogue, conflict) {
8
+ this.blobs = [];
9
+ this.callSuper();
10
+ },
7
11
  currentValue: function() {
8
12
  var pending, v = this.get('value');
9
13
  if ((pending = v.__pending__)) {
@@ -111,10 +115,16 @@ Spontaneous.Field.File = (function($, S) {
111
115
  dialogue.open();
112
116
  },
113
117
  unload: function() {
114
- this.callSuper();
115
118
  this.input = null;
116
119
  this._progress_bar = null;
120
+ this.freeBlobs();
117
121
  Spontaneous.UploadManager.unregister(this);
122
+ this.callSuper();
123
+ },
124
+ freeBlobs: function() {
125
+ this.blobs.forEach(function(url) {
126
+ window.URL.revokeObjectURL(url);
127
+ });
118
128
  },
119
129
  upload_complete: function(values) {
120
130
  this.set('value', values.processed_value);
@@ -146,6 +156,12 @@ Spontaneous.Field.File = (function($, S) {
146
156
  return true;
147
157
  },
148
158
 
159
+ createObjectURL: function(file) {
160
+ var url = window.URL.createObjectURL(file);
161
+ this.blobs.push(url);
162
+ return url;
163
+ },
164
+
149
165
  edit: function() {
150
166
  var self = this;
151
167
  var wrap = dom.div('.file-field', {'style':'position:relative;'});
@@ -164,10 +180,9 @@ Spontaneous.Field.File = (function($, S) {
164
180
 
165
181
  var files_selected = function(files) {
166
182
  if (files.length > 0) {
167
- var file = files[0], url = window.URL.createObjectURL(file);
183
+ var file = files[0], url = this.createObjectURL(file);
168
184
  this.selected_files = files;
169
185
  this._edited_value = url;
170
- window.URL.revokeObjectURL(url);
171
186
  set_info(File.filename(file), file.fileSize);
172
187
  }
173
188
  }.bind(this);
@@ -160,7 +160,7 @@ Spontaneous.Field.Image = (function($, S) {
160
160
  if (files.length > 0) {
161
161
  this.select_files(files);
162
162
  var file = files[0],
163
- url = window.URL.createObjectURL(file)
163
+ url = this.createObjectURL(file)
164
164
  , image = this.image;
165
165
  this._edited_value = url;
166
166
  image.__start_upload = true;
@@ -177,8 +177,6 @@ Spontaneous.Field.Image = (function($, S) {
177
177
  }
178
178
  }.bind(this));
179
179
  image.attr('src', url);
180
- // see http://www.htmlfivewow.com/slide25
181
- window.URL.revokeObjectURL(url);
182
180
  }
183
181
  return false;
184
182
  }.bind(this);
@@ -306,13 +304,12 @@ Spontaneous.Field.Image = (function($, S) {
306
304
 
307
305
  var files_selected = function(files) {
308
306
  if (files.length > 0) {
309
- var file = files[0], url = window.URL.createObjectURL(file);
307
+ var file = files[0], url = this.createObjectURL(file);
310
308
  img.attr('src', url).removeClass('empty');
311
309
  this.select_files(files);
312
310
  img.attr('src', url);
313
311
  this._edited_value = url;
314
312
  this.image.attr('src', url);
315
- window.URL.revokeObjectURL(url);
316
313
  set_info(File.filename(file), file.fileSize, null, null);
317
314
  }
318
315
  }.bind(this);
@@ -1,12 +1,21 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  Sequel.migration do
4
+ no_transaction
4
5
  up do
5
- [:content, :spontaneous_content_archive, :spontaneous_content_history].each do |table|
6
- alter_table table do
7
- add_column :content_hash, :varchar, size: 32
8
- add_column :published_content_hash, :varchar, size: 32
9
- add_column :content_hash_changed, :boolean, default: true
6
+ [:content, :spontaneous_content_history, :spontaneous_content_archive].each do |table|
7
+ begin
8
+ alter_table table do
9
+ add_column :content_hash, :varchar, size: 32
10
+ add_column :published_content_hash, :varchar, size: 32
11
+ add_column :content_hash_changed, :boolean, default: true
12
+ end
13
+ rescue ::Sequel::DatabaseError => e
14
+ # because syncing ignores the content_archive table
15
+ # if we run this migration, then sync, then re-run it
16
+ # (in the case where the production db is behind the dev db)
17
+ # this one table will throw an error
18
+ raise unless table == :spontaneous_content_archive
10
19
  end
11
20
  end
12
21
  alter_table :content do
@@ -15,32 +24,34 @@ Sequel.migration do
15
24
 
16
25
  # testing environment
17
26
  if defined?(Content)
27
+ transaction do
18
28
 
19
- self.logger = nil
29
+ self.logger = nil
20
30
 
21
- state = Spontaneous::State.first
31
+ state = Spontaneous::State.first
22
32
 
23
- published_revision = state.nil? ? nil : state[:published_revision]
33
+ published_revision = state.nil? ? nil : state[:published_revision]
24
34
 
25
- content_hash = published_content_hash = nil
35
+ content_hash = published_content_hash = nil
26
36
 
27
- model = Spontaneous::Model(:content)
37
+ model = Spontaneous::Model(:content)
28
38
 
29
- update_content_hashes = Proc.new do |content|
30
- published_content_hash = nil
31
- content_hash = content.calculate_content_hash!
32
- if published_revision
33
- model.mapper.scope(published_revision, false) do
34
- published = model[content.id]
35
- published_content_hash = published.calculate_content_hash! if published
39
+ update_content_hashes = Proc.new do |content|
40
+ published_content_hash = nil
41
+ content_hash = content.calculate_content_hash!
42
+ if published_revision
43
+ model.mapper.scope(published_revision, false) do
44
+ published = model[content.id]
45
+ published_content_hash = published.calculate_content_hash! if published
46
+ end
36
47
  end
48
+ p [content.id, content.class, content.path, content_hash, published_content_hash] #if published_content_hash != content_hash
49
+ model.dataset.unfiltered.where(id: content.id).update(content_hash: content_hash, published_content_hash: published_content_hash, content_hash_changed: (content_hash != published_content_hash))
37
50
  end
38
- p [content.id, content.class, content.path, content_hash, published_content_hash] #if published_content_hash != content_hash
39
- model.dataset.unfiltered.where(id: content.id).update(content_hash: content_hash, published_content_hash: published_content_hash, content_hash_changed: (content_hash != published_content_hash))
40
- end
41
51
 
42
- Content::Piece.dataset.order(Sequel.desc(:depth)).each(&update_content_hashes)
43
- Content::Page.dataset.order(Sequel.desc(:depth)).each(&update_content_hashes)
52
+ Content::Piece.dataset.order(Sequel.desc(:depth)).each(&update_content_hashes)
53
+ Content::Page.dataset.order(Sequel.desc(:depth)).each(&update_content_hashes)
54
+ end
44
55
  end
45
56
  end
46
57
 
@@ -1,12 +1,23 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  Sequel.migration do
4
+ no_transaction
4
5
  up do
5
6
  [:content, :spontaneous_content_archive, :spontaneous_content_history].each do |table|
6
- alter_table table do
7
- add_column :content_hash_changed_at, :timestamp
7
+ begin
8
+ alter_table table do
9
+ add_column :content_hash_changed_at, :timestamp
10
+ end
11
+ rescue ::Sequel::DatabaseError => e
12
+ # because syncing ignores the content_archive table
13
+ # if we run this migration, then sync, then re-run it
14
+ # (in the case where the production db is behind the dev db)
15
+ # this one table will throw an error
16
+ raise unless table == :spontaneous_content_archive
17
+ end
18
+ transaction do
19
+ self[table].update(content_hash_changed_at: :modified_at)
8
20
  end
9
- self[table].update(content_hash_changed_at: :modified_at)
10
21
  end
11
22
  end
12
23
 
@@ -5,12 +5,16 @@ module Spontaneous::Asset
5
5
  module Environment
6
6
  def self.new(context)
7
7
  if context.publishing?
8
- Publish.new(context.site, context.revision, context.development?)
8
+ publishing(context.site, context.revision, context.development?)
9
9
  else
10
- Preview.new(context.site)
10
+ preview(context.site)
11
11
  end
12
12
  end
13
13
 
14
+ def self.publishing(site, revision, development)
15
+ Publish.new(site, revision, development)
16
+ end
17
+
14
18
  def self.preview(site = Spontaneous::Site.instance)
15
19
  Preview.new(site)
16
20
  end
@@ -125,11 +129,19 @@ module Spontaneous::Asset
125
129
 
126
130
  def to_url(asset, body = false)
127
131
  return asset if asset.is_a?(String)
132
+ query = {}
133
+ query['body'] = 1 if body
134
+ query[asset.digest] = nil if dynamic_fingerprint?
128
135
  path = asset.logical_path
129
- path = "#{path}?body=1" if body
136
+ path = "#{path}?#{Rack::Utils.build_query(query)}" unless query.empty?
130
137
  "/" << asset_mount_point << "/" << path
131
138
  end
132
139
 
140
+ # include the asset fingerprint as a query param for cache busting
141
+ def dynamic_fingerprint?
142
+ true
143
+ end
144
+
133
145
  def asset_mount_point
134
146
  "assets"
135
147
  end
@@ -175,6 +187,11 @@ module Spontaneous::Asset
175
187
  @development || false
176
188
  end
177
189
 
190
+ # include the asset fingerprint as a query param for cache busting
191
+ def dynamic_fingerprint?
192
+ false
193
+ end
194
+
178
195
  # A proxy to the sprockets manifest that compiles assets on the first run
179
196
  # then re-uses them on the second
180
197
  class Manifest
@@ -202,29 +219,10 @@ module Spontaneous::Asset
202
219
  unless (args.all? { |key| assets.key?(key) })
203
220
  compile!(*args)
204
221
  end
205
- copy_assets_to_revision(args)
206
222
  end
207
223
 
208
224
  def compile!(*args)
209
225
  @manifest.compile(*args)
210
- copy_assets_to_revision(args)
211
- end
212
-
213
- def copy_assets_to_revision(logical_paths)
214
- assets = @manifest.assets
215
- paths = logical_paths.map { |a| assets[a] }.compact
216
- source, dest = shared_asset_dir, revision_asset_dir
217
- paths.each do |asset|
218
- copy_asset_to_revision(source, dest, asset)
219
- end
220
- end
221
-
222
- def copy_asset_to_revision(source, dest, asset)
223
- to = dest + asset
224
- return if to.exist?
225
- from = source + asset
226
- to.dirname.mkpath
227
- FileUtils.cp(from, to)
228
226
  end
229
227
 
230
228
  def asset_compilation_dir
@@ -70,7 +70,14 @@ module Spontaneous::Model
70
70
  # => [#<ContentClass...>, #<ContentClass...>]
71
71
  #
72
72
  def to_proc
73
- Proc.new { |obj| self === obj }
73
+ Proc.new { |obj| self.===(obj) }
74
+ end
75
+
76
+ # Expands type testing to include peeking inside PagePiece instances
77
+ def ===(other)
78
+ return true if super
79
+ return (self == other.content_class) if other.respond_to?(:content_class)
80
+ false
74
81
  end
75
82
  end
76
83
 
@@ -148,6 +155,10 @@ module Spontaneous::Model
148
155
 
149
156
  alias_method :is_page?, :page?
150
157
 
158
+ def content_class
159
+ self
160
+ end
161
+
151
162
  # Do not overwrite this method directly.
152
163
  # If you want a page to render the content of another configure
153
164
  # the type using Content::render
@@ -30,11 +30,6 @@ module Spontaneous::Model::Page
30
30
 
31
31
  private :__create_private_root=, :__create_private_root?
32
32
 
33
- def after_initialize
34
- super
35
- set_generated_slug
36
- end
37
-
38
33
  def before_create
39
34
  place_in_page_tree
40
35
  set_slug_from_dynamic_value
@@ -46,11 +41,6 @@ module Spontaneous::Model::Page
46
41
  fix_generated_slug_conflicts
47
42
  end
48
43
 
49
- def set_generated_slug
50
- return unless slug.nil?
51
- self.slug = generate_default_slug
52
- end
53
-
54
44
  def fix_generated_slug_conflicts
55
45
  o = s = slug || generate_default_slug
56
46
  n = 0
@@ -183,9 +173,13 @@ module Spontaneous::Model::Page
183
173
  end
184
174
  end
185
175
 
176
+ def initialized_slug
177
+ return slug unless slug.nil?
178
+ self.slug = generate_default_slug
179
+ end
186
180
 
187
181
  def calculate_path
188
- calculate_path_with_slug(slug)
182
+ calculate_path_with_slug(initialized_slug)
189
183
  end
190
184
 
191
185
  def calculate_path_with_slug(slug)
@@ -32,6 +32,12 @@ module Spontaneous
32
32
  target
33
33
  end
34
34
 
35
+ # Used by Spontaneous::Model::Core::=== to look inside PagePiece objects
36
+ # and test against the class of the target, not of the proxy
37
+ def content_class
38
+ target.class
39
+ end
40
+
35
41
  def id
36
42
  target.id
37
43
  end
@@ -132,6 +132,7 @@ module Spontaneous::Publishing
132
132
  :create_revision_directory,
133
133
  :render_revision,
134
134
  :generate_search_indexes,
135
+ :copy_assets,
135
136
  :copy_static_files,
136
137
  :generate_rackup_file,
137
138
  :activate_revision,
@@ -148,6 +149,7 @@ require 'spontaneous/publishing/steps/create_revision_directory'
148
149
  require 'spontaneous/publishing/steps/render_revision'
149
150
  require 'spontaneous/publishing/steps/generate_search_indexes'
150
151
  require 'spontaneous/publishing/steps/copy_static_files'
152
+ require 'spontaneous/publishing/steps/copy_assets'
151
153
  require 'spontaneous/publishing/steps/generate_rackup_file'
152
154
  require 'spontaneous/publishing/steps/activate_revision'
153
155
  require 'spontaneous/publishing/steps/write_revision_file'
@@ -0,0 +1,77 @@
1
+ module Spontaneous::Publishing::Steps
2
+ class CopyAssets < BaseStep
3
+
4
+ def count
5
+ assets.length
6
+ end
7
+
8
+ def call
9
+ @progress.stage("copying assets")
10
+ ensure_asset_dir
11
+ assets.each do |logical_path, asset|
12
+ copy_asset(asset)
13
+ @progress.step(1, "'#{logical_path}' => '#{asset}'")
14
+ end
15
+ end
16
+
17
+ def rollback
18
+ FileUtils.rm_r(revision_asset) if File.exists?(revision_asset)
19
+ end
20
+
21
+ def ensure_asset_dir
22
+ dir = revision_asset
23
+ end
24
+
25
+ def copy_asset(asset)
26
+ ['', '.gz'].each do |suffix|
27
+ copy_asset_file(asset + suffix)
28
+ end
29
+ end
30
+
31
+ def copy_asset_file(asset)
32
+ source = File.join(manifest.asset_compilation_dir, asset)
33
+ if File.exist?(source)
34
+ dest = ensure_dir File.join(revision_asset, asset)
35
+ link_file(source, dest)
36
+ end
37
+ end
38
+
39
+ def link_file(source, dest)
40
+ src_dev = File::stat(source).dev
41
+ dst_dev = File::stat(File.dirname(dest)).dev
42
+ if (src_dev == dst_dev)
43
+ FileUtils.ln(source, dest, :force => true)
44
+ else
45
+ FileUtils.cp(source, dest)
46
+ end
47
+ end
48
+
49
+ def revision_asset
50
+ @asset_dest ||= Pathname.new(Spontaneous.revision_dir(revision) / 'assets').tap do |path|
51
+ FileUtils.mkdir_p(path) unless File.exists?(path)
52
+ end
53
+ end
54
+
55
+ def ensure_dir(path)
56
+ dir = File.dirname(path)
57
+ FileUtils.mkdir_p(dir) unless File.exist?(dir)
58
+ path
59
+ end
60
+
61
+ def assets
62
+ manifest.assets
63
+ end
64
+
65
+ def manifest
66
+ environment.manifest
67
+ end
68
+
69
+ def environment
70
+ @environment ||= Spontaneous::Asset::Environment.publishing(site, revision, development?)
71
+ end
72
+
73
+ def development?
74
+ Spontaneous.development?
75
+ end
76
+ end
77
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Spontaneous
4
- VERSION = "0.2.0.beta7"
4
+ VERSION = "0.2.0.beta8"
5
5
  GEM = false
6
6
  end
@@ -0,0 +1,6 @@
1
+ $width: 23px;
2
+
3
+ .something {
4
+ width: $width;
5
+ background-image: asset_url('i/sine_waves.png');
6
+ }
@@ -1110,7 +1110,7 @@ describe "Back" do
1110
1110
  it "render SASS templates" do
1111
1111
  get "/assets/css/sass_template.css"
1112
1112
  assert last_response.ok?, "Should return 200 but got #{last_response.status}"
1113
- last_response.body.must_match /color: #ffeeff/
1113
+ last_response.body.must_match /color: #fef/
1114
1114
  end
1115
1115
 
1116
1116
  it "compile CoffeeScript" do
@@ -53,10 +53,10 @@ describe "AssetBundler" do
53
53
  compiled_css_path = Dir["#{@site.root}/public/@spontaneous/assets/*.css"].first
54
54
  css = File.read(compiled_css_path)
55
55
  css.must_match /\.simple \{\s+color: #aaa/
56
- css.must_match /\.basic \{\s+color: #aabbcc/
57
- css.must_match /\.complex \{\s+color: #ddeeff;\s*width: #{Date.today.day}px/
56
+ css.must_match /\.basic \{\s+color: #abc/
57
+ css.must_match /\.complex \{\s+color: #def;\s*width: #{Date.today.day}px/
58
58
  css.must_match /\.subdir\.simple \{\s+color: #000;/
59
- css.must_match /\.subdir\.complex \{\s+color: #aaaaaa;\s+height: #{Date.today.day}px/
59
+ css.must_match /\.subdir\.complex \{\s+color: #aaa;\s+height: #{Date.today.day}px/
60
60
  end
61
61
 
62
62
  it "produce compressed CSS by default" do
@@ -65,10 +65,10 @@ describe "AssetBundler" do
65
65
  compiled_css_path = Dir["#{@site.root}/public/@spontaneous/assets/spontaneous*.css"].first
66
66
  css = File.read(compiled_css_path)
67
67
  css.must_match /\.simple\{color:#aaa\}/
68
- css.must_match /\.basic\{color:#aabbcc\}/
69
- css.must_match /\.complex\{color:#ddeeff;width:#{Date.today.day}px\}/
68
+ css.must_match /\.basic\{color:#abc\}/
69
+ css.must_match /\.complex\{color:#def;width:#{Date.today.day}px\}/
70
70
  css.must_match /\.subdir\.simple\{color:#000\}/
71
- css.must_match /\.subdir\.complex\{color:#aaaaaa;height:#{Date.today.day}px\}/
71
+ css.must_match /\.subdir\.complex\{color:#aaa;height:#{Date.today.day}px\}/
72
72
  end
73
73
  end
74
74
 
@@ -6,9 +6,7 @@ require 'ostruct'
6
6
  describe "Assets" do
7
7
  include RackTestMethods
8
8
 
9
- def app
10
- Spontaneous::Rack::Back.application(site)
11
- end
9
+ let(:app) {Spontaneous::Rack::Back.application(site)}
12
10
 
13
11
  module LiveSimulation
14
12
  # simulate a production + publishing environment
@@ -59,6 +57,13 @@ describe "Assets" do
59
57
  end
60
58
  end
61
59
 
60
+ def asset_digest(asset_relative_path)
61
+ digest = context.asset_environment.environment.digest
62
+ digest.update(File.read(File.join(fixture_root, asset_relative_path)))
63
+ digest.hexdigest
64
+ end
65
+
66
+ let(:y_png_digest) { asset_digest('public2/i/y.png') }
62
67
 
63
68
  start do
64
69
  fixture_root = File.expand_path("../../fixtures/assets", __FILE__)
@@ -136,16 +141,32 @@ describe "Assets" do
136
141
  describe "development" do
137
142
  let(:context) { development_context }
138
143
 
144
+ let(:a_js_digest) { asset_digest('public1/js/a.js') }
145
+ let(:b_js_digest) { asset_digest('public2/js/b.js') }
146
+ let(:c_js_digest) { asset_digest('public2/js/c.js') }
147
+ let(:x_js_digest) { asset_digest('public1/x.js') }
148
+ # these are compiled so fairly complex to calculate their digests
149
+ # not impossible, but annoying
150
+ let(:n_js_digest) { '74f175e03a4bdc8c807aba4ae0314938' }
151
+ let(:m_js_digest) { 'dd35b142dc75b6ec15b2138e9e91c0c3' }
152
+ let(:all_js_digest) { 'd406fc3c21d90828a2f0a718c89e8d99' }
153
+
154
+ let(:a_css_digest) { '7b04d295476986c24d8c77245943e5b9' }
155
+ let(:b_css_digest) { '266643993e14da14f2473d45f003bd2c' }
156
+ let(:c_css_digest) { 'fc8ba0d0aae64081dc00b8444a198fb8' }
157
+ let(:x_css_digest) { '2560aec2891794825eba770bf84823fb' }
158
+ let(:all_css_digest) { 'cf61c624b91b9ea126804291ac55bd5d' }
159
+
139
160
  it "includes all js dependencies" do
140
161
  result = context.scripts('js/all', 'js/m', 'js/c', 'x')
141
162
  result.must_equal [
142
- '<script type="text/javascript" src="/assets/js/a.js?body=1"></script>',
143
- '<script type="text/javascript" src="/assets/js/b.js?body=1"></script>',
144
- '<script type="text/javascript" src="/assets/js/n.js?body=1"></script>',
145
- '<script type="text/javascript" src="/assets/js/all.js?body=1"></script>',
146
- '<script type="text/javascript" src="/assets/js/m.js?body=1"></script>',
147
- '<script type="text/javascript" src="/assets/js/c.js?body=1"></script>',
148
- '<script type="text/javascript" src="/assets/x.js?body=1"></script>'
163
+ %|<script type="text/javascript" src="/assets/js/a.js?body=1&#{a_js_digest}"></script>|,
164
+ %|<script type="text/javascript" src="/assets/js/b.js?body=1&#{b_js_digest}"></script>|,
165
+ %|<script type="text/javascript" src="/assets/js/n.js?body=1&#{n_js_digest}"></script>|,
166
+ %|<script type="text/javascript" src="/assets/js/all.js?body=1&#{all_js_digest}"></script>|,
167
+ %|<script type="text/javascript" src="/assets/js/m.js?body=1&#{m_js_digest}"></script>|,
168
+ %|<script type="text/javascript" src="/assets/js/c.js?body=1&#{c_js_digest}"></script>|,
169
+ %|<script type="text/javascript" src="/assets/x.js?body=1&#{x_js_digest}"></script>|
149
170
  ].join("\n")
150
171
  end
151
172
 
@@ -158,11 +179,11 @@ describe "Assets" do
158
179
  it "includes all css dependencies" do
159
180
  result = context.stylesheets('css/all', 'css/c', 'x')
160
181
  result.must_equal [
161
- '<link rel="stylesheet" href="/assets/css/b.css?body=1" />',
162
- '<link rel="stylesheet" href="/assets/css/a.css?body=1" />',
163
- '<link rel="stylesheet" href="/assets/css/all.css?body=1" />',
164
- '<link rel="stylesheet" href="/assets/css/c.css?body=1" />',
165
- '<link rel="stylesheet" href="/assets/x.css?body=1" />'
182
+ %|<link rel="stylesheet" href="/assets/css/b.css?body=1&#{b_css_digest}" />|,
183
+ %|<link rel="stylesheet" href="/assets/css/a.css?body=1&#{a_css_digest}" />|,
184
+ %|<link rel="stylesheet" href="/assets/css/all.css?body=1&#{all_css_digest}" />|,
185
+ %|<link rel="stylesheet" href="/assets/css/c.css?body=1&#{c_css_digest}" />|,
186
+ %|<link rel="stylesheet" href="/assets/x.css?body=1&#{x_css_digest}" />|
166
187
  ].join("\n")
167
188
  end
168
189
 
@@ -183,14 +204,24 @@ describe "Assets" do
183
204
  let(:app) { Spontaneous::Rack::Back.application(site) }
184
205
  let(:context) { preview_context }
185
206
 
207
+ let(:c_js_digest) { 'f669550dd7e10e9646ad781f44756950' }
208
+ let(:x_js_digest) { '6b4c9176b2838a4949a18284543fc19c' }
209
+ let(:n_js_digest) { '74f175e03a4bdc8c807aba4ae0314938' }
210
+ let(:m_js_digest) { 'dd35b142dc75b6ec15b2138e9e91c0c3' }
211
+ let(:all_js_digest) { 'cd1f681752f5038421be0bc5ea0e855d' }
212
+
213
+ let(:c_css_digest) { 'fc8ba0d0aae64081dc00b8444a198fb8' }
214
+ let(:x_css_digest) { '2560aec2891794825eba770bf84823fb' }
215
+ let(:all_css_digest) { 'bb2c289a27b3d5d4467dde6d60722fd3' }
216
+
186
217
  describe "javascript" do
187
- it "include scripts as separate files" do
218
+ it "include scripts as separate files with finger prints" do
188
219
  result = context.scripts('js/all', 'js/m.js', 'js/c.js', 'x')
189
220
  result.must_equal [
190
- '<script type="text/javascript" src="/assets/js/all.js"></script>',
191
- '<script type="text/javascript" src="/assets/js/m.js"></script>',
192
- '<script type="text/javascript" src="/assets/js/c.js"></script>',
193
- '<script type="text/javascript" src="/assets/x.js"></script>'
221
+ %|<script type="text/javascript" src="/assets/js/all.js?#{all_js_digest}"></script>|,
222
+ %|<script type="text/javascript" src="/assets/js/m.js?#{m_js_digest}"></script>|,
223
+ %|<script type="text/javascript" src="/assets/js/c.js?#{c_js_digest}"></script>|,
224
+ %|<script type="text/javascript" src="/assets/x.js?#{x_js_digest}"></script>|
194
225
  ].join("\n")
195
226
  end
196
227
 
@@ -199,15 +230,15 @@ describe "Assets" do
199
230
  it "handles urls passed as an array" do
200
231
  result = context.scripts(['js/all', 'js/m.js'])
201
232
  result.must_equal [
202
- '<script type="text/javascript" src="/assets/js/all.js"></script>',
203
- '<script type="text/javascript" src="/assets/js/m.js"></script>'
233
+ %|<script type="text/javascript" src="/assets/js/all.js?#{all_js_digest}"></script>|,
234
+ %|<script type="text/javascript" src="/assets/js/m.js?#{m_js_digest}"></script>|
204
235
  ].join("\n")
205
236
  end
206
237
 
207
238
  it "should ignore missing files" do
208
239
  result = context.scripts('js/all', 'js/missing')
209
240
  result.must_equal [
210
- '<script type="text/javascript" src="/assets/js/all.js"></script>',
241
+ %|<script type="text/javascript" src="/assets/js/all.js?#{all_js_digest}"></script>|,
211
242
  '<script type="text/javascript" src="js/missing.js"></script>'
212
243
  ].join("\n")
213
244
  end
@@ -244,7 +275,7 @@ describe "Assets" do
244
275
  context = preview_context
245
276
  result = context.scripts('js/all', '//use.typekit.com/abcde', 'http://cdn.google.com/jquery.js', 'https://cdn.google.com/jquery.js')
246
277
  result.must_equal [
247
- '<script type="text/javascript" src="/assets/js/all.js"></script>',
278
+ %|<script type="text/javascript" src="/assets/js/all.js?#{all_js_digest}"></script>|,
248
279
  '<script type="text/javascript" src="//use.typekit.com/abcde"></script>',
249
280
  '<script type="text/javascript" src="http://cdn.google.com/jquery.js"></script>',
250
281
  '<script type="text/javascript" src="https://cdn.google.com/jquery.js"></script>'
@@ -256,18 +287,18 @@ describe "Assets" do
256
287
  it "include css files as separate links" do
257
288
  result = context.stylesheets('css/all', 'css/c', 'x')
258
289
  result.must_equal [
259
- '<link rel="stylesheet" href="/assets/css/all.css" />',
260
- '<link rel="stylesheet" href="/assets/css/c.css" />',
261
- '<link rel="stylesheet" href="/assets/x.css" />'
290
+ %|<link rel="stylesheet" href="/assets/css/all.css?#{all_css_digest}" />|,
291
+ %|<link rel="stylesheet" href="/assets/css/c.css?#{c_css_digest}" />|,
292
+ %|<link rel="stylesheet" href="/assets/x.css?#{x_css_digest}" />|
262
293
  ].join("\n")
263
294
  end
264
295
 
265
296
  it "allows passing scripts as an array" do
266
297
  result = context.stylesheets(['css/all', 'css/c', 'x'])
267
298
  result.must_equal [
268
- '<link rel="stylesheet" href="/assets/css/all.css" />',
269
- '<link rel="stylesheet" href="/assets/css/c.css" />',
270
- '<link rel="stylesheet" href="/assets/x.css" />'
299
+ %|<link rel="stylesheet" href="/assets/css/all.css?#{all_css_digest}" />|,
300
+ %|<link rel="stylesheet" href="/assets/css/c.css?#{c_css_digest}" />|,
301
+ %|<link rel="stylesheet" href="/assets/x.css?#{x_css_digest}" />|
271
302
  ].join("\n")
272
303
  end
273
304
 
@@ -327,11 +358,11 @@ describe "Assets" do
327
358
 
328
359
  it "should allow for embedding asset images into templates" do
329
360
  result = renderer.render_string("${ asset_path 'i/y.png' }", @page.output(:html))
330
- result.must_equal "/assets/i/y.png"
361
+ result.must_equal "/assets/i/y.png?#{y_png_digest}"
331
362
  end
332
363
  it "should allow for embedding asset urls into templates" do
333
364
  result = renderer.render_string("${ asset_url 'i/y.png' }", @page.output(:html))
334
- result.must_equal "url(/assets/i/y.png)"
365
+ result.must_equal "url(/assets/i/y.png?#{y_png_digest})"
335
366
  end
336
367
  end
337
368
  end
@@ -340,11 +371,17 @@ describe "Assets" do
340
371
  let(:app) { Spontaneous::Rack::Front.application(site) }
341
372
  let(:context) { live_context }
342
373
  let(:revision) { site.revision(context.revision) }
374
+ let(:progress) { Spontaneous::Publishing::Progress::Silent.new }
375
+
376
+ def publish_assets(revision)
377
+ context.asset_environment.manifest.compile!
378
+ Spontaneous::Publishing::Steps::CopyAssets.new(site, revision, [], progress).call
379
+ end
343
380
 
344
381
  before do
345
382
  FileUtils.rm_f(Spontaneous.revision_dir) if File.exist?(Spontaneous.revision_dir)
346
383
  system "ln -nfs #{revision.root} #{Spontaneous.revision_dir}"
347
- # FileUtils.ln_s(revision.root, Spontaneous.revision_dir)
384
+ publish_assets(context.revision)
348
385
  end
349
386
 
350
387
  after do
@@ -352,14 +389,14 @@ describe "Assets" do
352
389
  end
353
390
 
354
391
  describe "javascript" do
355
- let(:all_sha) { "b1d3d85feff4d68e16d2b4da97717aa0" }
356
- let(:x_sha) { "cf638f60cff3ce84f156cb4621a914b4" }
392
+ let(:all_sha) { "ed62549e8edc1f61a1e27136602f01d9" }
393
+ let(:x_sha) { "66e92be1e412458f6ff02f4c5dd9beb1" }
357
394
  it "bundles & fingerprints local scripts" do
358
395
  result = context.scripts('js/all', 'js/m.js', 'js/c.js', 'x')
359
396
  result.must_equal [
360
397
  %(<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>',
398
+ '<script type="text/javascript" src="/assets/js/m-a5be7324bc314d5cf470a59c3732ef10.js"></script>',
399
+ '<script type="text/javascript" src="/assets/js/c-c24bcbb4f9647b078cc919746aa7fc3a.js"></script>',
363
400
  %(<script type="text/javascript" src="/assets/x-#{x_sha}.js"></script>)
364
401
  ].join("\n")
365
402
  end
@@ -413,6 +450,7 @@ describe "Assets" do
413
450
  context = live_context
414
451
  def context.revision; 100 end
415
452
  revision = site.revision(context.revision)
453
+ publish_assets(context.revision)
416
454
  manifest = Spontaneous::JSON.parse File.read(site.path("assets/tmp") + "manifest.json")
417
455
  compiled = manifest[:assets][:"js/all.js"]
418
456
  ::File.open(site.path("assets/tmp")+compiled, 'w') do |file|
@@ -426,14 +464,14 @@ describe "Assets" do
426
464
  end
427
465
 
428
466
  describe "css" do
429
- let(:all_sha) { "2468ffc5102b6bfcaf69a4fc8db59fdd" }
430
- let(:x_sha) { "ae3ee1dc79a34d24e28456118c1b9623" }
467
+ let(:all_sha) { "2e17f25ddeba996223a6cd1e28e7a319" }
468
+ let(:x_sha) { "2560aec2891794825eba770bf84823fb" }
431
469
 
432
470
  it "bundles & fingerprints local stylesheets" do
433
471
  result = context.stylesheets('css/all', 'css/a.css', 'x')
434
472
  result.must_equal [
435
473
  %(<link rel="stylesheet" href="/assets/css/all-#{all_sha}.css" />),
436
- '<link rel="stylesheet" href="/assets/css/a-35b26d0cd9c7ebff494c8627e0d4ed14.css" />',
474
+ '<link rel="stylesheet" href="/assets/css/a-0164c6d5b696ec2f2c5e70cade040da8.css" />',
437
475
  %(<link rel="stylesheet" href="/assets/x-#{x_sha}.css" />)
438
476
  ].join("\n")
439
477
  end
@@ -457,7 +495,7 @@ describe "Assets" do
457
495
  ].join("\n")
458
496
  end
459
497
 
460
- it "makes bundled scripts available under /assets" do
498
+ it "makes bundled stylesheets available under /assets" do
461
499
  path = context.stylesheet_urls('css/all').first
462
500
  get path
463
501
  asset_path = revision.path(path)
@@ -499,17 +537,15 @@ describe "Assets" do
499
537
  end
500
538
  end
501
539
 
502
- let(:y_sha) { "e2b6a69468b467c7414ae0e12124a66e" }
503
-
504
540
  describe "images" do
505
541
  it "bundles images and links using fingerprinted asset url" do
506
542
  path = context.stylesheet_urls('css/image1').first
507
543
  get path
508
544
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
509
545
  result = last_response.body
510
- result.must_match %r{background:url\(/assets/i/y-#{y_sha}\.png\)}
546
+ result.must_match %r{background:url\(/assets/i/y-#{y_png_digest}\.png\)}
511
547
 
512
- asset_path = revision.path("/assets/i/y-#{y_sha}.png")
548
+ asset_path = revision.path("/assets/i/y-#{y_png_digest}.png")
513
549
  assert asset_path.exist?
514
550
  end
515
551
 
@@ -526,8 +562,8 @@ describe "Assets" do
526
562
  get path
527
563
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
528
564
  result = last_response.body
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")
565
+ result.must_match %r{background:url\(/assets/i/y-#{y_png_digest}\.png\?query=true#hash\)}
566
+ asset_path = revision.path("/assets/i/y-#{y_png_digest}.png")
531
567
  assert asset_path.exist?
532
568
  end
533
569
  end
@@ -537,11 +573,11 @@ describe "Assets" do
537
573
 
538
574
  it "should allow for embedding asset images into templates" do
539
575
  result = renderer.render_string("${ asset_path 'i/y.png' }", @page.output(:html))
540
- result.must_equal "/assets/i/y-#{y_sha}.png"
576
+ result.must_equal "/assets/i/y-#{y_png_digest}.png"
541
577
  end
542
578
  it "should allow for embedding asset urls into templates" do
543
579
  result = renderer.render_string("${ asset_url 'i/y.png' }", @page.output(:html))
544
- result.must_equal "url(/assets/i/y-#{y_sha}.png)"
580
+ result.must_equal "url(/assets/i/y-#{y_png_digest}.png)"
545
581
  end
546
582
  end
547
583
  end
@@ -344,5 +344,15 @@ describe "Content" do
344
344
  it "provides a to_proc method that makes filtering by class easy" do
345
345
  @instances.select(&C).map(&:class).must_equal [C, C]
346
346
  end
347
+
348
+ it "can filter pagepieces that point to an object of the given type" do
349
+ pps = @instances.map { |i| Spontaneous::PagePiece.new(nil, i, nil)}
350
+ pps.select(&P).map { |i| i.to_page.class }.must_equal [P, P]
351
+ end
352
+
353
+ it "can filter content instances out a list POROs" do
354
+ pps = [Object.new, Object.new, @instances.first]
355
+ pps.select(&P).map { |i| i.to_page.class }.must_equal [P]
356
+ end
347
357
  end
348
358
  end
@@ -469,6 +469,80 @@ describe "Publishing Pipeline" do
469
469
  end
470
470
  end
471
471
 
472
+ describe "CopyAssets" do
473
+ let(:step) { Spontaneous::Publishing::Steps::CopyAssets }
474
+ let(:application_path) { Pathname.new(File.expand_path("../../fixtures/example_application", __FILE__)) }
475
+ let(:fixtures_path) { application_path + "assets" }
476
+ let(:revision_root) { @site.revision_dir(revision) }
477
+ let(:environment) { Spontaneous::Asset::Environment.publishing(@site, revision, false) }
478
+ let(:manifest) { environment.manifest }
479
+ let(:assets) { environment.manifest.assets }
480
+
481
+ def assert_assets(revision)
482
+ revision_root = @site.revision_dir(revision)
483
+ assets.length.must_equal 3
484
+ assets.each do |logical_path, asset|
485
+ assert File.exist?(File.join(revision_root, 'assets', asset)), "asset '#{asset}' does not exist"
486
+ end
487
+ end
488
+
489
+ before do
490
+ FileUtils.cp_r(fixtures_path, @site.root)
491
+ File.exist?(@site.root / 'assets/css/site.css.scss').must_equal true
492
+ manifest.compile!('css/site.css', 'i/xes.png')
493
+ end
494
+
495
+ it "has the right shortcut name" do
496
+ step.to_sym.must_equal :copy_assets
497
+ end
498
+
499
+ it "sets the progress stage to 'copying files'" do
500
+ progress = mock
501
+ progress.stubs(:step)
502
+ progress.expects(:stage).with("copying assets").once
503
+ run_step(progress)
504
+ end
505
+
506
+ it "steps the progress once" do
507
+ progress = mock
508
+ progress.stubs(:stage)
509
+ progress.expects(:step).with(1, instance_of(String)).times(3)
510
+ run_step(progress)
511
+ end
512
+
513
+ it "gives its step count as the number of facets" do
514
+ step.count(@site, revision, nil).must_equal 3
515
+ end
516
+
517
+ it "copies compiled assets to the revision's asset dir" do
518
+ run_step
519
+ assert_assets(revision)
520
+ end
521
+
522
+ it "copies compressed assets to the revision's asset dir" do
523
+ asset = assets.values.first
524
+ compressed = asset + '.gz'
525
+ FileUtils.cp(manifest.asset_compilation_dir + asset, manifest.asset_compilation_dir + compressed)
526
+ run_step
527
+ assert File.exist?(File.join(revision_root, 'assets', compressed)), "#{compressed} should exist"
528
+ end
529
+
530
+ it "deletes the copied files on rollback" do
531
+ instance = run_step
532
+ instance.rollback
533
+ refute File.exist?(File.join(revision_root, "assets"))
534
+ end
535
+
536
+ it "runs rollback after throwing an exception" do
537
+ instance = mock
538
+ step.expects(:new).returns(instance)
539
+ instance.expects(:call).raises(Exception)
540
+ instance.expects(:rollback)
541
+ lambda{ run_step }.must_raise(Exception)
542
+ end
543
+
544
+ end
545
+
472
546
  describe "GenerateRackupFile" do
473
547
  let(:step) { Spontaneous::Publishing::Steps::GenerateRackupFile }
474
548
  let(:rackup_path) { @site.revision_dir(revision) / "config.ru" }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spontaneous
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0.beta7
4
+ version: 0.2.0.beta8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garry Hill
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-03 00:00:00.000000000 Z
11
+ date: 2014-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -887,6 +887,7 @@ files:
887
887
  - lib/spontaneous/publishing/steps/activate_revision.rb
888
888
  - lib/spontaneous/publishing/steps/archive_old_revisions.rb
889
889
  - lib/spontaneous/publishing/steps/base_step.rb
890
+ - lib/spontaneous/publishing/steps/copy_assets.rb
890
891
  - lib/spontaneous/publishing/steps/copy_static_files.rb
891
892
  - lib/spontaneous/publishing/steps/create_revision_directory.rb
892
893
  - lib/spontaneous/publishing/steps/generate_rackup_file.rb
@@ -1030,6 +1031,9 @@ files:
1030
1031
  - test/fixtures/example_application/Gemfile
1031
1032
  - test/fixtures/example_application/Gemfile.lock
1032
1033
  - test/fixtures/example_application/Rakefile
1034
+ - test/fixtures/example_application/assets/css/site.css.scss
1035
+ - test/fixtures/example_application/assets/i/sine_waves.png
1036
+ - test/fixtures/example_application/assets/i/xes.png
1033
1037
  - test/fixtures/example_application/config/back.rb
1034
1038
  - test/fixtures/example_application/config/back.ru
1035
1039
  - test/fixtures/example_application/config/back.yml
@@ -1369,6 +1373,9 @@ test_files:
1369
1373
  - test/fixtures/example_application/Gemfile
1370
1374
  - test/fixtures/example_application/Gemfile.lock
1371
1375
  - test/fixtures/example_application/Rakefile
1376
+ - test/fixtures/example_application/assets/css/site.css.scss
1377
+ - test/fixtures/example_application/assets/i/sine_waves.png
1378
+ - test/fixtures/example_application/assets/i/xes.png
1372
1379
  - test/fixtures/example_application/config/back.rb
1373
1380
  - test/fixtures/example_application/config/back.ru
1374
1381
  - test/fixtures/example_application/config/back.yml