Article
Debug Your Rails App With ruby-debug
Debugging an Application
To see our debugger in action, we'll take a look at a problem that I've secretly introduced to the application's code. If you look at Figure 3, you'll notice that although we've provided a description for the new story we submitted, it doesn't show up on the final story page. If you've read the book, you'll notice that this example is identical to the example in Chapter 11; instead of solving it with breakpointer, we'll use ruby-debug.
![]()
So, let's crack the ruby-debug whip at this problem. First, add the debugger keyword to the new action in app/controllers/story_controller.rb, like so:
def new
@story = Story.new(params[:story])
@story.user = @current_user
if request.post? and @story.save
debugger
@story.tag_with params[:tags] if params[:tags]
flash[:notice] = "Story submission succeeded"
redirect_to :action => 'index'
end
end
As was the case when we used breakpointer on this problem, when you try to submit a story, you'll now experience the "hanging browser syndrome", which indicates that your debugger statement has kicked in and you're ready to debug.
Instead of firing up a separate client to connect to the inner workings of your application, ruby-debug has opened this debugger shell right inside the terminal window in which you've fired up your application server, as shown in Figure 4.
![]()
From this prompt you can explore your application while it's paused mid-execution, using a variety of commands. Throughout this example, I'll indicate the ruby-debug shell prompt using the characters (rdb), and commands typed at this prompt will appear in bold, as follows:
(rdb) list
The ruby-debug Commands
What follows is a quick rundown of the most important ruby-debug commands, along with a brief description of what they do. Don't worry too much about remembering every last detail -- the built-in help command will list all the available commands for you. You can also use the help <commandname> syntax to get help with a specific command.
backtrace: Display a trace of the execution stack, similar to what is displayed when your application raises an exception.break/delete: Display a list of breakpoints that have been set in your application. This command is also used to set new breakpoints, or delete existing ones, from within the ruby-debug shell.cont: Leave the current debugger shell and resume execution of the application until the next breakpoint is encountered.irb: Invoke an interactive Ruby interpreter at the current point of execution, similar to the shell used by the breakpoint library.list: Display the code fragments surrounding the current point of execution. (We'll make use of this command in a moment.)method/method instance: Explore the available class methods and instance methods, respectively.next/step: Continue execution one step at a time -- this is a huge improvement over the breakpoint library.p/pp: Short for print and pretty print respectively, these commands can be used to evaluate Ruby expressions and display the value of variables to the console.quit: Exit the debugger. Note that this will also exit the application server if it was invoked from the command line, as demonstrated above. To just exit the current debugging session, usecont.reload: Reload the Ruby source files from disk. This can be useful if you've changed class definitions and want to reload them dynamically without leaving the current debugging session.
For a list of all available commands and options, use the help command.
Moving Around in the Shell
Now that we've been dropped into a shell, it's time to make use of some of the commands we just discussed to get to the root of our problem (which is that our stories are ending up without descriptions).
First of all, let's find out exactly where we are in the execution of our story submission. This is the job of the list command, as shown in Figure 5.
![]()
As you can see, the list command displays a source code listing with an arrow pointing to the line of code that is next to be executed.
At this point, we can examine parts of the working environment from the irb shell, such as the @story instance variable or the params hash. Type irb at the prompt, and let's investigate the description attribute of the story object that's stored in our @story variable:
(rdb) irb
irb> @story.description
=> nil
The output of inspecting this variable is shown in Figure 6.
![]()
As you can see, even though we've entered a beautifully phrased story description into the form, the relevant description attribute of the new story object is nil, or empty.
But hang on a minute! Isn't there a command in ruby-debug that allows us to evaluate Ruby expressions and inspect variables without going through the hassle of using irb? There sure is! Let's exit the irb shell (using the command exit) and continue poking around from outside the shell.
Back in the native ruby-debug shell, we can use the pp (pretty print) command to display the value of our story's description once it's populated through the web form:
(rdb) pp params[:story][:description]
=> nil
If you type this into your ruby-debug shell, you'll see that it also returns an empty object. So, as a last resort, let's take a peek at the full params hash, which contains the values of all form fields that have been submitted, no matter which scope they reside in:
(rdb) pp params
![]()
As you can see, pp actually formats the output for us to make it more readable than the output we're used to seeing in the standard irb shell (hence the word "pretty"). While this feature is not exclusive to ruby-debug (the pp library can be loaded outside of ruby-debug as well), it's certainly convenient that ruby-debug makes use of it automatically.
The section I've highlighted in Figure 7 is the root of the problem. As you can see, the description is indeed present in the params hash, but it's not part of our story. While the story's name and link attributes are sitting nicely together in the params[:story] hash, the description is sitting separately in params[:description].
Now, how did that happen? If we take a look at our form template (located at app/views/story/new.rhtml) it appears that I've "accidentally" deleted a few characters:
new.rhtml
# Wrong:
<p>
description:<br />
<%= text_area :description, :rows => 10 %>
</p>
new.rhtml
# Wrong:
# Right:
<p>
description:<br />
<%= f.text_area :description, :rows => 10 %>
</p>
Instead of going through the FormBuilder object that the form_for helper provides, my code was calling text_area without applying the scope of the form field. As a result, the description was ending up as a separate entry in the params hash, and our story was never receiving its value.
But What About All the Fancy Tools in ruby-debug?
Admittedly, we haven't had to use any of ruby-debug's more advanced features to debug this example problem. But when we're forced to debug more complicated code, ruby-debug's advanced features become really handy.
Let's first take a look at the stepping methods. To do so we'll need to move our debugger statement into a method that contains a little more code than the previous example (so that we can actually step through each line). The best candidate for this task is the vote action of our StoryController; here's a version of this method to which I've added the debugger statement:
story_controller.rb (excerpt)
def vote
debugger
@story = Story.find(params[:id])
@story.votes.create(:user => @current_user)
respond_to do |wants|
wants.html { redirect_to :action => 'show', :permalink => @story.permalink }
wants.js { render }
end
end
To invoke the debugger in this new location, exit your current debugging session (if you haven't already) using the cont command. This will resurrect your stalled browser and allow you to continue browsing the shovell application. Now, select a story from the upcoming stories queue and click the Vote! button to engage the debugger once more.
Previously, we saw how the list command could be used to give us an indication of where in the source code our application was currently paused. When it's paused, we can use the next command to advance to the next line of code. Typing next will display the regular Rails log output for the following line, then return you to the ruby-debug prompt. From here you can once again use list to check your new location in the application, as I've done in Figure 8.
![]()
To explore the methods provided by an object that you're curious about, you can use the method command. The following command will produce a list of instance methods provided by the @story object, sorted alphabetically, as shown in Figure 9:
(rdb) method instance @story
![]()
The method command can be used to list class methods, too. The following command will produce an alphabetically sorted list of class methods provided by the Story class, as shown in Figure 10.
(rdb) method Story
![]()