photograph 0.0.2 → 0.0.4

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ea0a5dcbf3a353549a0009a793780daeeed56e2a
4
+ data.tar.gz: 648222a1f1475cc98c87c7ae1ab2efd67204cc1c
5
+ SHA512:
6
+ metadata.gz: f54b2bbc8a8fb484674a6b646458dfd690da18c670b8f2244473bcdaa292e23420c940f93b752c84e525223c5a69ac2d7790765d6626763067d8f267fc6bf871
7
+ data.tar.gz: 091f1490bd3c72f2a2bd243c2dc2a734ee68fc3f563ab1e8c8c4f96556786cee5e3513c249bf4354b7b59e6b293c6324e302c3b4082df7c3c5104bd2d371fc65
data/README.md CHANGED
@@ -1,20 +1,13 @@
1
1
  # Photograph
2
2
 
3
- Photograph solves the issue of generating previews or thumbnails of
4
- web-based documents. Think of generating a preview of a Google
5
- Spreadsheet. As a such document is dom based, it may be difficult to get a preview
6
- that truly reflects the original content.
3
+ Photograph allows to take screenshots of webpages, the rendering being
4
+ done by Chrome.
7
5
 
8
- Photograph solves that problem by firing a chrome instance thanks to
9
- capybara-webkit and sinatra to provides an easy way to interface it to
10
- your needs.
6
+ Typically, this can be used to generate screenshots for DOM based
7
+ documents.
11
8
 
12
- Obviously, it supports cropping to avoid reworking the image afterward.
13
-
14
- Please remind that having a chrome instance, even if it is being reused
15
- by all requests is still taking some time, aroung 600ms after the first
16
- request on my development machine. *Consider using photograph only if
17
- you expect the same exact rendering of your 'web documents'*.
9
+ Please remind that having a chrome instance doing the rendering, even if it is launched only once,
10
+ is slow, around 600ms.
18
11
 
19
12
  ## Installation
20
13
 
@@ -32,14 +25,19 @@ Or install it yourself as:
32
25
 
33
26
  ## Usage
34
27
 
28
+ **Using `Artist.shoot!` without a block had been deprecated in 0.3 and
29
+ will raise an exception.**
30
+
35
31
  Photograph can be used either directly through the Photograph::Artist
36
- class or by its little sinata app.
32
+ class or by its little sinatra app.
37
33
 
38
- @artist = Photograph::Artist.new("http://github.com")
39
- @artist.shoot!
34
+ @artist = Photograph::Artist.new(:url => "http://github.com")
35
+ @artist.shoot! do |image|
36
+ image # => MiniMagick instance you can toy with
40
37
 
41
- @artist.image
42
- # => MiniMagick instance you can toy with
38
+ send_file image.path,
39
+ :type => :png
40
+ end
43
41
 
44
42
  Or
45
43
 
@@ -53,17 +51,11 @@ Or
53
51
  4. Push to the branch (`git push origin my-new-feature`)
54
52
  5. Create new Pull Request
55
53
 
56
- ## Credits
57
-
58
-
59
- Photograph is maintained and funded by Tactilize.
60
-
61
- Contributors :
54
+ ## Authors
62
55
 
63
56
  * Jean-Hadrien Chabran
64
-
65
- The names and logos for Tactilize are trademarks of Tactilize.
57
+ * Danial Pearce
66
58
 
67
59
  ## License
68
60
 
69
- Photograph is Copyright © 2012 Tactilize. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
61
+ Photograph is Copyright © 2013 JHCHABRAN. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
@@ -1,7 +1,6 @@
1
1
  require 'capybara/poltergeist'
2
2
  require 'mini_magick'
3
3
 
4
-
5
4
  Capybara.default_wait_time = 15
6
5
 
7
6
  module Photograph
@@ -9,45 +8,77 @@ module Photograph
9
8
  attr_accessor :options
10
9
  attr_reader :image
11
10
 
12
- MissingUrlError = Class.new(Exception)
11
+ class MissingUrlError < ArgumentError; end
12
+ class DeprecationError < RuntimeError; end
13
+
13
14
  DefaultOptions = {
14
15
  :x => 0, # top left position
15
16
  :y => 0,
16
- :w => 1280, # bottom right position
17
- :h => 1024,
17
+ :w => 1280, # width
18
+ :h => 1024, # height
18
19
 
19
20
  :wait => 0.5, # if selector is nil, wait 1 seconds before taking the screenshot
20
21
  :selector => nil # wait until the selector matches to take the screenshot
21
22
  }
22
23
 
23
- def self.browser
24
- @browser ||= Capybara::Session.new :poltergeist
24
+ ##
25
+ # Instanciate a browser instance.
26
+ #
27
+ # Typically it's used to supply a Photograph instance your own browser session,
28
+ # allowing you to reuse it if you're using multiples +Photograph::Artist+
29
+ # instances.
30
+ def self.create_browser
31
+ Capybara::Session.new :poltergeist
25
32
  end
26
33
 
34
+ ##
35
+ # Returns current browser instance.
36
+ #
37
+ # If none had been supplied, it creates a new one.
27
38
  def browser
28
- self.class.browser
39
+ @options[:browser] ||= self.class.create_browser
29
40
  end
30
41
 
42
+ ##
43
+ # Normalize urls, allowing you to use "google.com" instead of "http://google.com"
44
+ def normalize_url url
45
+ unless url.match(/https?:\/\//)
46
+ url = "http://#{url}"
47
+ end
48
+
49
+ url
50
+ end
51
+
52
+ ##
53
+ # Creates a new +Photograph::Artist+ and configures it through +options+.
54
+ #
55
+ # Cropping is supported through the +x+, +y+, +w+ and +h+ options.
56
+ #
57
+ # Options:
58
+ # * +url+ mandatory, location you want to screenshot
59
+ # * +wait+ wait amount of seconds before screenshotting. *this is option is ignored if +selector+ is provided.
60
+ # * +selector+ wait until the provided +selector+ matches a dom node before screenshotting. Typically faster than an arbritrary +wait+ amount, used when your page has some dynamically inserted nodes.
61
+ # * +x+ top coordinate of the screenshot, default to 0
62
+ # * +y+ left coordinate of the screenshot, default to 0
63
+ # * +w+ width of the screenshot, default to 1280
64
+ # * +h+ height of the screenshot, default to 1024
65
+ # * +browser+ Capybara instance to use, typically instanciated by +Artist.create_browser+
31
66
  def initialize options={}
32
- raise MissingUrlError unless options[:url]
67
+ raise MissingUrlError.new('missing argument :url') unless options[:url]
33
68
 
34
69
  @options = DefaultOptions.merge(options)
70
+ @options[:url] = normalize_url(options[:url])
35
71
  end
36
72
 
73
+ ##
74
+ # Takes a screenshot and yield the resulting image.
37
75
  def shoot! &block
38
- @image = capture
76
+ raise DeprecationError.new('Using Artist#shoot! without a block had been deprecated') unless block_given?
39
77
 
40
- if block_given?
41
- yield @image
42
- clean!
43
- else
44
- @image
45
- end
46
- end
47
-
48
- def capture
49
78
  browser.visit @options[:url]
50
79
 
80
+ @before_hook.call(browser) if @before_hook
81
+
51
82
  if @options[:selector]
52
83
  browser.wait_until do
53
84
  browser.has_css? @options[:selector]
@@ -56,30 +87,32 @@ module Photograph
56
87
  sleep @options[:wait]
57
88
  end
58
89
 
59
- @tempfile = Tempfile.new(['photograph','.png'])
90
+ tempfile = Tempfile.new(['photograph','.png'])
60
91
 
61
- browser.driver.render @tempfile.path,
92
+ browser.driver.render tempfile.path,
62
93
  :width => options[:w] + options[:x],
63
94
  :height => options[:h] + options[:y]
64
95
 
65
- @image = adjust_image
96
+ yield adjust_image(tempfile)
97
+ ensure
98
+ tempfile.unlink if tempfile
66
99
  end
67
100
 
68
- def adjust_image
69
- image = MiniMagick::Image.read @tempfile
101
+ ##
102
+ # Crops a given +tempfile+ according to initially given +options+
103
+ def adjust_image tempfile
104
+ image = MiniMagick::Image.read tempfile
70
105
 
71
106
  if options[:h] && options[:w]
72
107
  image.crop "#{options[:w]}x#{options[:h]}+#{options[:x]}+#{options[:y]}"
73
-
74
- image.write @tempfile
75
-
108
+ image.write tempfile
76
109
  end
77
110
 
78
111
  image
79
112
  end
80
113
 
81
- def clean!
82
- @tempfile.unlink
114
+ def before &block
115
+ @before_hook = block
83
116
  end
84
117
  end
85
118
  end
@@ -2,12 +2,14 @@ require 'sinatra/base'
2
2
  require 'sinatra/json'
3
3
 
4
4
  module Photograph
5
- # Preload the chrome instance
6
- Artist.browser
7
-
8
5
  class Service < ::Sinatra::Base
9
6
  helpers Sinatra::JSON
10
7
 
8
+ # Reuse the same browser instance between requests.
9
+ def browser
10
+ @browser ||= Artist.create_browser
11
+ end
12
+
11
13
  get '/' do
12
14
  json :version => Photograph::VERSION
13
15
  end
@@ -19,7 +21,8 @@ module Photograph
19
21
  :w => params["w"].to_i,
20
22
  :h => params["h"].to_i,
21
23
  :wait => params["wait"].to_f,
22
- :selector => params["selector"]
24
+ :selector => params["selector"],
25
+ :browser => browser
23
26
 
24
27
  artist.shoot! do |image|
25
28
  send_file image.path,
@@ -1,3 +1,3 @@
1
1
  module Photograph
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.4"
3
3
  end
data/photograph.gemspec CHANGED
@@ -21,6 +21,8 @@ Gem::Specification.new do |gem|
21
21
  gem.add_dependency 'sinatra-contrib'
22
22
  gem.add_dependency 'thin'
23
23
 
24
- gem.add_development_dependency 'rspec', '~> 2.6'
24
+ gem.add_development_dependency 'rspec', '~> 2.14'
25
25
  gem.add_development_dependency 'rake'
26
+
27
+ gem.post_install_message = 'DEPRECATION: Artist#shoot! cannot be used without a block anymore. Please check your code, thank you.'
26
28
  end
@@ -2,47 +2,81 @@ require 'spec_helper'
2
2
 
3
3
  module Photograph
4
4
  describe Artist do
5
- URL = "http://rubygems.org"
5
+ let(:url) { 'http://rubygems.org' }
6
+ subject { Artist.new :url => url }
6
7
 
7
- it "should instanciate a new artist" do
8
- Artist.new :url => URL
9
- end
8
+ describe '.new' do
9
+ it { expect(subject).to be_kind_of(Artist) }
10
+
11
+ it('raises an error when not given a url') do
12
+ expect{ Artist.new }.to raise_error(Artist::MissingUrlError)
13
+ end
14
+
15
+ it('should leave http version of url untouched as it is already valid') do
16
+ expect(subject.options[:url]).to eq(url)
17
+ end
18
+
19
+ it('should leave https version of url untouched as it is already valid') do
20
+ expect(Artist.new(:url => 'https://rubygems.org').options[:url]).to eq('https://rubygems.org')
21
+ end
10
22
 
11
- it "should accept a block when shooting" do
12
- Artist.new(:url => URL).shoot! do |image|
13
- image.should respond_to(:path)
23
+ it('should prepend http:// to the url if protocol is not present') do
24
+ expect(Artist.new(:url => 'github.com').options[:url]).to eq('http://github.com')
14
25
  end
15
26
  end
16
27
 
17
- it "should raise an error without an url" do
18
- expect { Artist.new }.to raise_error(Artist::MissingUrlError)
28
+ describe '#options' do
29
+ it('should have default value for x') { expect(subject.options[:x]).to eq(0) }
30
+ it('should have default value for y') { expect(subject.options[:y]).to eq(0) }
31
+ it('should have default value for w') { expect(subject.options[:w]).to eq(1280) }
32
+ it('should have default value for h') { expect(subject.options[:h]).to eq(1024) }
19
33
  end
20
34
 
21
- describe "Default size values" do
22
- before(:each) { @artist = Artist.new :url => URL }
35
+ describe '#shoot!' do
36
+ context 'when browser has been provided' do
37
+ let(:browser) { Capybara::Session.new(:poltergeist) }
38
+ subject { Artist.new(:url => url, :browser => browser) }
23
39
 
24
- it "should have default values for x,y : 0,0" do
25
- @artist.options[:x].should == 0
26
- @artist.options[:y].should == 0
40
+ it 're-uses the browser provided to Artist#new' do
41
+ browser.should_receive(:visit)
42
+ subject.shoot! {}
43
+ end
27
44
  end
28
45
 
29
- it"should have default values for h,w : 1280, 1024" do
30
- @artist.options[:w].should == 1280
31
- @artist.options[:h].should == 1024
46
+ it('should accept a block when shooting') do
47
+ subject.shoot!{|image| image.should respond_to(:path) }
48
+ end
49
+
50
+ it('should raise an exception if no block was given when shooting') do
51
+ expect{ subject.shoot! }.to raise_error(Artist::DeprecationError)
32
52
  end
33
- end
34
53
 
35
- describe "Cropping" do
36
- before(:each) { @artist = Artist.new :url => URL }
54
+ describe 'Cropping' do
55
+ subject { Artist.new :url => url, :x => 200, :y => 100, :h => 400, :w => 400 }
56
+ before { subject.browser.driver.stub(:render) }
37
57
 
38
- it "should take a screenshot large enough to crop later" do
39
- pending
58
+ xit 'should take a screenshot large enough to crop later' do
59
+ subject.shoot!
60
+ end
61
+ end
62
+ end
40
63
 
41
- @artist = Artist.new :url => URL, :x => 200, :y => 100, :h => 400, :w => 400
64
+ describe "#before" do
65
+ subject { Artist.new :url => url }
66
+ before do
67
+ subject.browser.driver.stub(:visit)
68
+ subject.browser.driver.stub(:click_link)
69
+ subject.browser.driver.stub(:render)
70
+ subject.stub(:adjust_image)
71
+ end
42
72
 
43
- Artist.browser.driver.stub :render
73
+ it('should call the before hook before shooting') do
74
+ subject.before do |browser|
75
+ browser.click_link "Use the API"
76
+ end
44
77
 
45
- @artist.shoot!
78
+ allow(subject.browser).to receive(:click_link)
79
+ subject.shoot! {}
46
80
  end
47
81
  end
48
82
  end
metadata CHANGED
@@ -1,126 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: photograph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
5
- prerelease:
4
+ version: 0.0.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - JH. Chabran
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-10 00:00:00.000000000 Z
11
+ date: 2013-12-28 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: poltergeist
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: mini_magick
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: sinatra
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: sinatra-contrib
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: thin
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :runtime
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rspec
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
87
  - - ~>
100
88
  - !ruby/object:Gem::Version
101
- version: '2.6'
89
+ version: '2.14'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
94
  - - ~>
108
95
  - !ruby/object:Gem::Version
109
- version: '2.6'
96
+ version: '2.14'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: rake
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - '>='
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - '>='
124
109
  - !ruby/object:Gem::Version
125
110
  version: '0'
126
111
  description: Webservice that screenshots any url
@@ -149,33 +134,27 @@ files:
149
134
  - spec/spec_helper.rb
150
135
  homepage: https://github.com/jhchabran/photograph
151
136
  licenses: []
152
- post_install_message:
137
+ metadata: {}
138
+ post_install_message: 'DEPRECATION: Artist#shoot! cannot be used without a block anymore.
139
+ Please check your code, thank you.'
153
140
  rdoc_options: []
154
141
  require_paths:
155
142
  - lib
156
143
  required_ruby_version: !ruby/object:Gem::Requirement
157
- none: false
158
144
  requirements:
159
- - - ! '>='
145
+ - - '>='
160
146
  - !ruby/object:Gem::Version
161
147
  version: '0'
162
- segments:
163
- - 0
164
- hash: -3056213331410753270
165
148
  required_rubygems_version: !ruby/object:Gem::Requirement
166
- none: false
167
149
  requirements:
168
- - - ! '>='
150
+ - - '>='
169
151
  - !ruby/object:Gem::Version
170
152
  version: '0'
171
- segments:
172
- - 0
173
- hash: -3056213331410753270
174
153
  requirements: []
175
154
  rubyforge_project:
176
- rubygems_version: 1.8.23
155
+ rubygems_version: 2.0.3
177
156
  signing_key:
178
- specification_version: 3
157
+ specification_version: 4
179
158
  summary: Webservice that screenshots any url
180
159
  test_files:
181
160
  - spec/photograph/artist_spec.rb