PryDebug is a pure-ruby debugger, It simply relies on
uses Pry (an alternative to IRB) to evaluate code on breakpoints. This means you
have complete access to local variables (you can even change them!), and can get
any information you want using methods and Pry's commands (find out the value of
an instance variable, for example).
If you wonder why using a debugger: how often have you written things like this?
puts "HERE!!!" # add as many variables as needed until you find the source of the issue. p :foo => foo, :bar => bar, :baz => baz, :self => self
Adding a breakpoint on that line would tell you whether it was executed and let you see the value of all those variables and all the other without running your code again.
gem install pry_debug
PryDebug works has been tested with CRuby 1.9.2 and 1.8.7, and works with some glitches in JRuby.
Just use the
pry_debug executable, configure the debugger (add breakpoints,
...) and type
$ pry_debug file.rb debugged file set to test_debug.rb pry(main)> b Foo.foo added breakpoint 0 at Foo.foo pry(main)> r reached breakpoint 0 at Foo.foo From: /home/kilian/code/pry_debug/test_debug.rb @ line 2 in Class#foo: 1: class Foo => 2: def self.foo 3: a, b, c = 1, 2, 3 4: @foo = a + b + c 5: end 6: 7: def initialize(name) pry(Foo):1> n stepped at /home/kilian/code/pry_debug/test_debug.rb:3 From: /home/kilian/code/pry_debug/test_debug.rb @ line 3 in Class#foo: 1: class Foo 2: def self.foo => 3: a, b, c = 1, 2, 3 4: @foo = a + b + c 5: end 6: 7: def initialize(name) 8: @name = name pry(Foo):2> n stepped at /home/kilian/code/pry_debug/test_debug.rb:4 From: /home/kilian/code/pry_debug/test_debug.rb @ line 4 in Class#foo: 1: class Foo 2: def self.foo 3: a, b, c = 1, 2, 3 => 4: @foo = a + b + c 5: end 6: 7: def initialize(name) 8: @name = name 9: end pry(Foo):3> p a 1 => 1 pry(Foo):3> ls -i Instance variables: 
That's quite an important feature: just
b file.rb:line to break whenever
that line gets executed. Notice you can pass file.rb, path/to/file.rb, or
/full/path/to/file.rb to refer to the same file.
It will run Pry once the breakpoint is reached. When you're done, just type
c to continue.
Breakpoints can be listed and removed whenever Pry is running:
pry(Foo):3> b test_debug.rb:10 added breakpoint 1 at test_debug.rb:10 pry(Foo):3> bl breakpoint 0 at Foo.foo breakpoint 1 at test_debug.rb:10 pry(Foo):3> d 0 breakpoint 0 deleted pry(Foo):3> bl breakpoint 1 at test_debug.rb:10
(You can use breakpoint instead of b, breakpoint-list instead of bl, delete or del instead of d, and continue instead of c; I'm sure most people can't stand typing the full command names, though)
Often, you just want to break when a method gets called. Surely you can find the location of a method in your code most of the time, but that won't work for C-defined methods, and it is more work than just typing the name of the method, isn't it?
pry(Foo):3> b SomeClass#some_method added breakpoint 2 at SomeClass#some_method
SomeClass#some_method is used to break on the instance method
the class SomeClass. Both
SomeClass::some_method will break on a class method.
Note that, due to implementation details, it is currently hard for PryDebug to identify that some call to Class#new was in fact a call to Foo.new. It still can find what was called in most other cases.
You may want a breakpoint to be run only when a particular condition is met. You
can add a condition to the breakpoint using
cond breakpoint_id some code,
where some code will get run every time the breakpoint is reached. If it
evaluated to nil or false, then PryDebug won't actually stop at that
point. Conditions can also be removed using
pry(Foo):3> b foo.rb:15 added breakpoint 3 at foo.rb:15 pry(Foo):3> cond 3 @array.size > 10 condition set to @array.size > 10 pry(Foo):3> bl breakpoint 3 at foo.rb:15 (if @array.size > 10)
(Beware, exceptions in conditions are silently ignored)
Breaking at a given place is sometimes not enough. Sometimes, it is useful to
execute each line to find out what changed. That's what the
step command is for:
it makes the debugger execute code until the next line before breaking. You can
step instead of
run. It will then break on the first line of
next is a similar command, that will break on the next line in the same
file (though it would ideally break on the next line in the same method instead
of stepping into it if it is defined in the same file).
You don't need to do anything to benefit from this feature: whenever an exception isn't rescued, PryDebug will rescue it and bring you back to the place where it occured so you can find out why it was raised:
unrescued exception: RuntimeError: foo returning back to where the exception was raised From: /home/kilian/code/pry_debug/test_debug.rb @ line 22 in Object#N/A: 17: 18: 100.times.map do |n| 19: n * 2 20: end 21: => 22: raise "foo" 23: 24: __END__ 25: b Foo.foo 26: b Class#inherted 27: b FooBar.jump pry(main):3>
This is disabled by default because it is annoying in code where an exception
being raised and then rescued is normal. It can still be helpful: once enabled,
raising an exception causes PryDebug to stop. This can be toggled using the
pry(main):4> bor break on raise enabled pry(main):4> r exception raised: RuntimeError: foo From: /home/kilian/code/pry_debug/test_debug.rb @ line 22 in Object#N/A: 17: 18: 100.times.map do |n| 19: n * 2 20: end 21: => 22: raise "foo" 23: 24: __END__ 25: b Foo.foo 26: b Class#inherted 27: b FooBar.jump pry(main):5>
Instead of starting your program from PryDebug, you can start PryDebug from your program. In this case, it won't handle unrescued exceptions automatically, though.
require 'pry_debug' PryDebug.start(false) # true makes PryDebug load its own file # you can make it handle exceptions yourself (or break-on-raise instead): begin # ... rescue SystemExit # nothing rescue Exception => ex # PryDebug can still be started if binding = PryDebug.context_of_exception(ex) PryDebug.start_pry binding else PryDebug.start_pry ex end end
It is completely possible you will want to use PryDebug in code that uses Thread. PryDebug will ensure that only one thread uses Pry. It will also keep information about breakpoints and stepping on a thread basis, to avoid unexpected and undetermined results.