grope 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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