shutterbug 0.5.2 → 0.5.3
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 +8 -8
- data/Guardfile +2 -2
- data/README.md +7 -1
- data/lib/shutterbug/rasterize.js +59 -22
- data/lib/shutterbug/storage/s3_storage.rb +5 -1
- data/lib/shutterbug.rb +1 -1
- data/spec/shutterbug/s3_storage_spec.rb +42 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzA4MzljOTJjNWJlZGIzY2ZhNzlkNDBmOGMwNjRhOGMwZjlhZmU3ZA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NDJhM2E3YTY1NjVmMTc2ZjhmNDZhNzFlNDk0NmY2NzQ4MzJkNGFhMg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTViY2RkMGU2MjE5ZDkzOWFlNmY2ZmFmZmE0ZTU0NGExZjQxYmMwMjA3NjBj
|
10
|
+
ZDE3YjliM2VlOTQ0ODUxYjNmNDljZTRkMzVjYmRlN2RlMmJjNTE3NTIzYjg0
|
11
|
+
YzQ2NGVlMDYzOTUwNDVhMzg0YTY1ZGRiNGViNzNhM2E3NTFmNTc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NjdiZjg0N2RiOWY1YjJjYTJmM2U3ZjJjZTY2YzUzMmVjODIwMzAyOWZiYTFi
|
14
|
+
YzFkY2FiNmZiODMyMDBlZDk1MzBlNDYzNTE4NjViYzA3OTg5N2RmOGY3MGIw
|
15
|
+
NWFlNDdjNmY2YTNkYWIwZmY1YTZiZTlmN2NhNGM4YzEzMWVjYzc=
|
data/Guardfile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# A sample Guardfile
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
3
3
|
|
4
|
-
guard :rspec do
|
4
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
5
5
|
watch(%r{^spec/.+_spec\.rb$})
|
6
6
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
7
|
watch('spec/spec_helper.rb') { "spec" }
|
@@ -11,4 +11,4 @@ guard 'rack', :port => 9292 do
|
|
11
11
|
watch('Gemfile.lock')
|
12
12
|
watch('config.ru')
|
13
13
|
watch(%r{^(js|lib)/.*})
|
14
|
-
end
|
14
|
+
end
|
data/README.md
CHANGED
@@ -91,8 +91,14 @@ And a Procfile which looks like this:
|
|
91
91
|
|
92
92
|
## Changes ##
|
93
93
|
|
94
|
+
* June 4, 2105 – v 0.5.3
|
95
|
+
* `Rasterize.js`: Also loads non-base64 encoded iframe src content when walking
|
96
|
+
the iFrames. Also, better mechanics for waiting for page rendering.
|
97
|
+
* `s3_storage.rb`: Look for existing S3 bucket using `get`, to avoid 404 errors
|
98
|
+
when bucket limit of 100 is hit. (github issue #15)
|
99
|
+
|
94
100
|
* May 28, 2015 – v 0.5.2
|
95
|
-
* Rasterize.js
|
101
|
+
* `Rasterize.js`: "Loads an html page and does a depth-first walk of its
|
96
102
|
iframes. As the walk returns to the root each iframe's src is set to the
|
97
103
|
base64 png rendering of its contents." The purpose of this change was to
|
98
104
|
allow SVG document relative resource links (such as gradients) to work,
|
data/lib/shutterbug/rasterize.js
CHANGED
@@ -29,16 +29,16 @@
|
|
29
29
|
processIFrames(iframePage, cb6)
|
30
30
|
getIFrameSrc()
|
31
31
|
drainIFrameQueue() <- inner frame queue
|
32
|
-
cb6(
|
33
|
-
cb4(iframePage, iframeBase,
|
32
|
+
cb6(hasChangedIframes=false) <- no iframes found in inner iframe html so callback called immediately
|
33
|
+
cb4(iframePage, iframeBase, hasChangedIframes)
|
34
34
|
|
35
35
|
// replace page iframe src with rendered image
|
36
36
|
createPage(iframeHTML, iframeBase, cb7) <-- loaded in separate page so the updated iframe src is rendered correctly
|
37
37
|
cb7(finalIFrame)
|
38
38
|
updateIframeAndContinue(finalIFrame)
|
39
39
|
drainIFrameQueue() <- outer page queue
|
40
|
-
cb3(
|
41
|
-
cb1(page, base,
|
40
|
+
cb3(hasChangedIframes=true)
|
41
|
+
cb1(page, base, hasChangedIframes)
|
42
42
|
|
43
43
|
// render page
|
44
44
|
createPage(pageHtml, pageBase, cb8)
|
@@ -74,10 +74,10 @@ renderPage();
|
|
74
74
|
// Main function, called once
|
75
75
|
function renderPage() {
|
76
76
|
// this is the initial load of the html provided by phantom_job.rb
|
77
|
-
loadPage(fs.read(filename), function (page, base,
|
77
|
+
loadPage(fs.read(filename), function (page, base, hasChangedIframes) {
|
78
78
|
// At this point the page has been "walked", and if it includes iframes, loadPage() has been called on each iframe.
|
79
79
|
// The iframe srces have been updated to a data-uri that has an image taken of the iframe contents.
|
80
|
-
if (
|
80
|
+
if (hasChangedIframes) {
|
81
81
|
// phantom does not seem to want to render changed iframe content when the src parameter is modified
|
82
82
|
// so we need to reload it in another page
|
83
83
|
createPage(page.content, base, function (finalPage) {
|
@@ -103,25 +103,55 @@ function loadPage(html, callback) {
|
|
103
103
|
baselessHtml = html.replace(baseRegEx, '');
|
104
104
|
|
105
105
|
createPage(baselessHtml, base, function (page) {
|
106
|
-
processIframes(page, function (
|
107
|
-
callback(page, base,
|
106
|
+
processIframes(page, function (hasChangedIframes) {
|
107
|
+
callback(page, base, hasChangedIframes);
|
108
108
|
});
|
109
109
|
});
|
110
110
|
}
|
111
111
|
|
112
112
|
// Creates the phantom page object using the passed in html and base and returns it in a callback
|
113
113
|
function createPage(html, base, callback) {
|
114
|
-
var page = require('webpage').create()
|
114
|
+
var page = require('webpage').create(),
|
115
|
+
resourceCount = 0;
|
116
|
+
|
117
|
+
// onLoadFinished is signaled before all the contained iframe documents are loaded
|
118
|
+
// so we need to keep a count of the resources requested to make sure they load before we continue on
|
119
|
+
page.onResourceRequested = function() {
|
120
|
+
resourceCount++;
|
121
|
+
};
|
122
|
+
page.onResourceReceived = function(response) {
|
123
|
+
// phantom sends this for each chunk (4k in testing, the end is signaled with the stage variable)
|
124
|
+
if (response.stage == "end") {
|
125
|
+
resourceCount--;
|
126
|
+
}
|
127
|
+
};
|
128
|
+
page.onResourceError = function() {
|
129
|
+
resourceCount--;
|
130
|
+
};
|
115
131
|
|
116
132
|
page.onLoadFinished = function(status) {
|
133
|
+
var loadTime = (new Date()).getTime();
|
134
|
+
|
117
135
|
if (status !== 'success') {
|
118
136
|
console.log('Unable to load html!');
|
119
137
|
phantom.exit();
|
120
138
|
}
|
121
139
|
|
140
|
+
// wait until the next tick for any contained iframes to finish making their requests
|
122
141
|
window.setTimeout(function () {
|
123
|
-
|
124
|
-
|
142
|
+
// then wait for resourceCount to be zero or the timeout to hit
|
143
|
+
waitForAllResourcesToLoadOrTimeout();
|
144
|
+
}, 0);
|
145
|
+
|
146
|
+
function waitForAllResourcesToLoadOrTimeout() {
|
147
|
+
var now = (new Date()).getTime();
|
148
|
+
if ((resourceCount === 0) || (now - loadTime > 10000)) {
|
149
|
+
callback(page);
|
150
|
+
}
|
151
|
+
else {
|
152
|
+
window.setTimeout(waitForAllResourcesToLoadOrTimeout, 0)
|
153
|
+
}
|
154
|
+
}
|
125
155
|
};
|
126
156
|
|
127
157
|
page.viewportSize = viewportSize;
|
@@ -133,8 +163,9 @@ function createPage(html, base, callback) {
|
|
133
163
|
// with its src being the base64 encoded png render of the iframe contents
|
134
164
|
function processIframes(page, callback) {
|
135
165
|
|
136
|
-
var iframeSrc =
|
166
|
+
var iframeSrc = getIframeDataUriSrc(page),
|
137
167
|
iframePng = [],
|
168
|
+
changedFrameCount = 0,
|
138
169
|
queueIndex = 0;
|
139
170
|
|
140
171
|
// Since this is async we can't loop but instead recursivly call drainIFrameQueue()
|
@@ -148,14 +179,24 @@ function processIframes(page, callback) {
|
|
148
179
|
function drainIFrameQueue() {
|
149
180
|
if (queueIndex < iframeSrc.length) {
|
150
181
|
|
182
|
+
// getIframeSrc returns null for frames that don't use data-uris in the src
|
183
|
+
// it doesn't just skip them so that we can maintain the queueIndex
|
184
|
+
if (iframeSrc[queueIndex] === null) {
|
185
|
+
queueIndex++;
|
186
|
+
drainIFrameQueue();
|
187
|
+
return;
|
188
|
+
}
|
189
|
+
|
190
|
+
changedFrameCount++;
|
191
|
+
|
151
192
|
// This is the recursion down the iframes. It restarts the entire process
|
152
193
|
// of loading so we can handle an infinite number of embedded iframes.
|
153
194
|
// The recursion stops when it completes a depth first search of all the iframes
|
154
195
|
// starting at the root document.
|
155
196
|
|
156
|
-
loadPage(iframeSrc[queueIndex], function (iFrame, base,
|
197
|
+
loadPage(iframeSrc[queueIndex], function (iFrame, base, hasChangedIframes) {
|
157
198
|
// convert the iframe contents to a png and then set the src to it
|
158
|
-
if (
|
199
|
+
if (hasChangedIframes) {
|
159
200
|
// reload the iframe so the src is used
|
160
201
|
createPage(iFrame.content, base, function (finalIFrame) {
|
161
202
|
updateIframeAndContinue(finalIFrame);
|
@@ -177,7 +218,7 @@ function processIframes(page, callback) {
|
|
177
218
|
});
|
178
219
|
}
|
179
220
|
else {
|
180
|
-
callback(
|
221
|
+
callback(changedFrameCount > 0);
|
181
222
|
}
|
182
223
|
}
|
183
224
|
}
|
@@ -190,7 +231,7 @@ function renderIframeToImage(iFrame) {
|
|
190
231
|
// Walks all the iframes in the current page and pushes their src onto an array that is returned.
|
191
232
|
// Phantom.js only allows simple types to cross the evaluate() boundary so the array is marshalled
|
192
233
|
// as a JSON string.
|
193
|
-
function
|
234
|
+
function getIframeDataUriSrc(page) {
|
194
235
|
return JSON.parse(page.evaluate(function () {
|
195
236
|
var iframes = document.getElementsByTagName('iframe'),
|
196
237
|
src = [],
|
@@ -203,12 +244,8 @@ function getIframeSrc(page) {
|
|
203
244
|
src.push(iframe.src.substr(mimeType.length));
|
204
245
|
}
|
205
246
|
else {
|
206
|
-
|
207
|
-
|
208
|
-
}
|
209
|
-
catch (e) {
|
210
|
-
src.push('');
|
211
|
-
}
|
247
|
+
// null is used to signify that the iframe doesn't use data uris
|
248
|
+
src.push(null);
|
212
249
|
}
|
213
250
|
}
|
214
251
|
// only simple types can be returned...
|
@@ -26,8 +26,12 @@ module Shutterbug
|
|
26
26
|
:public => true)
|
27
27
|
end
|
28
28
|
|
29
|
+
def self.lookup_bin
|
30
|
+
self.connection.directories.get(Configuration.instance.s3_bin) || self.create_bin
|
31
|
+
end
|
32
|
+
|
29
33
|
def self.s3_bin
|
30
|
-
@s3_bin ||= self.
|
34
|
+
@s3_bin ||= self.lookup_bin
|
31
35
|
end
|
32
36
|
|
33
37
|
def self.write(filename)
|
data/lib/shutterbug.rb
CHANGED
@@ -2,11 +2,51 @@ require 'shared_examples_for_storage'
|
|
2
2
|
require 'fog'
|
3
3
|
|
4
4
|
describe Shutterbug::Storage::S3Storage do
|
5
|
-
let(:
|
5
|
+
let(:s3_bin) { nil }
|
6
|
+
let(:storage) { Shutterbug::Storage::S3Storage}
|
7
|
+
let(:mock_write_result) { double(:public_url => "http://amazon.cloud/url.png")}
|
6
8
|
before(:each) do
|
7
|
-
|
9
|
+
storage.stub(:write => mock_write_result)
|
8
10
|
end
|
9
11
|
|
10
12
|
it_behaves_like "a storage provider" do
|
11
13
|
end
|
14
|
+
|
15
|
+
describe "some class methods" do
|
16
|
+
before(:each) do
|
17
|
+
Fog.mock!
|
18
|
+
end
|
19
|
+
describe "s3_bin" do
|
20
|
+
subject do
|
21
|
+
storage
|
22
|
+
end
|
23
|
+
describe "when the bin is already configured" do
|
24
|
+
let(:fake_bin) { "xyzzy" }
|
25
|
+
it "should not call #lookup_bin or #create_bin" do
|
26
|
+
subject.instance_variable_set(:@s3_bin, fake_bin)
|
27
|
+
subject.should_not_receive(:lookup_bin)
|
28
|
+
subject.should_not_receive(:create_bin)
|
29
|
+
subject.s3_bin.should eq fake_bin
|
30
|
+
# reset
|
31
|
+
subject.instance_variable_set(:@s3_bin,nil)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
describe "when the bin can't be found" do
|
35
|
+
let(:fake_bin) { "xyzzy" }
|
36
|
+
it "should call #create_bin" do
|
37
|
+
subject.stub_chain(:connection,:directories,:get).and_return(nil)
|
38
|
+
subject.should_receive(:create_bin).and_return(fake_bin)
|
39
|
+
subject.s3_bin.should eq fake_bin
|
40
|
+
end
|
41
|
+
end
|
42
|
+
describe "when the bin exists" do
|
43
|
+
let(:fake_bin) { "xyzzy" }
|
44
|
+
it "should not call #create_bin" do
|
45
|
+
subject.stub_chain(:connection,:directories,:get).and_return(fake_bin)
|
46
|
+
subject.s3_bin.should eq fake_bin
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
12
51
|
end
|
52
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shutterbug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Noah Paessel
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-05
|
13
|
+
date: 2015-06-05 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|