How to stop the Rails debugger for the current request
Asked Answered
S

9

15

Say I have a loop in my code that calls the rails debugger a few times

def show
    animals = ['dog', 'cat', 'owl', 'tiger']
    for animal in animals
    debugger
    # do something else
end

Assuming I started my server with the --debugger option, when this page is viewed, the debugger is going to stop for every run of the loop.

I can type cont every time it stops so the request continues, but that's tedious, especially if we're not talking about it showing up 4 times as in this example, but 400. Is there a way to let the debugger continue without pausing at each point of the loop?

My currently workaround is restarting the server, but that's time consuming.

Scudder answered 31/5, 2011 at 9:37 Comment(1)
"Stop from continuing to pause" is a complicating expression. I fixed it accordingly.Alyssa
J
16

Just put conditions on the debugger statement so that it stops only when you want it to, e.g.:

debugger if animal == 'tiger'

or if, say, you want to examine the code only on loop 384:

animals.each_with_index do |animal, i|
  debugger if i == 384
  # do something
end

or put in a variable that will let you continue ad hoc:

continue_debugger = false
animals.each do |animal|
  debugger unless continue_debugger
  # in the debugger type `p continue_debugger = true` then `c` when done
end
Jola answered 31/5, 2011 at 12:9 Comment(3)
this is a good workaround, but it doesn't really answer my question. Sometimes I want to inspect 1, or 2, or 10 entries before I'd like to stop debugging -- so I'm looking for a command to stop the debugging for the current request (i.e. let the page continue to load). Do you know of such a thing? Thanks!Scudder
+1 for the 'ad hoc' solution - make sure you look at the comment. Since you can set variables for your code as a whole inside the debugger session kind of as if it were an irb session, this will allow you to choose to keep going after an arbitrary number of stops.Hedveh
one corrected to the continue_debugger variable - it WILL go out of scope in certain situations, so I went ahead and tacked an @ sign ahead of it to make sure it's accessible regardless of scope. This is the easiest solution! +1Scudder
I
12

put your debugger statement somewhere before the iteration, then set a breakpoint inside the iteration which you can then clear later.

Example:

def index

  debugger

  @things = Thing.all
  @things.each do |thing|
    # ... something you want to check out in the debugger
    thing.some_calculation
  end
end

When you enter the debugger, set a breakpoint inside:

b app/controllers/things_controller.rb:42

(Where 42 is the line number you want to break at, like thing.some_calculation above. Note that it has to be an executable line of code -- comments, blank lines won't work). The debugger will show a breakpoint number and location:

Breakpoint 1 at .../app/controllers/things_controller.rb:42

Now, every time you continue, you will stop at the breakpoint. When you're done and want to complete the request, delete the breakpoint:

delete 1

continue once more, and you will complete the request!

Imhoff answered 16/6, 2011 at 20:20 Comment(1)
Thank you very much! While this is much cleaner, I found Mori's solution to be a bit faster to implement. Wish I could award everybody here the bounty!Scudder
H
5

It looks like in the source of ruby-debug, the call to debugger will always stop execution whenever it is hit. So one solution is to do as was suggested by Mori in his 'ad-hoc' solution, to make a conditional around the call to debugger that you can tweak inside the debugger session itself, such that you avoid calling debugger. This is probably the neatest solution, and what I would do unless you have some strong nagging purity issues with the code involved.

If you really want to only do this without some external conditional and inside the debugger session itself, it is possible. What you have to do is set a breakpoint in the code itself, then you can delete that breakpoint in the debugger when it is triggered:

require 'rubygems'
require 'ruby-debug'

Debugger.start
Debugger.add_breakpoint(__FILE__, __LINE__ + 2)
while true do
  puts "Hi"
  puts "mom"
end
Debugger.stop

This produces this sort of interaction:

Breakpoint 1 at debug_test.rb:10
debug_test.rb:10
puts "Hi"
(rdb:1) c
Hi
mom
Breakpoint 1 at debug_test.rb:10
debug_test.rb:10
puts "Hi"
(rdb:1) c
Hi
mom
Breakpoint 1 at debug_test.rb:10
debug_test.rb:10
puts "Hi"
(rdb:1) info b
Num Enb What
  1 y   at ./debug_test.rb:10
        breakpoint already hit 3 times
(rdb:1) del 1
(rdb:1) c
Hi
mom
Hi
mom
Hi
mom

...and so on.

In this way, you are setting the breakpoint in code, then deleting it when you are done. Note that any time the line Debugger.add_breakpoint is called, it will re-set the breakpoint, so that's why it is outside of the loop and pointing 2 lines down. This technique can easily be extracted to require-ing a script that sets the breakpoint only when loading your server - Heck, you could write a whole framework class around controlling the Debugger module however you want. Of course, if you went this far, I would just create a singleton class that helps you implement Mori's ad-hoc solution and does or does-not call the debugger statement.

Hedveh answered 17/6, 2011 at 2:17 Comment(1)
Thank you VERY much for the elaborate response! I think Mori's solution is a bit easier to implement, but I wish I could choose every answer here as the correct one. Your time is much appreciated, Matt :)Scudder
S
1

I came up with another answer to this today that I like even better:

debugger unless @no_debug

Use that on every line that has a debugger stop. When you want to stop stopping just set @no_debug to something.

Suu answered 29/4, 2012 at 23:21 Comment(0)
S
0

I have another answer to this one: set a @debug on the class that you want to debug. That way you can do:

  if (@debug && (the_condition)) then debugger end

or

  debugger unless !@debug 

then when you are done with the debugger just @debug = false and c.

However, I'm not really happy with having debugger 'hard stops' in live code. These are the kind of things that can be accidentally checked in and forgotten about until something breaks. The @debug would certainly fall under that as well. To that end I think my ideal solution would use Matt's idea and a script that sets up a breakpoint inside the object when the object is created. That way you'd have the debugging that you want but you wouldn't have any code in source control that is specifically for development. I'll update this answer if I find such a solution.

Suu answered 21/1, 2012 at 16:43 Comment(0)
I
0

You can always comment out the debugger call from your code then type reload in your debug session. Then just cont once and the request will continue without triggering a debug session.

Because you're in development mode, you can just add the debugger call back in later and it will trigger correctly.

Incomparable answered 10/8, 2012 at 21:55 Comment(0)
A
0

Putting this here as an alternative since this question showed up first in my own searches. Let's say you have a piece of code that isn't working under a specific circumstance, but works otherwise, and you have a whole slew of tests that exercise this but one specific test that fails. It's a PITA to have to continually type continue into the debug console until you get to the test you really want to debug, so I started using this convention:

In your code:

def some_common_function
  debugger if defined? ::ASDF
  # do something
end

Then in your test:

should "do this thing that it isn't doing under a specific circumstance" do
  # setup the specific situation
  ::ASDF = true
  # your tests
end
Andersonandert answered 27/5, 2014 at 18:56 Comment(0)
P
0

If I want control back, I just do

eval return

and I will exit the currently running function, which will usually kick me back to the IRB [rails console] prompt.

Pilot answered 28/10, 2016 at 10:59 Comment(0)
O
0

exit debugger out of the loop, use

exit-all

instead of

cont

Although, it will create an error and you might have to remove debugger and send request again but it will get you out of all the loops

Ottillia answered 25/7, 2019 at 5:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.