shutterbug 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|