grope 0.0.4 → 0.1.1

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.
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