grope 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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 youpy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,35 @@
1
+ = grope
2
+
3
+ A non-GUI library to represent browser environment using WebKit Framework + RubyCocoa
4
+
5
+ == Sypnosis
6
+
7
+ require 'grope'
8
+
9
+ env = Grope::Env.new
10
+
11
+ env.load('http://example.com')
12
+ env.document.title //=> "Example Web Page"
13
+ env.find('//a').href //=> "http://www.rfc-editor.org/rfc/rfc2606.txt"
14
+ env.find('//a').offsetWidth //=> 58
15
+
16
+ env.load('http://nonn-et-twk.net/twk/nondrupal/flip/flip.html')
17
+ env.window.flipString('test') //=> "ʇsǝʇ"
18
+
19
+ == Requirements
20
+
21
+ * RubyCocoa
22
+
23
+ == Note on Patches/Pull Requests
24
+
25
+ * Fork the project.
26
+ * Make your feature addition or bug fix.
27
+ * Add tests for it. This is important so I don't break it in a
28
+ future version unintentionally.
29
+ * Commit, do not mess with rakefile, version, or history.
30
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
31
+ * Send me a pull request. Bonus points for topic branches.
32
+
33
+ == Copyright
34
+
35
+ Copyright (c) 2010 youpy. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "grope"
8
+ gem.summary = "A non-GUI library to represent browser environment"
9
+ gem.description = "A non-GUI library to represent browser environment using WebKit Framework"
10
+ gem.email = "youpy@buycheapviagraonlinenow.com"
11
+ gem.homepage = "http://github.com/youpy/grope"
12
+ gem.authors = ["youpy"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.add_dependency "mechanize", ">= 1.0.0"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+
36
+ task :default => :spec
37
+
38
+ require 'rake/rdoctask'
39
+ Rake::RDocTask.new do |rdoc|
40
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = "grope #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
data/bin/grope ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'optparse'
4
+ require 'irb'
5
+ require 'grope'
6
+
7
+ use_shared_cookie = false
8
+
9
+ opts = OptionParser.new do |opts|
10
+ opts.banner = "Grope: A non-GUI library to represent browser environment using WebKit Framework + RubyCocoa"
11
+ opts.define_head "Usage: #{$0} uri [options]"
12
+ opts.separator ""
13
+ opts.separator "Example:"
14
+ opts.separator " grope http://www.ruby-lang.org/"
15
+ opts.separator ""
16
+ opts.separator "Options:"
17
+
18
+ opts.on("-s", "--shared-cookie", "Use shared cookie") do
19
+ use_shared_cookie = true
20
+ end
21
+
22
+ opts.on_tail("-?", "--help", "Show this message") do
23
+ puts opts
24
+ exit
25
+ end
26
+ end
27
+ opts.parse!
28
+
29
+ uri = ARGV.shift
30
+
31
+ if uri.to_s.strip.empty?
32
+ puts opts
33
+ exit 1
34
+ end
35
+
36
+ @env = Grope::Env.new :use_shared_cookie => use_shared_cookie
37
+ @env.load(uri)
38
+
39
+ puts "Your environment is stored in @env..."
40
+ IRB.start
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'grope'
5
+
6
+ if ARGV.size != 1
7
+ warn "usage: #{$0} /path/to/js/to/minify.js"
8
+ exit 1
9
+ end
10
+
11
+ js_filename = ARGV.shift
12
+
13
+ env = Grope::Env.new
14
+ env.load('http://fmarcia.info/jsmin/test.html')
15
+ env.all('//textarea')[1].value = open(js_filename).read
16
+ env.document.getElementById('go').click
17
+ puts env.all('//textarea')[2].value
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'sinatra'
5
+ require 'grope'
6
+ require 'json'
7
+
8
+ $grope = Grope::Env.new
9
+ $grope.load('http://fmarcia.info/jsmin/test.html')
10
+
11
+ get '/' do
12
+ '<html><body><form action="/minify"><textarea name="js"></textarea><br><input type="submit"></form></body></html>'
13
+ end
14
+
15
+ get '/minify' do
16
+ content_type 'application/json'
17
+
18
+ $grope.all('//textarea')[1].value = params[:js]
19
+ $grope.find('id("go")').click
20
+
21
+ JSON.generate :result => $grope.all('//textarea')[2].value
22
+ end
23
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'grope'
5
+
6
+ env = Grope::Env.new
7
+ env.load('http://www.horaguchi.net/map_generator/map_generator.html')
8
+
9
+ while true
10
+ puts "\e[2J\e[0;0f"
11
+ env.window.generate
12
+ puts env.document.generator.text_map.value
13
+ sleep 0.5
14
+ end
data/examples/md5.rb ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'grope'
5
+
6
+ env = Grope::Env.new
7
+ env.load('http://www.onicos.com/staff/iz/amuse/javascript/expert/md5.html')
8
+ window = env.window
9
+ puts window.MD5_hexhash(window.utf16to8(STDIN.read))
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ env = Grope::Env.new
4
+ env.load('http://www.dinkypage.com/55654')
5
+
6
+ while true
7
+ puts env.document.body.innerText
8
+ sleep 1
9
+ end
data/grope.gemspec ADDED
@@ -0,0 +1,78 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{grope}
8
+ s.version = "0.0.4"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["youpy"]
12
+ s.date = %q{2010-07-05}
13
+ s.default_executable = %q{grope}
14
+ s.description = %q{A non-GUI library to represent browser environment using WebKit Framework}
15
+ s.email = %q{youpy@buycheapviagraonlinenow.com}
16
+ s.executables = ["grope"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
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"
45
+ ]
46
+ s.homepage = %q{http://github.com/youpy/grope}
47
+ s.rdoc_options = ["--charset=UTF-8"]
48
+ s.require_paths = ["lib"]
49
+ s.rubygems_version = %q{1.3.6}
50
+ s.summary = %q{A non-GUI library to represent browser environment}
51
+ s.test_files = [
52
+ "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"
60
+ ]
61
+
62
+ if s.respond_to? :specification_version then
63
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
64
+ s.specification_version = 3
65
+
66
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
67
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
68
+ s.add_runtime_dependency(%q<mechanize>, [">= 1.0.0"])
69
+ else
70
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
71
+ s.add_dependency(%q<mechanize>, [">= 1.0.0"])
72
+ end
73
+ else
74
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
75
+ s.add_dependency(%q<mechanize>, [">= 1.0.0"])
76
+ end
77
+ end
78
+
@@ -0,0 +1,23 @@
1
+ class DOMNode
2
+ def [](name)
3
+ hasAttribute(name.to_s).zero? ? nil : getAttribute(name.to_s)
4
+ end
5
+ end
6
+
7
+ [
8
+ DOMNodeList,
9
+ DOMHTMLOptionsCollection,
10
+ DOMHTMLCollection
11
+ ].each do |klass|
12
+ klass.class_eval do
13
+ def [](index)
14
+ item(index)
15
+ end
16
+ end
17
+ end
18
+
19
+ class WebScriptObject
20
+ def [](index)
21
+ webScriptValueAtIndex(index)
22
+ end
23
+ end
data/lib/grope/env.rb ADDED
@@ -0,0 +1,139 @@
1
+ require 'mechanize'
2
+
3
+ module Grope
4
+ class Env
5
+ def initialize(options = {})
6
+ @options = {
7
+ :timeout => 60,
8
+ :use_shared_cookie => false,
9
+ }.merge(options)
10
+
11
+ @webview = WebView.alloc
12
+ @webview.initWithFrame(NSMakeRect(0,0,100,100))
13
+ @webview.setPreferencesIdentifier('Grope')
14
+ @frame_load_delegate = FrameLoadDelegate.alloc.init
15
+ @webview.setFrameLoadDelegate(@frame_load_delegate)
16
+
17
+ unless @options[:use_shared_cookie]
18
+ @resource_load_delegate = WebResourceLoadDelegate.alloc.init
19
+ @resource_load_delegate.cookie_storage = Mechanize::CookieJar.new
20
+ @webview.setResourceLoadDelegate(@resource_load_delegate)
21
+ end
22
+ end
23
+
24
+ def load(url)
25
+ run do
26
+ @webview.setMainFrameURL(url)
27
+ if !@webview.mainFrame.provisionalDataSource
28
+ raise " ... not a proper url?"
29
+ end
30
+ end
31
+ end
32
+
33
+ def eval(js)
34
+ value = nil
35
+ run do
36
+ wso = @webview.windowScriptObject
37
+ value = WSOWrapper.wrap(wso.evaluateWebScript(<<JS % js))
38
+ (function() {
39
+ var Grope = {
40
+ click: function(e) { this._dispatchMouseEvent(e, 'click') },
41
+ mouseover: function(e) { this._dispatchMouseEvent(e, 'mouseover') },
42
+ mouseout: function(e) { this._dispatchMouseEvent(e, 'mouseout') },
43
+ mousedown: function(e) { this._dispatchMouseEvent(e, 'mousedown') },
44
+ mouseup: function(e) { this._dispatchMouseEvent(e, 'mouseup') },
45
+ xpath: function(exp, context, type /* want type */) {
46
+ if (typeof context == "function") {
47
+ type = context;
48
+ context = null;
49
+ }
50
+ if (!context) context = document;
51
+ exp = (context.ownerDocument || context).createExpression(exp, function (prefix) {
52
+ var o = document.createNSResolver(context)(prefix);
53
+ if (o) return o;
54
+ return (document.contentType == "application/xhtml+xml") ? "http://www.w3.org/1999/xhtml" : "";
55
+ });
56
+
57
+ switch (type) {
58
+ case String: return exp.evaluate(context, XPathResult.STRING_TYPE, null).stringValue;
59
+ case Number: return exp.evaluate(context, XPathResult.NUMBER_TYPE, null).numberValue;
60
+ case Boolean: return exp.evaluate(context, XPathResult.BOOLEAN_TYPE, null).booleanValue;
61
+ case Array:
62
+ var result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
63
+ for (var ret = [], i = 0, len = result.snapshotLength; i < len; i++) {
64
+ ret.push(result.snapshotItem(i));
65
+ }
66
+ return ret;
67
+ case undefined:
68
+ var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
69
+ switch (result.resultType) {
70
+ case XPathResult.STRING_TYPE : return result.stringValue;
71
+ case XPathResult.NUMBER_TYPE : return result.numberValue;
72
+ case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
73
+ case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
74
+ // not ensure the order.
75
+ var ret = [], i = null;
76
+ while ((i = result.iterateNext())) ret.push(i);
77
+ return ret;
78
+ }
79
+ return null;
80
+ default: throw(TypeError("$X: specified type is not valid type."));
81
+ }
82
+ },
83
+
84
+ _dispatchMouseEvent: function(e, type) {
85
+ var evt = document.createEvent('MouseEvents');
86
+ evt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
87
+ e.dispatchEvent(evt);
88
+ }
89
+ };
90
+
91
+ %s
92
+ })()
93
+ JS
94
+ end
95
+ wait
96
+ value
97
+ end
98
+
99
+ def wait(sec = 0)
100
+ run(sec) do; end
101
+ end
102
+
103
+ def document
104
+ eval('return document;')
105
+ end
106
+
107
+ def window
108
+ eval('return window;')
109
+ end
110
+
111
+ def all(xpath, node = nil)
112
+ node ||= document
113
+ js = eval('return Grope')
114
+ js.xpath(xpath, node)
115
+ end
116
+
117
+ def find(xpath, node = nil)
118
+ all(xpath, node)[0]
119
+ end
120
+
121
+ private
122
+
123
+ def run(wait_sec = 0)
124
+ @frame_load_delegate.performSelector_withObject_afterDelay('timeout:', @webview, @options[:timeout])
125
+
126
+ result = yield
127
+
128
+ run_loop = NSRunLoop.currentRunLoop
129
+ run_loop.runMode_beforeDate(NSDefaultRunLoopMode, Time.now)
130
+ while(@frame_load_delegate.should_keep_running &&
131
+ run_loop.runMode_beforeDate(NSDefaultRunLoopMode, Time.now + 0.1)); end
132
+ run_loop.runUntilDate(Time.now + wait_sec)
133
+
134
+ result
135
+ ensure
136
+ NSObject.cancelPreviousPerformRequestsWithTarget_selector_object(@frame_load_delegate, 'timeout:', @webview)
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,31 @@
1
+ module Grope
2
+ class FrameLoadDelegate < NSObject
3
+ attr_accessor :should_keep_running
4
+
5
+ def webView_didFailLoadWithError_forFrame(webview, error, frame)
6
+ terminate
7
+ end
8
+ alias webView_didFailProvisionalLoadWithError_forFrame webView_didFailLoadWithError_forFrame
9
+
10
+ def webView_didStartProvisionalLoadForFrame(webview, frame)
11
+ self.should_keep_running = true
12
+ end
13
+
14
+ def webView_willPerformClientRedirectToURL_delay_fireDate_forFrame(webview, url, delay, date, frame)
15
+ self.should_keep_running = true
16
+ end
17
+
18
+ def webView_didFinishLoadForFrame(webview, frame)
19
+ terminate
20
+ end
21
+
22
+ def terminate
23
+ self.should_keep_running = false
24
+ end
25
+
26
+ def timeout(webview)
27
+ warn "timeout"
28
+ terminate
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,85 @@
1
+ module Grope
2
+ class WebResourceLoadDelegate < NSObject
3
+ attr_accessor :cookie_storage
4
+
5
+ def webView_resource_willSendRequest_redirectResponse_fromDataSource(webview, resource, request, redirect_response, data_source)
6
+ request.setHTTPShouldHandleCookies(false)
7
+
8
+ if request.URL.to_s =~ /^http/
9
+ if redirect_response
10
+ set_cookies(redirect_response)
11
+ end
12
+
13
+ cookies = cookie_storage.cookies(URI(request.URL.to_s)).map {|wrapper| wrapper.cookie}
14
+ if cookies.size > 0
15
+ #warn "*** send cookie for %s ***\n%s" % [request.URL.to_s, cookies]
16
+ cookie_fields = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies)
17
+ request.setAllHTTPHeaderFields(cookie_fields)
18
+ end
19
+ end
20
+
21
+ request
22
+ end
23
+
24
+ def webView_resource_didReceiveResponse_fromDataSource(webview, resource, response, data_source)
25
+ set_cookies(response)
26
+ end
27
+
28
+ private
29
+
30
+ def set_cookies(response)
31
+ return unless response.isKindOfClass(NSHTTPURLResponse)
32
+
33
+ headers = response.allHeaderFields
34
+ url = response.URL
35
+
36
+ NSHTTPCookie.cookiesWithResponseHeaderFields_forURL(headers, url).each do |cookie|
37
+ cookie_storage.add(URI(url.to_s), NSHTTPCookieWrapper.new(cookie))
38
+ #warn "*** store cookie for %s ***\n%s" % [response.URL.to_s, cookie]
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ class NSHTTPCookie
45
+ def to_s
46
+ value = self.value
47
+ if value.size > 50
48
+ value = value[0, 47] + '...'
49
+ end
50
+
51
+ "%s=%s\t%s\t%s\t%s\n" % [name, value, domain, path, expiresDate, isSecure]
52
+ end
53
+ end
54
+
55
+ class NSHTTPCookieWrapper
56
+ attr_reader :cookie
57
+
58
+ def initialize(cookie)
59
+ @cookie = cookie
60
+ end
61
+
62
+ def domain
63
+ cookie.domain && cookie.domain.to_s.sub(/^\./, '')
64
+ end
65
+
66
+ def path
67
+ cookie.path && cookie.path.to_s
68
+ end
69
+
70
+ def name
71
+ cookie.name.to_s
72
+ end
73
+
74
+ def value
75
+ cookie.value
76
+ end
77
+
78
+ def secure
79
+ cookie.isSecure == true.to_ns ? true : false
80
+ end
81
+
82
+ def expired?
83
+ cookie.expiresDate && Time.at(cookie.expiresDate.timeIntervalSince1970.to_i)
84
+ end
85
+ end
@@ -0,0 +1,77 @@
1
+ module Grope
2
+ class WSOWrapper
3
+ instance_methods.each do |m|
4
+ undef_method m unless m.to_s =~
5
+ /inspect|to_s|class|method_missing|respond_to?|^__/
6
+ end
7
+
8
+ include Enumerable
9
+
10
+ attr_reader :wso
11
+
12
+ def self.wrap(value)
13
+ case value
14
+ when nil
15
+ nil
16
+ when WSOWrapper
17
+ value
18
+ when Integer
19
+ value
20
+ when OSX::NSCFBoolean
21
+ value
22
+ when OSX::NSCFNumber
23
+ value.to_i
24
+ when OSX::NSCFString
25
+ value.to_s
26
+ else
27
+ new(value)
28
+ end
29
+ end
30
+
31
+ def initialize(wso)
32
+ @wso = wso
33
+ end
34
+
35
+ def size
36
+ length
37
+ end
38
+
39
+ def each
40
+ i = 0
41
+ while i < size
42
+ yield self[i]
43
+ i += 1
44
+ end
45
+ end
46
+
47
+ def method_missing(name, *args)
48
+ args = unwrap_arguments(args)
49
+ result = nil
50
+
51
+ begin
52
+ if name.to_s =~ /^(apply|call|toString)$/
53
+ result = @wso.callWebScriptMethod_withArguments(name, args)
54
+ else
55
+ result = @wso.__send__(name, *args)
56
+ end
57
+ rescue
58
+ result = @wso.valueForKey(name)
59
+ end
60
+
61
+ if WebScriptObject === result &&
62
+ result.callWebScriptMethod_withArguments(:toString, []).to_s =~ /^function/
63
+ self.class.wrap(result.callWebScriptMethod_withArguments(:call, [@wso] + args))
64
+ else
65
+ self.class.wrap(result)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def unwrap_arguments(args)
72
+ args.map do |arg|
73
+ self.class === arg ? arg.wso : arg
74
+ end
75
+ end
76
+ end
77
+ end
data/lib/grope.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'osx/cocoa'
3
+ OSX.require_framework 'Webkit'
4
+ include OSX
5
+
6
+ module Grope
7
+ end
8
+
9
+ require 'grope/dom_ext'
10
+ require 'grope/wso_wrapper'
11
+ require 'grope/frame_load_delegate'
12
+ require 'grope/web_resource_load_delegate'
13
+ require 'grope/env'
data/spec/env_spec.rb ADDED
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Grope::Env do
4
+ before do
5
+ @env = Grope::Env.new
6
+ @env.load('http://example.com')
7
+ end
8
+
9
+ it "should initialize" do
10
+ @env.document.title.should eql('Example Web Page')
11
+ end
12
+
13
+ it "should get elements by XPath" do
14
+ 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')
17
+ end
18
+
19
+ it "should get links" do
20
+ result = @env.document.links
21
+ result[0].href.should eql('http://www.rfc-editor.org/rfc/rfc2606.txt')
22
+ end
23
+
24
+ it "should eval" do
25
+ @env.window.location.href.should eql('http://example.com/')
26
+ end
27
+
28
+ it "should redirect" do
29
+ @env.eval('location.href="http://example.org"')
30
+ @env.document.location.href.should eql('http://example.org/')
31
+ end
32
+
33
+ it "should redirect by click" do
34
+ anchor = @env.document.links[0]
35
+ js = @env.eval('return Grope;')
36
+ js.click(anchor)
37
+
38
+ # TODO: wait automatically after js function is called
39
+ @env.wait
40
+
41
+ @env.document.URL.should eql('http://www.rfc-editor.org/rfc/rfc2606.txt')
42
+ end
43
+
44
+ it "should get/call function" do
45
+ 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)
48
+ end
49
+
50
+ it "should call function in object" do
51
+ obj = @env.eval('return { hello: function() { return "hello"; }}')
52
+ obj.hello.should eql('hello')
53
+ end
54
+
55
+ it "should wait" do
56
+ now = Time.now.to_i
57
+
58
+ @env.wait(3)
59
+
60
+ (Time.now.to_i - now).should be_close(3, 1)
61
+ end
62
+
63
+ it "should use shared cookie storage if 'use_shared_cookie' option is true" do
64
+ env = Grope::Env.new(:use_shared_cookie => true)
65
+ env.instance_eval { @resource_load_delegate }.should be_nil
66
+ end
67
+
68
+ it "should use own cookie storage if 'use_shared_cookie' option is false" do
69
+ @env.instance_eval { @resource_load_delegate }.should_not be_nil
70
+ @env.load('http://google.com/')
71
+ @env.instance_eval { @resource_load_delegate }.cookie_storage.cookies(URI('http://google.com/')).should_not be_nil
72
+ end
73
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'grope'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ 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
+ 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
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grope
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 4
9
+ version: 0.0.4
10
+ platform: ruby
11
+ authors:
12
+ - youpy
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-05 00:00:00 +09:00
18
+ default_executable: grope
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 9
31
+ version: 1.2.9
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: mechanize
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 0
44
+ - 0
45
+ version: 1.0.0
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ description: A non-GUI library to represent browser environment using WebKit Framework
49
+ email: youpy@buycheapviagraonlinenow.com
50
+ executables:
51
+ - grope
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - LICENSE
56
+ - README.rdoc
57
+ files:
58
+ - .document
59
+ - .gitignore
60
+ - LICENSE
61
+ - README.rdoc
62
+ - Rakefile
63
+ - VERSION
64
+ - bin/grope
65
+ - examples/js_minifier.rb
66
+ - examples/js_minifier_sinatra.rb
67
+ - examples/map_generator.rb
68
+ - examples/md5.rb
69
+ - examples/patterns.rb
70
+ - grope.gemspec
71
+ - lib/grope.rb
72
+ - lib/grope/dom_ext.rb
73
+ - lib/grope/env.rb
74
+ - lib/grope/frame_load_delegate.rb
75
+ - lib/grope/web_resource_load_delegate.rb
76
+ - lib/grope/wso_wrapper.rb
77
+ - spec/env_spec.rb
78
+ - spec/spec.opts
79
+ - spec/spec_helper.rb
80
+ - spec/wso_wrapper.rb
81
+ has_rdoc: true
82
+ homepage: http://github.com/youpy/grope
83
+ licenses: []
84
+
85
+ post_install_message:
86
+ rdoc_options:
87
+ - --charset=UTF-8
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirements: []
105
+
106
+ rubyforge_project:
107
+ rubygems_version: 1.3.6
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: A non-GUI library to represent browser environment
111
+ test_files:
112
+ - spec/env_spec.rb
113
+ - spec/spec_helper.rb
114
+ - spec/wso_wrapper.rb
115
+ - examples/js_minifier.rb
116
+ - examples/js_minifier_sinatra.rb
117
+ - examples/map_generator.rb
118
+ - examples/md5.rb
119
+ - examples/patterns.rb