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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YWM3Y2Q1MDlmMzI3MjVkYjkzNTYzYzEyMTUwNzdhNmFjZWVhMzM1OQ==
4
+ YzA4MzljOTJjNWJlZGIzY2ZhNzlkNDBmOGMwNjRhOGMwZjlhZmU3ZA==
5
5
  data.tar.gz: !binary |-
6
- NTZhNGExZTc0OTNhOTFjMzE3MGE0N2NlMTQ1ZjQyZTVhNmRlYzkwNw==
6
+ NDJhM2E3YTY1NjVmMTc2ZjhmNDZhNzFlNDk0NmY2NzQ4MzJkNGFhMg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ODM2Nzc3MGVmZmQwOTVkZWZmNzg4NTllNjMzY2JiNjlhZTg5MzNhODNkNGJh
10
- NTg2NmU4MGM3OGUwZDIyMTc3ZjQ5OWUxZGJlNmQ0ZjY0ZDBjYTRhOTEwZTZl
11
- NGFjYzM2Y2M4N2IzODBmZTIzZjA4Y2Y4ZDg4YzNjMzk0YTY0Y2Q=
9
+ OTViY2RkMGU2MjE5ZDkzOWFlNmY2ZmFmZmE0ZTU0NGExZjQxYmMwMjA3NjBj
10
+ ZDE3YjliM2VlOTQ0ODUxYjNmNDljZTRkMzVjYmRlN2RlMmJjNTE3NTIzYjg0
11
+ YzQ2NGVlMDYzOTUwNDVhMzg0YTY1ZGRiNGViNzNhM2E3NTFmNTc=
12
12
  data.tar.gz: !binary |-
13
- NWVhNDM2YzlkOWExNzJiMjIwNDgxZGZhZjZmYWY2ZTNkZWU4Y2U5NjE4M2Fh
14
- NDliYTI4MDc3ZTdiMzUxNDY5MTZjMTViYzc4OWVmZTU3ZmIxYjZlMWFkOWM2
15
- OTgyYmFmZmVkODA4NTcwMGM3MTg1ZjI1MzE0ZGQzY2RmNTIzNGY=
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: "Loads an html page and does a depth-first walk of its
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,
@@ -29,16 +29,16 @@
29
29
  processIFrames(iframePage, cb6)
30
30
  getIFrameSrc()
31
31
  drainIFrameQueue() <- inner frame queue
32
- cb6(hasIframes=false) <- no iframes found in inner iframe html so callback called immediately
33
- cb4(iframePage, iframeBase, hasIframes)
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(hasIframes=true)
41
- cb1(page, base, hasIframes)
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, hasIframes) {
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 (hasIframes) {
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 (hasIframes) {
107
- callback(page, base, hasIframes);
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
- callback(page);
124
- }, 200);
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 = getIframeSrc(page),
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, hasIFrames) {
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 (hasIFrames) {
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(iframeSrc.length > 0);
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 getIframeSrc(page) {
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
- try {
207
- src.push(serializer.serializeToString(iframe.contentDocument));
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.create_bin
34
+ @s3_bin ||= self.lookup_bin
31
35
  end
32
36
 
33
37
  def self.write(filename)
data/lib/shutterbug.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Shutterbug
2
- VERSION = "0.5.2"
2
+ VERSION = "0.5.3"
3
3
  autoload :Rackapp, "shutterbug/rackapp"
4
4
  autoload :Configuration, "shutterbug/configuration"
5
5
  autoload :Storage, "shutterbug/storage"
@@ -2,11 +2,51 @@ require 'shared_examples_for_storage'
2
2
  require 'fog'
3
3
 
4
4
  describe Shutterbug::Storage::S3Storage do
5
- let(:mock_write_result) { mock(:public_url => "http://amazon.cloud/url.png")}
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
- Shutterbug::Storage::S3Storage.stub!(:write => mock_write_result)
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.2
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-28 00:00:00.000000000 Z
13
+ date: 2015-06-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler