Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 [Florian Gross]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@492 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
981d86cbb9
commit
505e2d99da
@ -1,85 +1,83 @@
|
||||
begin
|
||||
require 'simplecc'
|
||||
rescue LoadError
|
||||
class Continuation #:nodoc:
|
||||
def self.create(*args, &block)
|
||||
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
||||
result ||= args
|
||||
return *[cc, *result]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Binding #:nodoc:
|
||||
# This method returns the binding of the method that called your
|
||||
# method. It will raise an Exception when you're not inside a method.
|
||||
#
|
||||
# It's used like this:
|
||||
# def inc_counter(amount = 1)
|
||||
# Binding.of_caller do |binding|
|
||||
# # Create a lambda that will increase the variable 'counter'
|
||||
# # in the caller of this method when called.
|
||||
# inc = eval("lambda { |arg| counter += arg }", binding)
|
||||
# # We can refer to amount from inside this block safely.
|
||||
# inc.call(amount)
|
||||
# end
|
||||
# # No other statements can go here. Put them inside the block.
|
||||
# end
|
||||
# counter = 0
|
||||
# 2.times { inc_counter }
|
||||
# counter # => 2
|
||||
#
|
||||
# Binding.of_caller must be the last statement in the method.
|
||||
# This means that you will have to put everything you want to
|
||||
# do after the call to Binding.of_caller into the block of it.
|
||||
# This should be no problem however, because Ruby has closures.
|
||||
# If you don't do this an Exception will be raised. Because of
|
||||
# the way that Binding.of_caller is implemented it has to be
|
||||
# done this way.
|
||||
def self.of_caller(&block)
|
||||
old_critical = Thread.critical
|
||||
Thread.critical = true
|
||||
count = 0
|
||||
cc, result, error, extra_data = Continuation.create(nil, nil)
|
||||
error.call if error
|
||||
|
||||
tracer = lambda do |*args|
|
||||
type, context, extra_data = args[0], args[4], args
|
||||
if type == "return"
|
||||
count += 1
|
||||
# First this method and then calling one will return --
|
||||
# the trace event of the second event gets the context
|
||||
# of the method which called the method that called this
|
||||
# method.
|
||||
if count == 2
|
||||
# It would be nice if we could restore the trace_func
|
||||
# that was set before we swapped in our own one, but
|
||||
# this is impossible without overloading set_trace_func
|
||||
# in current Ruby.
|
||||
set_trace_func(nil)
|
||||
cc.call(eval("binding", context), nil, extra_data)
|
||||
end
|
||||
elsif type == "line" then
|
||||
nil
|
||||
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
||||
nil
|
||||
else
|
||||
set_trace_func(nil)
|
||||
error_msg = "Binding.of_caller used in non-method context or " +
|
||||
"trailing statements of method using it aren't in the block."
|
||||
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
||||
end
|
||||
end
|
||||
|
||||
unless result
|
||||
set_trace_func(tracer)
|
||||
return nil
|
||||
else
|
||||
Thread.critical = old_critical
|
||||
case block.arity
|
||||
when 1 then yield(result)
|
||||
else yield(result, extra_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
begin
|
||||
require 'simplecc'
|
||||
rescue LoadError
|
||||
class Continuation; end # :nodoc: # for RDoc
|
||||
def Continuation.create(*args, &block) # :nodoc:
|
||||
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
||||
result ||= args
|
||||
return *[cc, *result]
|
||||
end
|
||||
end
|
||||
|
||||
class Binding; end # for RDoc
|
||||
# This method returns the binding of the method that called your
|
||||
# method. It will raise an Exception when you're not inside a method.
|
||||
#
|
||||
# It's used like this:
|
||||
# def inc_counter(amount = 1)
|
||||
# Binding.of_caller do |binding|
|
||||
# # Create a lambda that will increase the variable 'counter'
|
||||
# # in the caller of this method when called.
|
||||
# inc = eval("lambda { |arg| counter += arg }", binding)
|
||||
# # We can refer to amount from inside this block safely.
|
||||
# inc.call(amount)
|
||||
# end
|
||||
# # No other statements can go here. Put them inside the block.
|
||||
# end
|
||||
# counter = 0
|
||||
# 2.times { inc_counter }
|
||||
# counter # => 2
|
||||
#
|
||||
# Binding.of_caller must be the last statement in the method.
|
||||
# This means that you will have to put everything you want to
|
||||
# do after the call to Binding.of_caller into the block of it.
|
||||
# This should be no problem however, because Ruby has closures.
|
||||
# If you don't do this an Exception will be raised. Because of
|
||||
# the way that Binding.of_caller is implemented it has to be
|
||||
# done this way.
|
||||
def Binding.of_caller(&block)
|
||||
old_critical = Thread.critical
|
||||
Thread.critical = true
|
||||
count = 0
|
||||
cc, result, error, extra_data = Continuation.create(nil, nil)
|
||||
error.call if error
|
||||
|
||||
tracer = lambda do |*args|
|
||||
type, context, extra_data = args[0], args[4], args
|
||||
if type == "return"
|
||||
count += 1
|
||||
# First this method and then calling one will return --
|
||||
# the trace event of the second event gets the context
|
||||
# of the method which called the method that called this
|
||||
# method.
|
||||
if count == 2
|
||||
# It would be nice if we could restore the trace_func
|
||||
# that was set before we swapped in our own one, but
|
||||
# this is impossible without overloading set_trace_func
|
||||
# in current Ruby.
|
||||
set_trace_func(nil)
|
||||
cc.call(eval("binding", context), nil, extra_data)
|
||||
end
|
||||
elsif type == "line" then
|
||||
nil
|
||||
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
||||
nil
|
||||
else
|
||||
set_trace_func(nil)
|
||||
error_msg = "Binding.of_caller used in non-method context or " +
|
||||
"trailing statements of method using it aren't in the block."
|
||||
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
||||
end
|
||||
end
|
||||
|
||||
unless result
|
||||
set_trace_func(tracer)
|
||||
return nil
|
||||
else
|
||||
Thread.critical = old_critical
|
||||
case block.arity
|
||||
when 1 then yield(result)
|
||||
else yield(result, extra_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
||||
*SVN*
|
||||
|
||||
* Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 [Florian Gross]
|
||||
|
||||
* Fixed skeleton Rakefile to work with sqlite3 out of the box #521 [rasputnik]
|
||||
|
||||
* Fixed that script/breakpointer didn't get the Ruby path rewritten as the other scripts #523 [brandt@kurowski.net]
|
||||
|
@ -1,85 +1,83 @@
|
||||
begin
|
||||
require 'simplecc'
|
||||
rescue LoadError
|
||||
class Continuation #:nodoc:
|
||||
def self.create(*args, &block)
|
||||
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
||||
result ||= args
|
||||
return *[cc, *result]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Binding #:nodoc:
|
||||
# This method returns the binding of the method that called your
|
||||
# method. It will raise an Exception when you're not inside a method.
|
||||
#
|
||||
# It's used like this:
|
||||
# def inc_counter(amount = 1)
|
||||
# Binding.of_caller do |binding|
|
||||
# # Create a lambda that will increase the variable 'counter'
|
||||
# # in the caller of this method when called.
|
||||
# inc = eval("lambda { |arg| counter += arg }", binding)
|
||||
# # We can refer to amount from inside this block safely.
|
||||
# inc.call(amount)
|
||||
# end
|
||||
# # No other statements can go here. Put them inside the block.
|
||||
# end
|
||||
# counter = 0
|
||||
# 2.times { inc_counter }
|
||||
# counter # => 2
|
||||
#
|
||||
# Binding.of_caller must be the last statement in the method.
|
||||
# This means that you will have to put everything you want to
|
||||
# do after the call to Binding.of_caller into the block of it.
|
||||
# This should be no problem however, because Ruby has closures.
|
||||
# If you don't do this an Exception will be raised. Because of
|
||||
# the way that Binding.of_caller is implemented it has to be
|
||||
# done this way.
|
||||
def self.of_caller(&block)
|
||||
old_critical = Thread.critical
|
||||
Thread.critical = true
|
||||
count = 0
|
||||
cc, result, error, extra_data = Continuation.create(nil, nil)
|
||||
error.call if error
|
||||
|
||||
tracer = lambda do |*args|
|
||||
type, context, extra_data = args[0], args[4], args
|
||||
if type == "return"
|
||||
count += 1
|
||||
# First this method and then calling one will return --
|
||||
# the trace event of the second event gets the context
|
||||
# of the method which called the method that called this
|
||||
# method.
|
||||
if count == 2
|
||||
# It would be nice if we could restore the trace_func
|
||||
# that was set before we swapped in our own one, but
|
||||
# this is impossible without overloading set_trace_func
|
||||
# in current Ruby.
|
||||
set_trace_func(nil)
|
||||
cc.call(eval("binding", context), nil, extra_data)
|
||||
end
|
||||
elsif type == "line" then
|
||||
nil
|
||||
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
||||
nil
|
||||
else
|
||||
set_trace_func(nil)
|
||||
error_msg = "Binding.of_caller used in non-method context or " +
|
||||
"trailing statements of method using it aren't in the block."
|
||||
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
||||
end
|
||||
end
|
||||
|
||||
unless result
|
||||
set_trace_func(tracer)
|
||||
return nil
|
||||
else
|
||||
Thread.critical = old_critical
|
||||
case block.arity
|
||||
when 1 then yield(result)
|
||||
else yield(result, extra_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
begin
|
||||
require 'simplecc'
|
||||
rescue LoadError
|
||||
class Continuation; end # :nodoc: # for RDoc
|
||||
def Continuation.create(*args, &block) # :nodoc:
|
||||
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
||||
result ||= args
|
||||
return *[cc, *result]
|
||||
end
|
||||
end
|
||||
|
||||
class Binding; end # for RDoc
|
||||
# This method returns the binding of the method that called your
|
||||
# method. It will raise an Exception when you're not inside a method.
|
||||
#
|
||||
# It's used like this:
|
||||
# def inc_counter(amount = 1)
|
||||
# Binding.of_caller do |binding|
|
||||
# # Create a lambda that will increase the variable 'counter'
|
||||
# # in the caller of this method when called.
|
||||
# inc = eval("lambda { |arg| counter += arg }", binding)
|
||||
# # We can refer to amount from inside this block safely.
|
||||
# inc.call(amount)
|
||||
# end
|
||||
# # No other statements can go here. Put them inside the block.
|
||||
# end
|
||||
# counter = 0
|
||||
# 2.times { inc_counter }
|
||||
# counter # => 2
|
||||
#
|
||||
# Binding.of_caller must be the last statement in the method.
|
||||
# This means that you will have to put everything you want to
|
||||
# do after the call to Binding.of_caller into the block of it.
|
||||
# This should be no problem however, because Ruby has closures.
|
||||
# If you don't do this an Exception will be raised. Because of
|
||||
# the way that Binding.of_caller is implemented it has to be
|
||||
# done this way.
|
||||
def Binding.of_caller(&block)
|
||||
old_critical = Thread.critical
|
||||
Thread.critical = true
|
||||
count = 0
|
||||
cc, result, error, extra_data = Continuation.create(nil, nil)
|
||||
error.call if error
|
||||
|
||||
tracer = lambda do |*args|
|
||||
type, context, extra_data = args[0], args[4], args
|
||||
if type == "return"
|
||||
count += 1
|
||||
# First this method and then calling one will return --
|
||||
# the trace event of the second event gets the context
|
||||
# of the method which called the method that called this
|
||||
# method.
|
||||
if count == 2
|
||||
# It would be nice if we could restore the trace_func
|
||||
# that was set before we swapped in our own one, but
|
||||
# this is impossible without overloading set_trace_func
|
||||
# in current Ruby.
|
||||
set_trace_func(nil)
|
||||
cc.call(eval("binding", context), nil, extra_data)
|
||||
end
|
||||
elsif type == "line" then
|
||||
nil
|
||||
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
||||
nil
|
||||
else
|
||||
set_trace_func(nil)
|
||||
error_msg = "Binding.of_caller used in non-method context or " +
|
||||
"trailing statements of method using it aren't in the block."
|
||||
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
||||
end
|
||||
end
|
||||
|
||||
unless result
|
||||
set_trace_func(tracer)
|
||||
return nil
|
||||
else
|
||||
Thread.critical = old_critical
|
||||
case block.arity
|
||||
when 1 then yield(result)
|
||||
else yield(result, extra_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -21,6 +21,9 @@
|
||||
require 'drb/acl'
|
||||
|
||||
module Breakpoint
|
||||
id = %q$Id: breakpoint.rb 41 2005-01-22 20:22:10Z flgr $
|
||||
Version = id.split(" ")[2].to_i
|
||||
|
||||
extend self
|
||||
|
||||
# This will pop up an interactive ruby session at a
|
||||
@ -114,10 +117,10 @@ def breakpoint(id = nil, context = nil, &block)
|
||||
end
|
||||
end
|
||||
|
||||
module CommandBundle #:nodoc:
|
||||
module CommandBundle
|
||||
# Proxy to a Breakpoint client. Lets you directly execute code
|
||||
# in the context of the client.
|
||||
class Client#:nodoc:
|
||||
class Client
|
||||
def initialize(eval_handler) # :nodoc:
|
||||
@eval_handler = eval_handler
|
||||
end
|
||||
@ -133,15 +136,23 @@ def eval(code)
|
||||
end
|
||||
|
||||
# Will execute the specified statement at the client.
|
||||
def method_missing(method, *args)
|
||||
if args.empty?
|
||||
result = eval("#{method}")
|
||||
def method_missing(method, *args, &block)
|
||||
if args.empty? and not block
|
||||
result = eval "#{method}"
|
||||
else
|
||||
result = eval("#{method}(*Marshal.load(#{Marshal.dump(args).inspect}))")
|
||||
end
|
||||
|
||||
unless [true, false, nil].include?(result)
|
||||
result.extend(DRbUndumped) if result
|
||||
# This is a bit ugly. The alternative would be using an
|
||||
# eval context instead of an eval handler for executing
|
||||
# the code at the client. The problem with that approach
|
||||
# is that we would have to handle special expressions
|
||||
# like "self", "nil" or constants ourself which is hard.
|
||||
remote = eval %{
|
||||
result = lambda { |block, *args| #{method}(*args, &block) }
|
||||
def result.call_with_block(*args, &block)
|
||||
call(block, *args)
|
||||
end
|
||||
result
|
||||
}
|
||||
remote.call_with_block(*args, &block)
|
||||
end
|
||||
|
||||
return result
|
||||
@ -175,6 +186,7 @@ def source_lines(context = 5, return_line_numbers = false)
|
||||
# client.File.open("temp.txt", "w") { |f| f.puts "Hello" }
|
||||
def client()
|
||||
if Breakpoint.use_drb? then
|
||||
sleep(0.5) until Breakpoint.drb_service.eval_handler
|
||||
Client.new(Breakpoint.drb_service.eval_handler)
|
||||
else
|
||||
Client.new(lambda { |code| eval(code, TOPLEVEL_BINDING) })
|
||||
@ -205,7 +217,7 @@ def handle_breakpoint(context, message, file = "", line = "", &block) # :nodoc:
|
||||
# These exceptions will be raised on failed asserts
|
||||
# if Breakpoint.asserts_cause_exceptions is set to
|
||||
# true.
|
||||
class FailedAssertError < RuntimeError#:nodoc:
|
||||
class FailedAssertError < RuntimeError
|
||||
end
|
||||
|
||||
# This asserts that the block evaluates to true.
|
||||
@ -279,7 +291,7 @@ def collision
|
||||
@collision_handler.call
|
||||
end
|
||||
|
||||
def ping; end
|
||||
def ping() end
|
||||
|
||||
def add_breakpoint(context, message)
|
||||
workspace = IRB::WorkSpace.new(context)
|
||||
@ -290,31 +302,7 @@ def add_breakpoint(context, message)
|
||||
@handler.call(workspace, message)
|
||||
end
|
||||
|
||||
def register_handler(&block)
|
||||
@handler = block
|
||||
end
|
||||
|
||||
def unregister_handler
|
||||
@handler = nil
|
||||
end
|
||||
|
||||
attr_reader :eval_handler
|
||||
|
||||
def register_eval_handler(&block)
|
||||
@eval_handler = block
|
||||
end
|
||||
|
||||
def unregister_eval_handler
|
||||
@eval_handler = lambda { }
|
||||
end
|
||||
|
||||
def register_collision_handler(&block)
|
||||
@collision_handler = block
|
||||
end
|
||||
|
||||
def unregister_collision_handler
|
||||
@collision_handler = lambda { }
|
||||
end
|
||||
attr_accessor :handler, :eval_handler, :collision_handler
|
||||
end
|
||||
|
||||
# Will run Breakpoint in DRb mode. This will spawn a server
|
||||
@ -359,7 +347,8 @@ def unregister_collision_handler
|
||||
#
|
||||
# Detailed information about running DRb through firewalls is
|
||||
# available at http://www.rubygarden.org/ruby?DrbTutorial
|
||||
def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'], ignore_collisions = false) #:nodoc:
|
||||
def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
|
||||
ignore_collisions = false)
|
||||
|
||||
return false if @use_drb
|
||||
|
||||
@ -402,7 +391,7 @@ def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'], i
|
||||
end
|
||||
|
||||
# Deactivates a running Breakpoint service.
|
||||
def deactivate_drb #:nodoc:
|
||||
def deactivate_drb
|
||||
@service.stop_service unless @service.nil?
|
||||
@service = nil
|
||||
@use_drb = false
|
||||
@ -411,7 +400,7 @@ def deactivate_drb #:nodoc:
|
||||
|
||||
# Returns true when Breakpoints are used over DRb.
|
||||
# Breakpoint.activate_drb causes this to be true.
|
||||
def use_drb? #:nodoc:
|
||||
def use_drb?
|
||||
@use_drb == true
|
||||
end
|
||||
end
|
||||
@ -440,7 +429,11 @@ def self.start(ap_path = nil, main_context = nil, workspace = nil)
|
||||
@CONF[:MAIN_CONTEXT] = irb.context
|
||||
|
||||
old_sigint = trap("SIGINT") do
|
||||
irb.signal_handle
|
||||
begin
|
||||
irb.signal_handle
|
||||
rescue RubyLex::TerminateLineInput
|
||||
# ignored
|
||||
end
|
||||
end
|
||||
|
||||
catch(:IRB_EXIT) do
|
||||
@ -464,7 +457,7 @@ def result.last_value; end
|
||||
end
|
||||
end
|
||||
|
||||
class Context#:nodoc:
|
||||
class Context
|
||||
alias :old_evaluate :evaluate
|
||||
def evaluate(line, line_no)
|
||||
if line.chomp == "exit" then
|
||||
@ -475,7 +468,7 @@ def evaluate(line, line_no)
|
||||
end
|
||||
end
|
||||
|
||||
class WorkSpace#:nodoc:
|
||||
class WorkSpace
|
||||
alias :old_evaluate :evaluate
|
||||
|
||||
def evaluate(*args)
|
||||
@ -493,7 +486,7 @@ def evaluate(*args)
|
||||
end
|
||||
end
|
||||
|
||||
module InputCompletor#:nodoc:
|
||||
module InputCompletor
|
||||
def self.eval(code, context, *more)
|
||||
# Big hack, this assumes that InputCompletor
|
||||
# will only call eval() when it wants code
|
||||
@ -504,9 +497,9 @@ def self.eval(code, context, *more)
|
||||
end
|
||||
|
||||
module DRb # :nodoc:
|
||||
class DRbObject#:nodoc:
|
||||
undef :inspect
|
||||
undef :clone
|
||||
class DRbObject
|
||||
undef :inspect if method_defined?(:inspect)
|
||||
undef :clone if method_defined?(:clone)
|
||||
end
|
||||
end
|
||||
|
||||
@ -522,4 +515,4 @@ def assert(&block)
|
||||
Binding.of_caller do |context|
|
||||
Breakpoint.assert(context, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -2,17 +2,18 @@
|
||||
require 'optparse'
|
||||
require 'timeout'
|
||||
|
||||
options = {
|
||||
Options = {
|
||||
:ClientURI => nil,
|
||||
:ServerURI => "druby://localhost:42531",
|
||||
:RetryDelay => 1,
|
||||
:RetryDelay => 3,
|
||||
:Permanent => true,
|
||||
:Verbose => false
|
||||
}
|
||||
|
||||
ARGV.options do |opts|
|
||||
script_name = File.basename($0)
|
||||
opts.banner = [
|
||||
"Usage: ruby #{script_name} [options] [server uri]",
|
||||
"Usage: ruby #{script_name} [Options] [server uri]",
|
||||
"",
|
||||
"This tool lets you connect to a breakpoint service ",
|
||||
"which was started via Breakpoint.activate_drb.",
|
||||
@ -29,18 +30,13 @@
|
||||
"connections from the server.",
|
||||
"Default: Find a good URI automatically.",
|
||||
"Example: -c druby://localhost:12345"
|
||||
) { |options[:ClientURI]| }
|
||||
) { |Options[:ClientURI]| }
|
||||
|
||||
opts.on("-s", "--server-uri=uri",
|
||||
"Connect to the server specified at the",
|
||||
"specified uri.",
|
||||
"Default: druby://localhost:42531"
|
||||
) { |options[:ServerURI]| }
|
||||
|
||||
opts.on("-v", "--verbose",
|
||||
"Report all connections and disconnections",
|
||||
"Default: false"
|
||||
) { |options[:Verbose]| }
|
||||
) { |Options[:ServerURI]| }
|
||||
|
||||
opts.on("-R", "--retry-delay=delay", Integer,
|
||||
"Automatically try to reconnect to the",
|
||||
@ -49,124 +45,149 @@
|
||||
"A value of 0 disables automatical",
|
||||
"reconnecting completely.",
|
||||
"Default: 10"
|
||||
) { |options[:RetryDelay]| }
|
||||
) { |Options[:RetryDelay]| }
|
||||
|
||||
opts.on("-P", "--[no-]permanent",
|
||||
"Run the breakpoint client in permanent mode.",
|
||||
"This means that the client will keep continue",
|
||||
"running even after the server has closed the",
|
||||
"connection. Useful for example in Rails."
|
||||
) { |Options[:Permanent]| }
|
||||
|
||||
opts.on("-V", "--[no-]verbose",
|
||||
"Run the breakpoint client in verbose mode.",
|
||||
"Will produce more messages, for example between",
|
||||
"individual breakpoints. This might help in seeing",
|
||||
"that the breakpoint client is still alive, but adds",
|
||||
"quite a bit of clutter."
|
||||
) { |Options[:Verbose]| }
|
||||
|
||||
opts.separator ""
|
||||
|
||||
opts.on("-h", "--help",
|
||||
"Show this help message."
|
||||
) { puts opts; exit }
|
||||
opts.on("-v", "--version",
|
||||
"Display the version information."
|
||||
) do
|
||||
id = %q$Id: breakpoint_client.rb 40 2005-01-22 20:05:00Z flgr $
|
||||
puts id.sub("Id: ", "")
|
||||
puts "(Breakpoint::Version = #{Breakpoint::Version})"
|
||||
exit
|
||||
end
|
||||
|
||||
opts.parse!
|
||||
end
|
||||
|
||||
options[:ServerURI] = ARGV[0] if ARGV[0]
|
||||
Options[:ServerURI] = ARGV[0] if ARGV[0]
|
||||
|
||||
$running = true
|
||||
module Handlers
|
||||
extend self
|
||||
|
||||
trap("INT"){$running = false}
|
||||
def breakpoint_handler(workspace, message)
|
||||
puts message
|
||||
IRB.start(nil, nil, workspace)
|
||||
|
||||
puts "Waiting for initial breakpoint..."
|
||||
puts ""
|
||||
if Options[:Verbose] then
|
||||
puts "Resumed execution. Waiting for next breakpoint...", ""
|
||||
end
|
||||
end
|
||||
|
||||
def eval_handler(code)
|
||||
result = eval(code, TOPLEVEL_BINDING)
|
||||
if result then
|
||||
DRbObject.new(result)
|
||||
else
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def collision_handler()
|
||||
msg = [
|
||||
" *** Breakpoint service collision ***",
|
||||
" Another Breakpoint service tried to use the",
|
||||
" port already occupied by this one. It will",
|
||||
" keep waiting until this Breakpoint service",
|
||||
" is shut down.",
|
||||
" ",
|
||||
" If you are using the Breakpoint library for",
|
||||
" debugging a Rails or other CGI application",
|
||||
" this likely means that this Breakpoint",
|
||||
" session belongs to an earlier, outdated",
|
||||
" request and should be shut down via 'exit'."
|
||||
].join("\n")
|
||||
|
||||
if RUBY_PLATFORM["win"] then
|
||||
# This sucks. Sorry, I'm not doing this because
|
||||
# I like funky message boxes -- I need to do this
|
||||
# because on Windows I have no way of displaying
|
||||
# my notification via puts() when gets() is still
|
||||
# being performed on STDIN. I have not found a
|
||||
# better solution.
|
||||
begin
|
||||
require 'tk'
|
||||
root = TkRoot.new { withdraw }
|
||||
Tk.messageBox('message' => msg, 'type' => 'ok')
|
||||
root.destroy
|
||||
rescue Exception
|
||||
puts "", msg, ""
|
||||
end
|
||||
else
|
||||
puts "", msg, ""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Used for checking whether we are currently in the reconnecting loop.
|
||||
reconnecting = false
|
||||
|
||||
loop do
|
||||
DRb.start_service(options[:ClientURI])
|
||||
DRb.start_service(Options[:ClientURI])
|
||||
|
||||
begin
|
||||
service = DRbObject.new(nil, options[:ServerURI])
|
||||
service = DRbObject.new(nil, Options[:ServerURI])
|
||||
|
||||
begin
|
||||
timeout(10) { service.ping }
|
||||
rescue Timeout::Error, DRb::DRbConnError
|
||||
if options[:Verbose]
|
||||
puts "",
|
||||
" *** Breakpoint service didn't respond to ping request ***",
|
||||
" This likely happened because of a misconfigured ACL (see the",
|
||||
" documentation of Breakpoint.activate_drb, note that by default",
|
||||
" you can only connect to a remote Breakpoint service via a SSH",
|
||||
" tunnel), but might also be caused by an extremely slow connection.",
|
||||
""
|
||||
end
|
||||
raise
|
||||
end
|
||||
service.eval_handler = Handlers.method(:eval_handler)
|
||||
service.collision_handler = Handlers.method(:collision_handler)
|
||||
service.handler = Handlers.method(:breakpoint_handler)
|
||||
|
||||
begin
|
||||
service.register_eval_handler do |code|
|
||||
result = eval(code, TOPLEVEL_BINDING)
|
||||
if result
|
||||
DRbObject.new(result)
|
||||
else
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
service.register_collision_handler do
|
||||
msg = [
|
||||
" *** Breakpoint service collision ***",
|
||||
" Another Breakpoint service tried to use the",
|
||||
" port already occupied by this one. It will",
|
||||
" keep waiting until this Breakpoint service",
|
||||
" is shut down.",
|
||||
" ",
|
||||
" If you are using the Breakpoint library for",
|
||||
" debugging a Rails or other CGI application",
|
||||
" this likely means that this Breakpoint",
|
||||
" session belongs to an earlier, outdated",
|
||||
" request and should be shut down via 'exit'."
|
||||
].join("\n")
|
||||
|
||||
if RUBY_PLATFORM["win"] then
|
||||
# This sucks. Sorry, I'm not doing this because
|
||||
# I like funky message boxes -- I need to do this
|
||||
# because on Windows I have no way of displaying
|
||||
# my notification via puts() when gets() is still
|
||||
# being performed on STDIN. I have not found a
|
||||
# better solution.
|
||||
begin
|
||||
require 'tk'
|
||||
root = TkRoot.new { withdraw }
|
||||
Tk.messageBox('message' => msg, 'type' => 'ok')
|
||||
root.destroy
|
||||
rescue Exception
|
||||
puts "", msg, ""
|
||||
end
|
||||
else
|
||||
puts "", msg, ""
|
||||
end
|
||||
reconnecting = false
|
||||
if Options[:Verbose] then
|
||||
puts "Connection established. Waiting for breakpoint...", ""
|
||||
end
|
||||
|
||||
service.register_handler do |workspace, message|
|
||||
puts message
|
||||
IRB.start(nil, nil, workspace)
|
||||
puts "", "Resumed execution. Waiting for next breakpoint...", ""
|
||||
end
|
||||
|
||||
puts "Connection established. Waiting for breakpoint...", "" if options[:Verbose]
|
||||
|
||||
while $running
|
||||
loop do
|
||||
begin
|
||||
service.ping
|
||||
rescue DRb::DRbConnError => error
|
||||
puts "Server exited. Closing connection..." if options[:Verbose]
|
||||
puts "Server exited. Closing connection...", ""
|
||||
exit! unless Options[:Permanent]
|
||||
break
|
||||
end
|
||||
|
||||
sleep(0.5)
|
||||
end
|
||||
ensure
|
||||
service.unregister_handler
|
||||
service.eval_handler = nil
|
||||
service.collision_handler = nil
|
||||
service.handler = nil
|
||||
end
|
||||
rescue Exception => error
|
||||
break unless $running
|
||||
if options[:RetryDelay] > 0 then
|
||||
puts "No connection to breakpoint service at #{options[:ServerURI]}:", " (#{error.inspect})" if options[:Verbose]
|
||||
error.backtrace if $DEBUG
|
||||
if Options[:RetryDelay] > 0 then
|
||||
if not reconnecting then
|
||||
reconnecting = true
|
||||
puts "No connection to breakpoint service at #{Options[:ServerURI]} " +
|
||||
"(#{error.class})"
|
||||
puts error.backtrace if $DEBUG
|
||||
puts "Tries to connect will be made every #{Options[:RetryDelay]} seconds..."
|
||||
end
|
||||
|
||||
puts " Reconnecting in #{options[:RetryDelay]} seconds..." if options[:Verbose]
|
||||
|
||||
sleep options[:RetryDelay]
|
||||
sleep Options[:RetryDelay]
|
||||
retry
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user