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
@@ -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
Expand Up @@ -21,7 +21,7 @@ def self.request_handler=(block)

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

@@block.call(
CocoaToRackRequestAdapter.new(request),
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["QUERY_STRING"] ||= ns_url_request.URL.query.to_s
env["rack.url_scheme"] = ns_url_request.URL.scheme.to_s

super(env)
end

Expand Down
29 changes: 29 additions & 0 deletions lib/log_decorator.rb
@@ -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
@@ -1,6 +1,7 @@
require 'osx/cocoa'
require 'lib/cocoa_utils'
require "lib/local_protocol"
require "lib/log_decorator"


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


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

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)
# puts "webView_resource_willSendRequest_redirectResponse_fromDataSource #{request.URL.to_s}"

# p request.HTTPBody
request_headers = request.allHTTPHeaderFields.to_hash
if request_headers.values.collect{|v|v.to_s}.include?("XMLHttpRequest")
@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)
# p 9
end


end

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)
OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
end




def webView_didStartProvisionalLoadForFrame(v, frame)
end

Expand Down
40 changes: 39 additions & 1 deletion notes.txt
@@ -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
Schnauzer.new do |request, 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
@@ -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
Expand Up @@ -2,12 +2,12 @@
require "benchmark"
require "lib/schnauzer"
require 'sinatra/test'
require "spec"
require "test/spec"



describe "schnauzer + sinatra app" do
before(:all) do
before do
app =
Sinatra.new do
set :static, true
Expand All @@ -21,6 +21,33 @@
"#{params[:to_echo]}, echoed back"
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
(<<-HTML)
Expand Down Expand Up @@ -48,6 +75,35 @@
</html>
HTML
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

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

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

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

it "same thing but with schnauzer (using the session object to request)" do
@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

it "basic ajax works" do
@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.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("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

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

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

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

it "uses base url provided to make absolute urls" do
Expand All @@ -32,7 +32,7 @@
"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

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

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

it "load from url" do
@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

it "performance" do
Expand Down

0 comments on commit 4c38b13

Please sign in to comment.