grope 0.0.4 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -2,20 +2,31 @@
2
2
 
3
3
  A non-GUI library to represent browser environment using WebKit Framework + RubyCocoa
4
4
 
5
- == Sypnosis
5
+ == Synopsis
6
6
 
7
7
  require 'grope'
8
8
 
9
9
  env = Grope::Env.new
10
10
 
11
+ === Load URL
11
12
  env.load('http://example.com')
12
13
  env.document.title //=> "Example Web Page"
14
+
15
+ === Search by XPath
13
16
  env.find('//a').href //=> "http://www.rfc-editor.org/rfc/rfc2606.txt"
14
17
  env.find('//a').offsetWidth //=> 58
15
18
 
19
+ === Execute javascript
16
20
  env.load('http://nonn-et-twk.net/twk/nondrupal/flip/flip.html')
17
21
  env.window.flipString('test') //=> "ʇsǝʇ"
18
22
 
23
+ === Capture screenshot
24
+ # capture whole page and save to 'capture.png'
25
+ @env.capture
26
+
27
+ # capture specified element
28
+ @env.capture(@env.find('id("header")'), 'capture_header.png')
29
+
19
30
  == Requirements
20
31
 
21
32
  * RubyCocoa
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.1.1
data/bin/grope CHANGED
@@ -19,7 +19,7 @@ opts = OptionParser.new do |opts|
19
19
  use_shared_cookie = true
20
20
  end
21
21
 
22
- opts.on_tail("-?", "--help", "Show this message") do
22
+ opts.on_tail("-h", "--help", "Show this message") do
23
23
  puts opts
24
24
  exit
25
25
  end
@@ -3,15 +3,11 @@
3
3
  require 'rubygems'
4
4
  require 'grope'
5
5
 
6
- if ARGV.size != 1
7
- warn "usage: #{$0} /path/to/js/to/minify.js"
8
- exit 1
9
- end
10
-
11
6
  js_filename = ARGV.shift
7
+ js = (js_filename ? open(js_filename) : STDIN).read
12
8
 
13
9
  env = Grope::Env.new
14
10
  env.load('http://fmarcia.info/jsmin/test.html')
15
- env.all('//textarea')[1].value = open(js_filename).read
11
+ env.all('//textarea')[1].value = js
16
12
  env.document.getElementById('go').click
17
13
  puts env.all('//textarea')[2].value
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'grope'
5
+
6
+ url, filename, xpath = ARGV
7
+
8
+ env = Grope::Env.new
9
+ puts "Fetching %s ..." % url
10
+ env.load(url)
11
+ env.wait
12
+ env.capture(xpath ? env.find(xpath) : nil, filename)
13
+ puts " ... done"
data/grope.gemspec CHANGED
@@ -1,69 +1,68 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{grope}
8
- s.version = "0.0.4"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["youpy"]
12
- s.date = %q{2010-07-05}
12
+ s.date = %q{2011-02-20}
13
13
  s.default_executable = %q{grope}
14
14
  s.description = %q{A non-GUI library to represent browser environment using WebKit Framework}
15
15
  s.email = %q{youpy@buycheapviagraonlinenow.com}
16
16
  s.executables = ["grope"]
17
17
  s.extra_rdoc_files = [
18
18
  "LICENSE",
19
- "README.rdoc"
19
+ "README.rdoc"
20
20
  ]
21
21
  s.files = [
22
22
  ".document",
23
- ".gitignore",
24
- "LICENSE",
25
- "README.rdoc",
26
- "Rakefile",
27
- "VERSION",
28
- "bin/grope",
29
- "examples/js_minifier.rb",
30
- "examples/js_minifier_sinatra.rb",
31
- "examples/map_generator.rb",
32
- "examples/md5.rb",
33
- "examples/patterns.rb",
34
- "grope.gemspec",
35
- "lib/grope.rb",
36
- "lib/grope/dom_ext.rb",
37
- "lib/grope/env.rb",
38
- "lib/grope/frame_load_delegate.rb",
39
- "lib/grope/web_resource_load_delegate.rb",
40
- "lib/grope/wso_wrapper.rb",
41
- "spec/env_spec.rb",
42
- "spec/spec.opts",
43
- "spec/spec_helper.rb",
44
- "spec/wso_wrapper.rb"
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/grope",
28
+ "examples/js_minifier.rb",
29
+ "examples/js_minifier_sinatra.rb",
30
+ "examples/map_generator.rb",
31
+ "examples/md5.rb",
32
+ "examples/patterns.rb",
33
+ "examples/url2png.rb",
34
+ "grope.gemspec",
35
+ "lib/grope.rb",
36
+ "lib/grope/dom_ext.rb",
37
+ "lib/grope/env.rb",
38
+ "lib/grope/frame_load_delegate.rb",
39
+ "lib/grope/web_resource_load_delegate.rb",
40
+ "lib/grope/wso_wrapper.rb",
41
+ "spec/env_spec.rb",
42
+ "spec/spec.opts",
43
+ "spec/spec_helper.rb",
44
+ "spec/wso_wrapper_spec.rb"
45
45
  ]
46
46
  s.homepage = %q{http://github.com/youpy/grope}
47
- s.rdoc_options = ["--charset=UTF-8"]
48
47
  s.require_paths = ["lib"]
49
- s.rubygems_version = %q{1.3.6}
48
+ s.rubygems_version = %q{1.5.0}
50
49
  s.summary = %q{A non-GUI library to represent browser environment}
51
50
  s.test_files = [
51
+ "examples/js_minifier.rb",
52
+ "examples/js_minifier_sinatra.rb",
53
+ "examples/map_generator.rb",
54
+ "examples/md5.rb",
55
+ "examples/patterns.rb",
56
+ "examples/url2png.rb",
52
57
  "spec/env_spec.rb",
53
- "spec/spec_helper.rb",
54
- "spec/wso_wrapper.rb",
55
- "examples/js_minifier.rb",
56
- "examples/js_minifier_sinatra.rb",
57
- "examples/map_generator.rb",
58
- "examples/md5.rb",
59
- "examples/patterns.rb"
58
+ "spec/spec_helper.rb",
59
+ "spec/wso_wrapper_spec.rb"
60
60
  ]
61
61
 
62
62
  if s.respond_to? :specification_version then
63
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
64
63
  s.specification_version = 3
65
64
 
66
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
65
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
67
66
  s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
68
67
  s.add_runtime_dependency(%q<mechanize>, [">= 1.0.0"])
69
68
  else
data/lib/grope/env.rb CHANGED
@@ -6,11 +6,21 @@ module Grope
6
6
  @options = {
7
7
  :timeout => 60,
8
8
  :use_shared_cookie => false,
9
+ :init_width => 1024,
10
+ :init_height => 600
9
11
  }.merge(options)
10
12
 
13
+ NSApplication.sharedApplication
11
14
  @webview = WebView.alloc
12
- @webview.initWithFrame(NSMakeRect(0,0,100,100))
15
+ @webview.initWithFrame(rect)
13
16
  @webview.setPreferencesIdentifier('Grope')
17
+ @webview.preferences.setShouldPrintBackgrounds(true)
18
+ @webview.preferences.setAllowsAnimatedImages(false)
19
+ @webview.mainFrame.frameView.setAllowsScrolling(false)
20
+ @webview.setMediaStyle('screen')
21
+
22
+ create_window
23
+
14
24
  @frame_load_delegate = FrameLoadDelegate.alloc.init
15
25
  @webview.setFrameLoadDelegate(@frame_load_delegate)
16
26
 
@@ -80,6 +90,15 @@ module Grope
80
90
  default: throw(TypeError("$X: specified type is not valid type."));
81
91
  }
82
92
  },
93
+ getElementPosition: function(elem) {
94
+ var position = elem.getBoundingClientRect();
95
+ return {
96
+ left: Math.round(window.scrollX+position.left),
97
+ top: Math.round(window.scrollY+position.top),
98
+ width: elem.clientWidth,
99
+ height: elem.clientHeight
100
+ }
101
+ },
83
102
 
84
103
  _dispatchMouseEvent: function(e, type) {
85
104
  var evt = document.createEvent('MouseEvents');
@@ -110,7 +129,6 @@ JS
110
129
 
111
130
  def all(xpath, node = nil)
112
131
  node ||= document
113
- js = eval('return Grope')
114
132
  js.xpath(xpath, node)
115
133
  end
116
134
 
@@ -118,6 +136,37 @@ JS
118
136
  all(xpath, node)[0]
119
137
  end
120
138
 
139
+ def capture(elem = nil, filename = "capture.png")
140
+ view = @webview.mainFrame.frameView.documentView
141
+ bounds = view.bounds
142
+
143
+ if elem
144
+ position = js.getElementPosition(elem)
145
+
146
+ raise "element's width is 0" if position.width.zero?
147
+ raise "element's height is 0" if position.height.zero?
148
+
149
+ bounds.origin.x = position.left
150
+ bounds.origin.y = position.top
151
+ bounds.size.width = position.width
152
+ bounds.size.height = position.height
153
+ end
154
+
155
+ wait
156
+
157
+ view.display
158
+ view.window.setContentSize(NSUnionRect(view.bounds, bounds).size)
159
+ view.setFrame(NSUnionRect(view.bounds, bounds))
160
+
161
+ view.lockFocus
162
+ bitmapdata = NSBitmapImageRep.alloc
163
+ bitmapdata.initWithFocusedViewRect(bounds)
164
+ view.unlockFocus
165
+
166
+ bitmapdata.representationUsingType_properties(NSPNGFileType, nil).
167
+ writeToFile_atomically(filename.to_s, 1)
168
+ end
169
+
121
170
  private
122
171
 
123
172
  def run(wait_sec = 0)
@@ -127,7 +176,7 @@ JS
127
176
 
128
177
  run_loop = NSRunLoop.currentRunLoop
129
178
  run_loop.runMode_beforeDate(NSDefaultRunLoopMode, Time.now)
130
- while(@frame_load_delegate.should_keep_running &&
179
+ while(@webview.isLoading? && @frame_load_delegate.should_keep_running &&
131
180
  run_loop.runMode_beforeDate(NSDefaultRunLoopMode, Time.now + 0.1)); end
132
181
  run_loop.runUntilDate(Time.now + wait_sec)
133
182
 
@@ -135,5 +184,20 @@ JS
135
184
  ensure
136
185
  NSObject.cancelPreviousPerformRequestsWithTarget_selector_object(@frame_load_delegate, 'timeout:', @webview)
137
186
  end
187
+
188
+ def js
189
+ eval('return Grope')
190
+ end
191
+
192
+ def create_window
193
+ unless @window
194
+ @window = NSWindow.alloc.initWithContentRect_styleMask_backing_defer_(rect, NSBorderlessWindowMask, 2, false)
195
+ @window.setContentView(@webview)
196
+ end
197
+ end
198
+
199
+ def rect
200
+ NSMakeRect(0,0,@options[:init_width],@options[:init_height])
201
+ end
138
202
  end
139
203
  end
@@ -16,7 +16,9 @@ module Grope
16
16
  end
17
17
 
18
18
  def webView_didFinishLoadForFrame(webview, frame)
19
- terminate
19
+ if frame == webview.mainFrame
20
+ terminate
21
+ end
20
22
  end
21
23
 
22
24
  def terminate
@@ -18,9 +18,9 @@ module Grope
18
18
  when Integer
19
19
  value
20
20
  when OSX::NSCFBoolean
21
- value
21
+ value.boolValue
22
22
  when OSX::NSCFNumber
23
- value.to_i
23
+ value.integer? ? value.to_i : value.to_f
24
24
  when OSX::NSCFString
25
25
  value.to_s
26
26
  else
data/spec/env_spec.rb CHANGED
@@ -1,33 +1,46 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
+ require 'pathname'
4
+ require 'shellwords'
5
+
3
6
  describe Grope::Env do
4
7
  before do
5
8
  @env = Grope::Env.new
6
- @env.load('http://example.com')
9
+ @env.load('http://httpstat.us/')
10
+ sleep 1
7
11
  end
8
12
 
9
13
  it "should initialize" do
10
- @env.document.title.should eql('Example Web Page')
14
+ @env.document.title.should eql('httpstat.us')
11
15
  end
12
16
 
13
17
  it "should get elements by XPath" do
14
18
  body = @env.find('//body')
15
- @env.find('//a', body).href.should eql('http://www.rfc-editor.org/rfc/rfc2606.txt')
16
- @env.all('//a', body)[0].href.should eql('http://www.rfc-editor.org/rfc/rfc2606.txt')
19
+ @env.find('//a', body).href.should eql('http://httpstat.us/200')
20
+ @env.all('//a', body)[0].href.should eql('http://httpstat.us/200')
17
21
  end
18
22
 
19
23
  it "should get links" do
20
24
  result = @env.document.links
21
- result[0].href.should eql('http://www.rfc-editor.org/rfc/rfc2606.txt')
25
+ result[0].href.should eql('http://httpstat.us/200')
22
26
  end
23
27
 
24
28
  it "should eval" do
25
- @env.window.location.href.should eql('http://example.com/')
29
+ @env.window.location.href.should eql('http://httpstat.us/')
30
+ end
31
+
32
+ it "should redirect by javascript" do
33
+ @env.eval('location.href="http://httpstat.us/200"')
34
+ @env.wait
35
+
36
+ @env.document.URL.should eql('http://httpstat.us/200')
26
37
  end
27
38
 
28
- it "should redirect" do
29
- @env.eval('location.href="http://example.org"')
30
- @env.document.location.href.should eql('http://example.org/')
39
+ it "should redirect by status code" do
40
+ @env.load('http://httpstat.us/302')
41
+ @env.wait
42
+
43
+ @env.document.URL.should eql('http://httpstat.us/')
31
44
  end
32
45
 
33
46
  it "should redirect by click" do
@@ -38,13 +51,13 @@ describe Grope::Env do
38
51
  # TODO: wait automatically after js function is called
39
52
  @env.wait
40
53
 
41
- @env.document.URL.should eql('http://www.rfc-editor.org/rfc/rfc2606.txt')
54
+ @env.document.URL.should eql('http://httpstat.us/200')
42
55
  end
43
56
 
44
57
  it "should get/call function" do
45
58
  function = @env.eval('return function(x, y) { return x * y }')
46
- function.call(false, 3, 5).should eql(15)
47
- function.apply(false, [3, 5]).should eql(15)
59
+ function.call(false, 3, 5).should eql(15.0)
60
+ function.apply(false, [3, 1.5]).should eql(4.5)
48
61
  end
49
62
 
50
63
  it "should call function in object" do
@@ -70,4 +83,54 @@ describe Grope::Env do
70
83
  @env.load('http://google.com/')
71
84
  @env.instance_eval { @resource_load_delegate }.cookie_storage.cookies(URI('http://google.com/')).should_not be_nil
72
85
  end
86
+
87
+ describe "#capture" do
88
+ before do
89
+ dir = Pathname(Dir.tmpdir)
90
+ @filename = dir + 'test.png'
91
+ end
92
+
93
+ after do
94
+ if @filename.file?
95
+ @filename.unlink
96
+ end
97
+ end
98
+
99
+ it "should capture specified element and write to file as png" do
100
+
101
+ element = @env.find('//p')
102
+
103
+ @env.capture(element, @filename)
104
+
105
+ info = `file #{Shellwords.shellescape(@filename.to_s)}`
106
+
107
+ info.should match(/PNG image/)
108
+
109
+ width, height = info.scan(/(\d+) x (\d+)/)[0]
110
+
111
+ width.to_i.should eql(element.clientWidth)
112
+ height.to_i.should eql(element.clientHeight)
113
+ end
114
+
115
+ it "should capture whole content" do
116
+ @env.load('http://httpstat.us/200')
117
+ @env.capture(nil, @filename)
118
+
119
+ info = `file #{Shellwords.shellescape(@filename.to_s)}`
120
+
121
+ info.should match(/PNG image/)
122
+
123
+ width, height = info.scan(/(\d+) x (\d+)/)[0]
124
+
125
+ width.to_i.should eql(1024)
126
+ height.to_i.should eql(600)
127
+ end
128
+
129
+ it "should raise error if width(or height) of specified element is zero" do
130
+ lambda {
131
+ element = @env.find('//a')
132
+ @env.capture(element, @filename)
133
+ }.should raise_error(RuntimeError)
134
+ end
135
+ end
73
136
  end
@@ -0,0 +1,11 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Grope::WSOWrapper do
4
+ it "should wrap" do
5
+ Grope::WSOWrapper.wrap(nil).should eql(nil)
6
+ Grope::WSOWrapper.wrap(true.to_ns).should eql(true)
7
+ Grope::WSOWrapper.wrap(false.to_ns).should eql(false)
8
+ Grope::WSOWrapper.wrap(1.to_ns).should eql(1)
9
+ Grope::WSOWrapper.wrap("a".to_ns).should eql("a")
10
+ end
11
+ end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grope
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 25
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
- - 0
8
- - 4
9
- version: 0.0.4
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - youpy
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-07-05 00:00:00 +09:00
18
+ date: 2011-02-20 00:00:00 +09:00
18
19
  default_executable: grope
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: rspec
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 13
27
30
  segments:
28
31
  - 1
29
32
  - 2
@@ -35,9 +38,11 @@ dependencies:
35
38
  name: mechanize
36
39
  prerelease: false
37
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
38
42
  requirements:
39
43
  - - ">="
40
44
  - !ruby/object:Gem::Version
45
+ hash: 23
41
46
  segments:
42
47
  - 1
43
48
  - 0
@@ -56,7 +61,6 @@ extra_rdoc_files:
56
61
  - README.rdoc
57
62
  files:
58
63
  - .document
59
- - .gitignore
60
64
  - LICENSE
61
65
  - README.rdoc
62
66
  - Rakefile
@@ -67,6 +71,7 @@ files:
67
71
  - examples/map_generator.rb
68
72
  - examples/md5.rb
69
73
  - examples/patterns.rb
74
+ - examples/url2png.rb
70
75
  - grope.gemspec
71
76
  - lib/grope.rb
72
77
  - lib/grope/dom_ext.rb
@@ -77,43 +82,48 @@ files:
77
82
  - spec/env_spec.rb
78
83
  - spec/spec.opts
79
84
  - spec/spec_helper.rb
80
- - spec/wso_wrapper.rb
85
+ - spec/wso_wrapper_spec.rb
81
86
  has_rdoc: true
82
87
  homepage: http://github.com/youpy/grope
83
88
  licenses: []
84
89
 
85
90
  post_install_message:
86
- rdoc_options:
87
- - --charset=UTF-8
91
+ rdoc_options: []
92
+
88
93
  require_paths:
89
94
  - lib
90
95
  required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
91
97
  requirements:
92
98
  - - ">="
93
99
  - !ruby/object:Gem::Version
100
+ hash: 3
94
101
  segments:
95
102
  - 0
96
103
  version: "0"
97
104
  required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
98
106
  requirements:
99
107
  - - ">="
100
108
  - !ruby/object:Gem::Version
109
+ hash: 3
101
110
  segments:
102
111
  - 0
103
112
  version: "0"
104
113
  requirements: []
105
114
 
106
115
  rubyforge_project:
107
- rubygems_version: 1.3.6
116
+ rubygems_version: 1.5.0
108
117
  signing_key:
109
118
  specification_version: 3
110
119
  summary: A non-GUI library to represent browser environment
111
120
  test_files:
112
- - spec/env_spec.rb
113
- - spec/spec_helper.rb
114
- - spec/wso_wrapper.rb
115
121
  - examples/js_minifier.rb
116
122
  - examples/js_minifier_sinatra.rb
117
123
  - examples/map_generator.rb
118
124
  - examples/md5.rb
119
125
  - examples/patterns.rb
126
+ - examples/url2png.rb
127
+ - spec/env_spec.rb
128
+ - spec/spec_helper.rb
129
+ - spec/wso_wrapper_spec.rb
data/.gitignore DELETED
@@ -1,21 +0,0 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- ## PROJECT::SPECIFIC
data/spec/wso_wrapper.rb DELETED
@@ -1,11 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe Grope::WSOWrapper do
4
- it "should wrap" do
5
- WSOWrapper.wrap(nil).should eql(nil)
6
- WSOWrapper.wrap(true.to_ns).should eql(true)
7
- WSOWrapper.wrap(false.to_ns).should eql(false)
8
- WSOWrapper.wrap(1.to_ns).should eql(1)
9
- WSOWrapper.wrap("a".to_ns).should eql("a")
10
- end
11
- end