| 1 | # The Breakpoint library provides the convenience of
|
|---|
| 2 | # being able to inspect and modify state, diagnose
|
|---|
| 3 | # bugs all via IRB by simply setting breakpoints in
|
|---|
| 4 | # your applications by the call of a method.
|
|---|
| 5 | #
|
|---|
| 6 | # This library was written and is supported by me,
|
|---|
| 7 | # Florian Gross. I can be reached at flgr@ccan.de
|
|---|
| 8 | # and enjoy getting feedback about my libraries.
|
|---|
| 9 | #
|
|---|
| 10 | # The whole library (including breakpoint_client.rb
|
|---|
| 11 | # and binding_of_caller.rb) is licensed under the
|
|---|
| 12 | # same license that Ruby uses. (Which is currently
|
|---|
| 13 | # either the GNU General Public License or a custom
|
|---|
| 14 | # one that allows for commercial usage.) If you for
|
|---|
| 15 | # some good reason need to use this under another
|
|---|
| 16 | # license please contact me.
|
|---|
| 17 |
|
|---|
| 18 | require 'irb'
|
|---|
| 19 | require 'binding_of_caller'
|
|---|
| 20 | require 'drb'
|
|---|
| 21 | require 'drb/acl'
|
|---|
| 22 |
|
|---|
| 23 | module Breakpoint
|
|---|
| 24 | id = %q$Id: breakpoint.rb 92 2005-02-04 22:35:53Z flgr $
|
|---|
| 25 | Version = id.split(" ")[2].to_i
|
|---|
| 26 |
|
|---|
| 27 | extend self
|
|---|
| 28 |
|
|---|
| 29 | # This will pop up an interactive ruby session at a
|
|---|
| 30 | # pre-defined break point in a Ruby application. In
|
|---|
| 31 | # this session you can examine the environment of
|
|---|
| 32 | # the break point.
|
|---|
| 33 | #
|
|---|
| 34 | # You can get a list of variables in the context using
|
|---|
| 35 | # local_variables via +local_variables+. You can then
|
|---|
| 36 | # examine their values by typing their names.
|
|---|
| 37 | #
|
|---|
| 38 | # You can have a look at the call stack via +caller+.
|
|---|
| 39 | #
|
|---|
| 40 | # The source code around the location where the breakpoint
|
|---|
| 41 | # was executed can be examined via +source_lines+. Its
|
|---|
| 42 | # argument specifies how much lines of context to display.
|
|---|
| 43 | # The default amount of context is 5 lines. Note that
|
|---|
| 44 | # the call to +source_lines+ can raise an exception when
|
|---|
| 45 | # it isn't able to read in the source code.
|
|---|
| 46 | #
|
|---|
| 47 | # breakpoints can also return a value. They will execute
|
|---|
| 48 | # a supplied block for getting a default return value.
|
|---|
| 49 | # A custom value can be returned from the session by doing
|
|---|
| 50 | # +throw(:debug_return, value)+.
|
|---|
| 51 | #
|
|---|
| 52 | # You can also give names to break points which will be
|
|---|
| 53 | # used in the message that is displayed upon execution
|
|---|
| 54 | # of them.
|
|---|
| 55 | #
|
|---|
| 56 | # Here's a sample of how breakpoints should be placed:
|
|---|
| 57 | #
|
|---|
| 58 | # class Person
|
|---|
| 59 | # def initialize(name, age)
|
|---|
| 60 | # @name, @age = name, age
|
|---|
| 61 | # breakpoint("Person#initialize")
|
|---|
| 62 | # end
|
|---|
| 63 | #
|
|---|
| 64 | # attr_reader :age
|
|---|
| 65 | # def name
|
|---|
| 66 | # breakpoint("Person#name") { @name }
|
|---|
| 67 | # end
|
|---|
| 68 | # end
|
|---|
| 69 | #
|
|---|
| 70 | # person = Person.new("Random Person", 23)
|
|---|
| 71 | # puts "Name: #{person.name}"
|
|---|
| 72 | #
|
|---|
| 73 | # And here is a sample debug session:
|
|---|
| 74 | #
|
|---|
| 75 | # Executing break point "Person#initialize" at file.rb:4 in `initialize'
|
|---|
| 76 | # irb(#<Person:0x292fbe8>):001:0> local_variables
|
|---|
| 77 | # => ["name", "age", "_", "__"]
|
|---|
| 78 | # irb(#<Person:0x292fbe8>):002:0> [name, age]
|
|---|
| 79 | # => ["Random Person", 23]
|
|---|
| 80 | # irb(#<Person:0x292fbe8>):003:0> [@name, @age]
|
|---|
| 81 | # => ["Random Person", 23]
|
|---|
| 82 | # irb(#<Person:0x292fbe8>):004:0> self
|
|---|
| 83 | # => #<Person:0x292fbe8 @age=23, @name="Random Person">
|
|---|
| 84 | # irb(#<Person:0x292fbe8>):005:0> @age += 1; self
|
|---|
| 85 | # => #<Person:0x292fbe8 @age=24, @name="Random Person">
|
|---|
| 86 | # irb(#<Person:0x292fbe8>):006:0> exit
|
|---|
| 87 | # Executing break point "Person#name" at file.rb:9 in `name'
|
|---|
| 88 | # irb(#<Person:0x292fbe8>):001:0> throw(:debug_return, "Overriden name")
|
|---|
| 89 | # Name: Overriden name
|
|---|
| 90 | #
|
|---|
| 91 | # Breakpoint sessions will automatically have a few
|
|---|
| 92 | # convenience methods available. See Breakpoint::CommandBundle
|
|---|
| 93 | # for a list of them.
|
|---|
| 94 | #
|
|---|
| 95 | # Breakpoints can also be used remotely over sockets.
|
|---|
| 96 | # This is implemented by running part of the IRB session
|
|---|
| 97 | # in the application and part of it in a special client.
|
|---|
| 98 | # You have to call Breakpoint.activate_drb to enable
|
|---|
| 99 | # support for remote breakpoints and then run
|
|---|
| 100 | # breakpoint_client.rb which is distributed with this
|
|---|
| 101 | # library. See the documentation of Breakpoint.activate_drb
|
|---|
| 102 | # for details.
|
|---|
| 103 | def breakpoint(id = nil, context = nil, &block)
|
|---|
| 104 | callstack = caller
|
|---|
| 105 | callstack.slice!(0, 3) if callstack.first["breakpoint"]
|
|---|
| 106 | file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
|
|---|
| 107 |
|
|---|
| 108 | message = "Executing break point " + (id ? "#{id.inspect} " : "") +
|
|---|
| 109 | "at #{file}:#{line}" + (method ? " in `#{method}'" : "")
|
|---|
| 110 |
|
|---|
| 111 | if context then
|
|---|
| 112 | return handle_breakpoint(context, message, file, line, &block)
|
|---|
| 113 | end
|
|---|
| 114 |
|
|---|
| 115 | Binding.of_caller do |binding_context|
|
|---|
| 116 | handle_breakpoint(binding_context, message, file, line, &block)
|
|---|
| 117 | end
|
|---|
| 118 | end
|
|---|
| 119 |
|
|---|
| 120 | module CommandBundle
|
|---|
| 121 | # Proxy to a Breakpoint client. Lets you directly execute code
|
|---|
| 122 | # in the context of the client.
|
|---|
| 123 | class Client
|
|---|
| 124 | def initialize(eval_handler) # :nodoc:
|
|---|
| 125 | eval_handler.untaint
|
|---|
| 126 | @eval_handler = eval_handler
|
|---|
| 127 | end
|
|---|
| 128 |
|
|---|
| 129 | instance_methods.each do |method|
|
|---|
| 130 | next if method[/^__.+__$/]
|
|---|
| 131 | undef_method method
|
|---|
| 132 | end
|
|---|
| 133 |
|
|---|
| 134 | # Executes the specified code at the client.
|
|---|
| 135 | def eval(code)
|
|---|
| 136 | @eval_handler.call(code)
|
|---|
| 137 | end
|
|---|
| 138 |
|
|---|
| 139 | # Will execute the specified statement at the client.
|
|---|
| 140 | def method_missing(method, *args, &block)
|
|---|
| 141 | if args.empty? and not block
|
|---|
| 142 | result = eval "#{method}"
|
|---|
| 143 | else
|
|---|
| 144 | # This is a bit ugly. The alternative would be using an
|
|---|
| 145 | # eval context instead of an eval handler for executing
|
|---|
| 146 | # the code at the client. The problem with that approach
|
|---|
| 147 | # is that we would have to handle special expressions
|
|---|
| 148 | # like "self", "nil" or constants ourself which is hard.
|
|---|
| 149 | remote = eval %{
|
|---|
| 150 | result = lambda { |block, *args| #{method}(*args, &block) }
|
|---|
| 151 | def result.call_with_block(*args, &block)
|
|---|
| 152 | call(block, *args)
|
|---|
| 153 | end
|
|---|
| 154 | result
|
|---|
| 155 | }
|
|---|
| 156 | remote.call_with_block(*args, &block)
|
|---|
| 157 | end
|
|---|
| 158 |
|
|---|
| 159 | return result
|
|---|
| 160 | end
|
|---|
| 161 | end
|
|---|
| 162 |
|
|---|
| 163 | # Returns the source code surrounding the location where the
|
|---|
| 164 | # breakpoint was issued.
|
|---|
| 165 | def source_lines(context = 5, return_line_numbers = false)
|
|---|
| 166 | lines = File.readlines(@__bp_file).map { |line| line.chomp }
|
|---|
| 167 |
|
|---|
| 168 | break_line = @__bp_line
|
|---|
| 169 | start_line = [break_line - context, 1].max
|
|---|
| 170 | end_line = break_line + context
|
|---|
| 171 |
|
|---|
| 172 | result = lines[(start_line - 1) .. (end_line - 1)]
|
|---|
| 173 |
|
|---|
| 174 | if return_line_numbers then
|
|---|
| 175 | return [start_line, break_line, result]
|
|---|
| 176 | else
|
|---|
| 177 | return result
|
|---|
| 178 | end
|
|---|
| 179 | end
|
|---|
| 180 |
|
|---|
| 181 | # Lets an object that will forward method calls to the breakpoint
|
|---|
| 182 | # client. This is useful for outputting longer things at the client
|
|---|
| 183 | # and so on. You can for example do these things:
|
|---|
| 184 | #
|
|---|
| 185 | # client.puts "Hello" # outputs "Hello" at client console
|
|---|
| 186 | # # outputs "Hello" into the file temp.txt at the client
|
|---|
| 187 | # client.File.open("temp.txt", "w") { |f| f.puts "Hello" }
|
|---|
| 188 | def client()
|
|---|
| 189 | if Breakpoint.use_drb? then
|
|---|
| 190 | sleep(0.5) until Breakpoint.drb_service.eval_handler
|
|---|
| 191 | Client.new(Breakpoint.drb_service.eval_handler)
|
|---|
| 192 | else
|
|---|
| 193 | Client.new(lambda { |code| eval(code, TOPLEVEL_BINDING) })
|
|---|
| 194 | end
|
|---|
| 195 | end
|
|---|
| 196 | end
|
|---|
| 197 |
|
|---|
| 198 | def handle_breakpoint(context, message, file = "", line = "", &block) # :nodoc:
|
|---|
| 199 | catch(:debug_return) do |value|
|
|---|
| 200 | eval(%{
|
|---|
| 201 | @__bp_file = #{file.inspect}
|
|---|
| 202 | @__bp_line = #{line}
|
|---|
| 203 | extend Breakpoint::CommandBundle
|
|---|
| 204 | extend DRbUndumped if self
|
|---|
| 205 | }, context) rescue nil
|
|---|
| 206 |
|
|---|
| 207 | if not use_drb? then
|
|---|
| 208 | puts message
|
|---|
| 209 | IRB.start(nil, IRB::WorkSpace.new(context))
|
|---|
| 210 | else
|
|---|
| 211 | @drb_service.add_breakpoint(context, message)
|
|---|
| 212 | end
|
|---|
| 213 |
|
|---|
| 214 | block.call if block
|
|---|
| 215 | end
|
|---|
| 216 | end
|
|---|
| 217 |
|
|---|
| 218 | # These exceptions will be raised on failed asserts
|
|---|
| 219 | # if Breakpoint.asserts_cause_exceptions is set to
|
|---|
| 220 | # true.
|
|---|
| 221 | class FailedAssertError < RuntimeError
|
|---|
| 222 | end
|
|---|
| 223 |
|
|---|
| 224 | # This asserts that the block evaluates to true.
|
|---|
| 225 | # If it doesn't evaluate to true a breakpoint will
|
|---|
| 226 | # automatically be created at that execution point.
|
|---|
| 227 | #
|
|---|
| 228 | # You can disable assert checking in production
|
|---|
| 229 | # code by setting Breakpoint.optimize_asserts to
|
|---|
| 230 | # true. (It will still be enabled when Ruby is run
|
|---|
| 231 | # via the -d argument.)
|
|---|
| 232 | #
|
|---|
| 233 | # Example:
|
|---|
| 234 | # person_name = "Foobar"
|
|---|
| 235 | # assert { not person_name.nil? }
|
|---|
| 236 | #
|
|---|
| 237 | # Note: If you want to use this method from an
|
|---|
| 238 | # unit test, you will have to call it by its full
|
|---|
| 239 | # name, Breakpoint.assert.
|
|---|
| 240 | def assert(context = nil, &condition)
|
|---|
| 241 | return if Breakpoint.optimize_asserts and not $DEBUG
|
|---|
| 242 | return if yield
|
|---|
| 243 |
|
|---|
| 244 | callstack = caller
|
|---|
| 245 | callstack.slice!(0, 3) if callstack.first["assert"]
|
|---|
| 246 | file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
|
|---|
| 247 |
|
|---|
| 248 | message = "Assert failed at #{file}:#{line}#{" in `#{method}'" if method}."
|
|---|
| 249 |
|
|---|
| 250 | if Breakpoint.asserts_cause_exceptions and not $DEBUG then
|
|---|
| 251 | raise(Breakpoint::FailedAssertError, message)
|
|---|
| 252 | end
|
|---|
| 253 |
|
|---|
| 254 | message += " Executing implicit breakpoint."
|
|---|
| 255 |
|
|---|
| 256 | if context then
|
|---|
| 257 | return handle_breakpoint(context, message, file, line)
|
|---|
| 258 | end
|
|---|
| 259 |
|
|---|
| 260 | Binding.of_caller do |context|
|
|---|
| 261 | handle_breakpoint(context, message, file, line)
|
|---|
| 262 | end
|
|---|
| 263 | end
|
|---|
| 264 |
|
|---|
| 265 | # Whether asserts should be ignored if not in debug mode.
|
|---|
| 266 | # Debug mode can be enabled by running ruby with the -d
|
|---|
| 267 | # switch or by setting $DEBUG to true.
|
|---|
| 268 | attr_accessor :optimize_asserts
|
|---|
| 269 | self.optimize_asserts = false
|
|---|
| 270 |
|
|---|
| 271 | # Whether an Exception should be raised on failed asserts
|
|---|
| 272 | # in non-$DEBUG code or not. By default this is disabled.
|
|---|
| 273 | attr_accessor :asserts_cause_exceptions
|
|---|
| 274 | self.asserts_cause_exceptions = false
|
|---|
| 275 | @use_drb = false
|
|---|
| 276 |
|
|---|
| 277 | attr_reader :drb_service # :nodoc:
|
|---|
| 278 |
|
|---|
| 279 | class DRbService # :nodoc:
|
|---|
| 280 | include DRbUndumped
|
|---|
| 281 |
|
|---|
| 282 | def initialize
|
|---|
| 283 | @handler = @eval_handler = @collision_handler = nil
|
|---|
| 284 |
|
|---|
| 285 | IRB.instance_eval { @CONF[:RC] = true }
|
|---|
| 286 | IRB.run_config
|
|---|
| 287 | end
|
|---|
| 288 |
|
|---|
| 289 | def collision
|
|---|
| 290 | sleep(0.5) until @collision_handler
|
|---|
| 291 |
|
|---|
| 292 | @collision_handler.untaint
|
|---|
| 293 |
|
|---|
| 294 | @collision_handler.call
|
|---|
| 295 | end
|
|---|
| 296 |
|
|---|
| 297 | def ping() end
|
|---|
| 298 |
|
|---|
| 299 | def add_breakpoint(context, message)
|
|---|
| 300 | workspace = IRB::WorkSpace.new(context)
|
|---|
| 301 | workspace.extend(DRbUndumped)
|
|---|
| 302 |
|
|---|
| 303 | sleep(0.5) until @handler
|
|---|
| 304 |
|
|---|
| 305 | @handler.untaint
|
|---|
| 306 | @handler.call(workspace, message)
|
|---|
| 307 | end
|
|---|
| 308 |
|
|---|
| 309 | attr_accessor :handler, :eval_handler, :collision_handler
|
|---|
| 310 | end
|
|---|
| 311 |
|
|---|
| 312 | # Will run Breakpoint in DRb mode. This will spawn a server
|
|---|
| 313 | # that can be attached to via the breakpoint-client command
|
|---|
| 314 | # whenever a breakpoint is executed. This is useful when you
|
|---|
| 315 | # are debugging CGI applications or other applications where
|
|---|
| 316 | # you can't access debug sessions via the standard input and
|
|---|
| 317 | # output of your application.
|
|---|
| 318 | #
|
|---|
| 319 | # You can specify an URI where the DRb server will run at.
|
|---|
| 320 | # This way you can specify the port the server runs on. The
|
|---|
| 321 | # default URI is druby://localhost:42531.
|
|---|
| 322 | #
|
|---|
| 323 | # Please note that breakpoints will be skipped silently in
|
|---|
| 324 | # case the DRb server can not spawned. (This can happen if
|
|---|
| 325 | # the port is already used by another instance of your
|
|---|
| 326 | # application on CGI or another application.)
|
|---|
| 327 | #
|
|---|
| 328 | # Also note that by default this will only allow access
|
|---|
| 329 | # from localhost. You can however specify a list of
|
|---|
| 330 | # allowed hosts or nil (to allow access from everywhere).
|
|---|
| 331 | # But that will still not protect you from somebody
|
|---|
| 332 | # reading the data as it goes through the net.
|
|---|
| 333 | #
|
|---|
| 334 | # A good approach for getting security and remote access
|
|---|
| 335 | # is setting up an SSH tunnel between the DRb service
|
|---|
| 336 | # and the client. This is usually done like this:
|
|---|
| 337 | #
|
|---|
| 338 | # $ ssh -L20000:127.0.0.1:20000 -R10000:127.0.0.1:10000 example.com
|
|---|
| 339 | # (This will connect port 20000 at the client side to port
|
|---|
| 340 | # 20000 at the server side, and port 10000 at the server
|
|---|
| 341 | # side to port 10000 at the client side.)
|
|---|
| 342 | #
|
|---|
| 343 | # After that do this on the server side: (the code being debugged)
|
|---|
| 344 | # Breakpoint.activate_drb("druby://127.0.0.1:20000", "localhost")
|
|---|
| 345 | #
|
|---|
| 346 | # And at the client side:
|
|---|
| 347 | # ruby breakpoint_client.rb -c druby://127.0.0.1:10000 -s druby://127.0.0.1:20000
|
|---|
| 348 | #
|
|---|
| 349 | # Running through such a SSH proxy will also let you use
|
|---|
| 350 | # breakpoint.rb in case you are behind a firewall.
|
|---|
| 351 | #
|
|---|
| 352 | # Detailed information about running DRb through firewalls is
|
|---|
| 353 | # available at http://www.rubygarden.org/ruby?DrbTutorial
|
|---|
| 354 | def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
|
|---|
| 355 | ignore_collisions = false)
|
|---|
| 356 |
|
|---|
| 357 | return false if @use_drb
|
|---|
| 358 |
|
|---|
| 359 | uri ||= 'druby://localhost:42531'
|
|---|
| 360 |
|
|---|
| 361 | if allowed_hosts then
|
|---|
| 362 | acl = ["deny", "all"]
|
|---|
| 363 |
|
|---|
| 364 | Array(allowed_hosts).each do |host|
|
|---|
| 365 | acl += ["allow", host]
|
|---|
| 366 | end
|
|---|
| 367 |
|
|---|
| 368 | DRb.install_acl(ACL.new(acl))
|
|---|
| 369 | end
|
|---|
| 370 |
|
|---|
| 371 | @use_drb = true
|
|---|
| 372 | @drb_service = DRbService.new
|
|---|
| 373 | did_collision = false
|
|---|
| 374 | begin
|
|---|
| 375 | @service = DRb.start_service(uri, @drb_service)
|
|---|
| 376 | rescue Errno::EADDRINUSE
|
|---|
| 377 | if ignore_collisions then
|
|---|
| 378 | nil
|
|---|
| 379 | else
|
|---|
| 380 | # The port is already occupied by another
|
|---|
| 381 | # Breakpoint service. We will try to tell
|
|---|
| 382 | # the old service that we want its port.
|
|---|
| 383 | # It will then forward that request to the
|
|---|
| 384 | # user and retry.
|
|---|
| 385 | unless did_collision then
|
|---|
| 386 | DRbObject.new(nil, uri).collision
|
|---|
| 387 | did_collision = true
|
|---|
| 388 | end
|
|---|
| 389 | sleep(10)
|
|---|
| 390 | retry
|
|---|
| 391 | end
|
|---|
| 392 | end
|
|---|
| 393 |
|
|---|
| 394 | return true
|
|---|
| 395 | end
|
|---|
| 396 |
|
|---|
| 397 | # Deactivates a running Breakpoint service.
|
|---|
| 398 | def deactivate_drb
|
|---|
| 399 | @service.stop_service unless @service.nil?
|
|---|
| 400 | @service = nil
|
|---|
| 401 | @use_drb = false
|
|---|
| 402 | @drb_service = nil
|
|---|
| 403 | end
|
|---|
| 404 |
|
|---|
| 405 | # Returns true when Breakpoints are used over DRb.
|
|---|
| 406 | # Breakpoint.activate_drb causes this to be true.
|
|---|
| 407 | def use_drb?
|
|---|
| 408 | @use_drb == true
|
|---|
| 409 | end
|
|---|
| 410 | end
|
|---|
| 411 |
|
|---|
| 412 | module IRB # :nodoc:
|
|---|
| 413 | class << self; remove_method :start; end
|
|---|
| 414 | def self.start(ap_path = nil, main_context = nil, workspace = nil)
|
|---|
| 415 | $0 = File::basename(ap_path, ".rb") if ap_path
|
|---|
| 416 |
|
|---|
| 417 | # suppress some warnings about redefined constants
|
|---|
| 418 | old_verbose, $VERBOSE = $VERBOSE, nil
|
|---|
| 419 | IRB.setup(ap_path)
|
|---|
| 420 | $VERBOSE = old_verbose
|
|---|
| 421 |
|
|---|
| 422 | if @CONF[:SCRIPT] then
|
|---|
| 423 | irb = Irb.new(main_context, @CONF[:SCRIPT])
|
|---|
| 424 | else
|
|---|
| 425 | irb = Irb.new(main_context)
|
|---|
| 426 | end
|
|---|
| 427 |
|
|---|
| 428 | if workspace then
|
|---|
| 429 | irb.context.workspace = workspace
|
|---|
| 430 | end
|
|---|
| 431 |
|
|---|
| 432 | @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
|---|
| 433 | @CONF[:MAIN_CONTEXT] = irb.context
|
|---|
| 434 |
|
|---|
| 435 | old_sigint = trap("SIGINT") do
|
|---|
| 436 | begin
|
|---|
| 437 | irb.signal_handle
|
|---|
| 438 | rescue RubyLex::TerminateLineInput
|
|---|
| 439 | # ignored
|
|---|
| 440 | end
|
|---|
| 441 | end
|
|---|
| 442 |
|
|---|
| 443 | catch(:IRB_EXIT) do
|
|---|
| 444 | irb.eval_input
|
|---|
| 445 | end
|
|---|
| 446 | ensure
|
|---|
| 447 | trap("SIGINT", old_sigint)
|
|---|
| 448 | end
|
|---|
| 449 |
|
|---|
| 450 | class << self
|
|---|
| 451 | alias :old_CurrentContext :CurrentContext
|
|---|
| 452 | remove_method :CurrentContext
|
|---|
| 453 | end
|
|---|
| 454 | def IRB.CurrentContext
|
|---|
| 455 | if old_CurrentContext.nil? and Breakpoint.use_drb? then
|
|---|
| 456 | result = Object.new
|
|---|
| 457 | def result.last_value; end
|
|---|
| 458 | return result
|
|---|
| 459 | else
|
|---|
| 460 | old_CurrentContext
|
|---|
| 461 | end
|
|---|
| 462 | end
|
|---|
| 463 | def IRB.parse_opts() end
|
|---|
| 464 |
|
|---|
| 465 | class Context #:nodoc:
|
|---|
| 466 | alias :old_evaluate :evaluate
|
|---|
| 467 | def evaluate(line, line_no)
|
|---|
| 468 | if line.chomp == "exit" then
|
|---|
| 469 | exit
|
|---|
| 470 | else
|
|---|
| 471 | old_evaluate(line, line_no)
|
|---|
| 472 | end
|
|---|
| 473 | end
|
|---|
| 474 | end
|
|---|
| 475 |
|
|---|
| 476 | class WorkSpace #:nodoc:
|
|---|
| 477 | alias :old_evaluate :evaluate
|
|---|
| 478 |
|
|---|
| 479 | def evaluate(*args)
|
|---|
| 480 | if Breakpoint.use_drb? then
|
|---|
| 481 | result = old_evaluate(*args)
|
|---|
| 482 | if args[0] != :no_proxy and
|
|---|
| 483 | not [true, false, nil].include?(result)
|
|---|
| 484 | then
|
|---|
| 485 | result.extend(DRbUndumped) rescue nil
|
|---|
| 486 | end
|
|---|
| 487 | return result
|
|---|
| 488 | else
|
|---|
| 489 | old_evaluate(*args)
|
|---|
| 490 | end
|
|---|
| 491 | end
|
|---|
| 492 | end
|
|---|
| 493 |
|
|---|
| 494 | module InputCompletor #:nodoc:
|
|---|
| 495 | def self.eval(code, context, *more)
|
|---|
| 496 | # Big hack, this assumes that InputCompletor
|
|---|
| 497 | # will only call eval() when it wants code
|
|---|
| 498 | # to be executed in the IRB context.
|
|---|
| 499 | IRB.conf[:MAIN_CONTEXT].workspace.evaluate(:no_proxy, code, *more)
|
|---|
| 500 | end
|
|---|
| 501 | end
|
|---|
| 502 | end
|
|---|
| 503 |
|
|---|
| 504 | module DRb #:nodoc:
|
|---|
| 505 | class DRbObject #:nodoc:
|
|---|
| 506 | undef :inspect if method_defined?(:inspect)
|
|---|
| 507 | undef :clone if method_defined?(:clone)
|
|---|
| 508 | end
|
|---|
| 509 | end
|
|---|
| 510 |
|
|---|
| 511 | # See Breakpoint.breakpoint
|
|---|
| 512 | def breakpoint(id = nil, &block)
|
|---|
| 513 | Binding.of_caller do |context|
|
|---|
| 514 | Breakpoint.breakpoint(id, context, &block)
|
|---|
| 515 | end
|
|---|
| 516 | end
|
|---|
| 517 |
|
|---|
| 518 | # See Breakpoint.assert
|
|---|
| 519 | def assert(&block)
|
|---|
| 520 | Binding.of_caller do |context|
|
|---|
| 521 | Breakpoint.assert(context, &block)
|
|---|
| 522 | end
|
|---|
| 523 | end
|
|---|