Skip to content

Commit

Permalink
sc - starting out with some better inspection tools to be more produc…
Browse files Browse the repository at this point in the history
…tive working with cocoa
  • Loading branch information
sconover committed Oct 16, 2009
1 parent c41dc00 commit 4c38b13
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 31 deletions.
Binary file added .DS_Store
Binary file not shown.
38 changes: 38 additions & 0 deletions lib/decorator.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,38 @@
class Object
def singleton_class
(class << self; self; end)
end
end

class Decorator
def before(&block)
@before = block
end

def after(&block)
@after = block
end

def apply_to(subject)
before_block = @before
after_block = @after

subject.public_methods(false).each do |method|
next if method == "inspect"
subject.singleton_class.class_eval do
original_method_symbol = "original_#{method}".to_sym
alias_method original_method_symbol, method

define_method(method) do |*args|
before_block.call(method.to_sym, args) if before_block

result = subject.send(original_method_symbol, *args)

after_block.call(method.to_sym, args, result) if after_block

result
end
end
end
end
end
4 changes: 2 additions & 2 deletions lib/local_protocol.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def self.request_handler=(block)


def startLoading def startLoading
rack_response = CocoaToRackResponseAdapter.new(request, self) rack_response = CocoaToRackResponseAdapter.new(request, self)

@@block.call( @@block.call(
CocoaToRackRequestAdapter.new(request), CocoaToRackRequestAdapter.new(request),
rack_response rack_response
Expand Down Expand Up @@ -63,7 +63,7 @@ def initialize(ns_url_request)
env["PATH_INFO"] ||= ns_url_request.URL.path.to_s env["PATH_INFO"] ||= ns_url_request.URL.path.to_s
env["QUERY_STRING"] ||= ns_url_request.URL.query.to_s env["QUERY_STRING"] ||= ns_url_request.URL.query.to_s
env["rack.url_scheme"] = ns_url_request.URL.scheme.to_s env["rack.url_scheme"] = ns_url_request.URL.scheme.to_s

super(env) super(env)
end end


Expand Down
29 changes: 29 additions & 0 deletions lib/log_decorator.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,29 @@
require "lib/decorator"

class LogDecorator
def initialize(stream)
@stream = stream
end

def apply_to(subject)
the_stream = @stream

decorator = Decorator.new
decorator.before do |method_symbol, args|
the_stream << "-> #{subject.class}.#{method_symbol.to_s}#{_args_to_s(args)}\n"
end
decorator.after do |method_symbol, args, result|
the_stream << "<- #{subject.class}.#{method_symbol.to_s}#{_args_to_s(args)} : #{result.inspect}\n"
end

decorator.apply_to(subject)
end

def _args_to_s(args)
if args.length > 0
"(" + args.collect{|arg|arg.class.to_s}.join(", ") + ")"
else
""
end
end
end
16 changes: 9 additions & 7 deletions lib/schnauzer.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'osx/cocoa' require 'osx/cocoa'
require 'lib/cocoa_utils' require 'lib/cocoa_utils'
require "lib/local_protocol" require "lib/local_protocol"
require "lib/log_decorator"




OSX.require_framework 'WebKit' OSX.require_framework 'WebKit'
Expand Down Expand Up @@ -76,7 +77,12 @@ def js(str)
end end
end end



class Object
def log_calls(to=$stdout)
LogDecorator.new(to).apply_to(self)
self
end
end


class WebResourceLoadDelegate < OSX::NSObject class WebResourceLoadDelegate < OSX::NSObject


Expand All @@ -94,7 +100,7 @@ def xwebView_identifierForInitialRequest_fromDataSource(v, request, source)


def webView_resource_willSendRequest_redirectResponse_fromDataSource(v, resource, request, response, source) def webView_resource_willSendRequest_redirectResponse_fromDataSource(v, resource, request, response, source)
# puts "webView_resource_willSendRequest_redirectResponse_fromDataSource #{request.URL.to_s}" # puts "webView_resource_willSendRequest_redirectResponse_fromDataSource #{request.URL.to_s}"

# p request.HTTPBody
request_headers = request.allHTTPHeaderFields.to_hash request_headers = request.allHTTPHeaderFields.to_hash
if request_headers.values.collect{|v|v.to_s}.include?("XMLHttpRequest") if request_headers.values.collect{|v|v.to_s}.include?("XMLHttpRequest")
@unresolved_ajax_request = true @unresolved_ajax_request = true
Expand Down Expand Up @@ -135,8 +141,6 @@ def webView_resource_didReceiveAuthenticationChallenge_fromDataSource(v, resourc
def webView_resource_didCancelAuthenticationChallenge_fromDataSource(v, resource, challenge, source) def webView_resource_didCancelAuthenticationChallenge_fromDataSource(v, resource, challenge, source)
# p 9 # p 9
end end


end end


class WebFrameLoadDelegate < OSX::NSObject class WebFrameLoadDelegate < OSX::NSObject
Expand All @@ -152,9 +156,7 @@ def webView_didFailLoadWithError_forFrame(webview, load_error, frame)
def webView_didFailProvisionalLoadWithError_forFrame(webview, load_error, frame) def webView_didFailProvisionalLoadWithError_forFrame(webview, load_error, frame)
OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent) OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
end end




def webView_didStartProvisionalLoadForFrame(v, frame) def webView_didStartProvisionalLoadForFrame(v, frame)
end end


Expand Down
40 changes: 39 additions & 1 deletion notes.txt
Original file line number Original file line Diff line number Diff line change
@@ -1,4 +1,36 @@


Coming back to schnauzer

- I have no idea what's going on in this objc binding code
- Neither would anyone else
- So the objective ought to be to make a nice objc binding/exploration toolkit
- dynamic adapter class that logs automatically?
...
- and then, copious documentation on what I find is happening


Thoughts

- bundler-ify?
- MacRuby mode / adapter (vs MRI+rubycocoa)?
- recognize MacRuby, run tests in that context

===========

Getting this done is about
1) understanding webkit bugs and working with the community to get them fixed
2) flexing the api and driving out memory leaks and such
3) documenting the esoteric parts of the code
4) webrat?


No http daemons, no network traffic. It's all in-process.
Real browser
Fast
Simple/minimal api

=============

rack request/response rack request/response
Schnauzer.new do |request, response| Schnauzer.new do |request, response|
#handle the request, write to the response #handle the request, write to the response
Expand All @@ -16,4 +48,10 @@ assert failures






need to dealloc. lots of lurking memory issues. need to dealloc. lots of lurking memory issues.


this project represents a lot of objc and cocoa webkit reverse engineering. comment that stuff carefully.


mode where js errors raise a ruby exception?
19 changes: 19 additions & 0 deletions schnauzer.gemspec
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,19 @@
Gem::Specification.new do |s|
s.name = %q{schnauzer}
s.version = "0.1"

s.authors = ["Steve Conover"]
s.date = %q{2009-02-24}
s.description = %q{A real Safari browser in ruby}
s.email = %q{sconover@gmail.com}
s.extensions = []
s.extra_rdoc_files = ["README", "LICENSE"]
s.files = []
s.has_rdoc = true
s.homepage = %q{http://github.com/sconover/schnauzer/tree/master}
s.rdoc_options = ["--quiet", "--title", "Schnauzer", "--main", "README", "--inline-source"]
s.require_paths = ["lib"]
s.summary = %q{A real Safari browser in ruby. You can provide your own url request/response handling. It's fast.}
s.add_dependency "sinatra"
s.add_dependency "rubycocoa"
end
93 changes: 85 additions & 8 deletions spec/app_integration_spec.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
require "benchmark" require "benchmark"
require "lib/schnauzer" require "lib/schnauzer"
require 'sinatra/test' require 'sinatra/test'
require "spec" require "test/spec"






describe "schnauzer + sinatra app" do describe "schnauzer + sinatra app" do
before(:all) do before do
app = app =
Sinatra.new do Sinatra.new do
set :static, true set :static, true
Expand All @@ -21,6 +21,33 @@
"#{params[:to_echo]}, echoed back" "#{params[:to_echo]}, echoed back"
end end


post '/echo_standard_post' do
(<<-HTML)
<html>
<body>
<div id="message">You posted #{params[:to_echo]}</div>
</body>
</html>
HTML
end

post '/echo_post' do
"#{params[:to_echo]}, post echoed back"
end

get '/html_post' do
(<<-HTML)
<html>
<body>
hi I do standard post
<form id="the_form" method="post" action="/echo_standard_post">
<input id='message_to_post' type="hidden" name="to_echo" value=""/>
<input type="submit"/>
</form>
</body>
</html>
HTML
end


get '/ajax' do get '/ajax' do
(<<-HTML) (<<-HTML)
Expand Down Expand Up @@ -48,6 +75,35 @@
</html> </html>
HTML HTML
end end

get '/ajax_post' do
(<<-HTML)
<html>
<head>
<script type="text/javascript" language="JavaScript" src="/prototype.js"></script>
<script type="text/javascript">
function doEchoPost(str) {
$('message').innerHTML = "echoing " + str
new Ajax.Request(
"/echo_post",
{method: 'post',
postBody: str,
onComplete: function(result){document.getElementById("message").innerHTML = result.responseText},
onFailure: function(){document.getElementById("message").innerHTML = "failed"}}
)
}
</script>
</head>
<body>
hi I do ajax post
<button id="ajax_button" onclick="doEchoPost('peter_rabbit')">click me to do ajax</button>
message goes here:<div id="message"></div>
</body>
</html>
HTML
end

end end


harness = Sinatra::TestHarness.new(app) harness = Sinatra::TestHarness.new(app)
Expand All @@ -58,7 +114,7 @@
@harness = harness @harness = harness
end end


after(:all) do after do
Schnauzer.unload_request_handler Schnauzer.unload_request_handler
end end


Expand All @@ -69,21 +125,42 @@


it "same thing but with schnauzer (using the session object to request)" do it "same thing but with schnauzer (using the session object to request)" do
@browser.load_url("local://host/foo/hi") @browser.load_url("local://host/foo/hi")
@browser.js("document.body.innerHTML").should include("hi I'm a web app") @browser.js("document.body.innerHTML").should.include("hi I'm a web app")
end end


it "basic ajax works" do it "basic ajax works" do
@browser.load_url("local://host/ajax") @browser.load_url("local://host/ajax")
@browser.js("document.body.innerHTML").should include("hi I do ajax") @browser.js("document.body.innerHTML").should.include("hi I do ajax")


@browser.js("document.getElementById('ajax_button').onclick()") @browser.js("document.getElementById('ajax_button').onclick()")
@browser.js("document.body.innerHTML").should include(%{message goes here:<div id="message">peter_rabbit, echoed back</div>}) @browser.js("document.body.innerHTML").should.include(%{message goes here:<div id="message">peter_rabbit, echoed back</div>})


@browser.js("doEcho('flopsy')") @browser.js("doEcho('flopsy')")
@browser.js("document.body.innerHTML").should include(%{message goes here:<div id="message">flopsy, echoed back</div>}) @browser.js("document.body.innerHTML").should.include(%{message goes here:<div id="message">flopsy, echoed back</div>})
end

xit "standard post" do
@browser.load_url("local://host/html_post")

@browser.js("document.getElementById('message_to_post').value = 'Peter Rabbit'")
@browser.js("document.getElementById('the_form').submit()")

@browser.js("document.body.innerHTML").should.include("You posted Peter Rabbit")
end


xit "ajax post works" do
@browser.load_url("local://host/ajax_post")
@browser.js("document.body.innerHTML").should.include("hi I do ajax")

@browser.js("document.getElementById('ajax_button').onclick()")
@browser.js("document.body.innerHTML").should.include(%{message goes here:<div id="message">peter_rabbit, post echoed back</div>})

# @browser.js("doEchoPost('flopsy')")
# @browser.js("document.body.innerHTML").should.include(%{message goes here:<div id="message">flopsy, post echoed back</div>})
end end


xit "ajax page performance" do it "ajax page performance" do
n = 20 n = 20
time = time =
Benchmark.realtime { Benchmark.realtime {
Expand Down
10 changes: 5 additions & 5 deletions spec/browser_basics_spec.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
require "rubygems" require "rubygems"
require "lib/schnauzer" require "lib/schnauzer"
require "benchmark" require "benchmark"
require "spec" require "test/spec"


describe Schnauzer do describe Schnauzer do
before do before do
Expand All @@ -19,7 +19,7 @@
HTML HTML
) )


@browser.js("document.body.innerHTML").should include("<i>hello world</i>") @browser.js("document.body.innerHTML").should.include("<i>hello world</i>")
end end


it "uses base url provided to make absolute urls" do it "uses base url provided to make absolute urls" do
Expand All @@ -32,7 +32,7 @@
"http://bar") "http://bar")




@browser.js("document.getElementById('mylink').href").should == "http://bar/mypage.html" @browser.js("document.getElementById('mylink').href").should.equal "http://bar/mypage.html"
end end


it "executes javascript" do it "executes javascript" do
Expand All @@ -51,12 +51,12 @@
) )


@browser.js("document.body.innerHTML") \ @browser.js("document.body.innerHTML") \
.should include(%{hello <div id="the_spot">world</div>}) .should.include(%{hello <div id="the_spot">world</div>})
end end


it "load from url" do it "load from url" do
@browser.load_url("file://#{File.expand_path("spec/little_page.html")}") @browser.load_url("file://#{File.expand_path("spec/little_page.html")}")
@browser.js("document.body.innerHTML").should include(%{hello <div id="foo">world</div>}) @browser.js("document.body.innerHTML").should.include(%{hello <div id="foo">world</div>})
end end


it "performance" do it "performance" do
Expand Down
Loading

0 comments on commit 4c38b13

Please sign in to comment.