Debug Your Rails App With ruby-debug

At the start of this year, I published a book through SitePoint titled Build Your Own Ruby On Rails Web Applications. You’ll forgive me if I’m a little biased, but it is, I believe, still the best beginner’s Rails book out there, despite the fact that many other books on Rails have been released since.

One of the best things about the Ruby on Rails framework is that it’s undergoing constant change — it’s continually being improved and tweaked. Unfortunately, this change means that anyone who purchased my book when it was initially published will have been faced with a quandary over which version of Ruby to use:

  • You could follow the book’s advice and stick with Ruby 1.8.4. This would allow you to work your way happily through the book (including the chapter on debugging), but it would also mean that you wouldn’t receive the benefits of the bug fixes and security updates that come with having an up-to-date installation of Ruby.
  • Alternatively, you could upgrade to a more recent version of Ruby. Doing so, however, would have prevented you from using the breakpointer client — a tool to which much of Chapter 11 is devoted.

At the time of the book’s publication, the breakpointer client was the best tool available for debugging a Rails application. However, breakpointer is not compatible with the latest version of the Ruby programming language.

So what’s a Rails developer to do?

In this article we’ll look at an alternative debugging tool for debugging Rails applications (or any Ruby script, for that matter) while still remaining up-to-date with the latest version of the Ruby interpreter. This tool is even slated to be bundled with future releases of Rails.

The magic new tool to which I’m referring is called ruby-debug.

A quick warning: this isn’t a beginners’ article. I’ll assume that you’ve either read my book, or have a solid understanding of how to develop a web application using Ruby on Rails. Ideally you’ll have Rails installed and will be comfortable with using the Interactive Ruby (irb) shell. It will also help if you’re familiar with shovell, the sample application used in the book; download the sample to play along at home (note: the download is 2.2MB in size).

What Broke Breakpointer?

Before we delve into the inner workings of ruby-debug, let’s look briefly at why a tool as useful as breakpointer came to be incompatible with the language for which it was designed to be used.

The breakpoint library, of which the breakpointer client is a part, was developed and released to the public by Florian Gross. It was built on top of a Ruby extension called binding.of_caller, which relied on a bug in Ruby’s implementation of trace calls. This bug, however, was fixed in Ruby 1.8.5! The result is that both the binding.of_caller extension and the breakpoint library became unusable. The library is also no longer being maintained, so it’s effectively dead.

Say Hello to ruby-debug

Back in July of 2006, Kent Sibilev released the first version of ruby-debug, describing it as "a faster implementation of the standard debug.rb." Today, ruby-debug is the most viable option with regards to debugging Ruby scripts — and that includes Rails applications.

While it would be beyond the scope of this article for me to explain how ruby-debug works its magic behind the scenes, it suffices to say that ruby-debug uses a natively compiled Ruby extension that’s written in C. The result is that it performs amazingly well, even with very large Ruby scripts.

Comparing Feature Sets

As I described in the tutorial in my book, the breakpointer client allowed us to step into our application at run time and explore it from the command line. While the ruby-debug tool includes similar functionality, it goes way beyond what good old breakpointer had to offer.

Unlike breakpointer, which worked from a simple irb prompt, ruby-debug provides you with a more advanced shell, similar to that provided by GDB, the GNU debugger for the C programming language.

In this shell you can:

  • Step forward and backward in your code.
  • Execute and skip lines of code (without copying and pasting them from your code editor window).
  • List the actual source context at which you’ve stopped your application.
  • Step into irb mode and make use of the same shell that’s used by breakpointer (if you’re someone who finds old habits difficult to shake).

Installing ruby-debug

The following steps will configure your system for debugging with ruby-debug.
First of all, we need to install the ruby-debug library. Since it’s distributed as a RubyGems package, the installation is as easy as typing the following command:

$ sudo gem install ruby-debug -y

The install script will prompt you to indicate the platform on which you’re installing the gem. If you’re on a Mac or a Linux system, select option 1; if you’re on a Windows machine, select option 2. The installation process should look similar to that shown in Figure 1.

Debug Your Rails App With ruby-debug

Y is for Yes, Please!
By invoking the gem command with the -y switch, we instructed the utility to automatically install all other packages upon which ruby-debug depends. In this case, those packages include the ruby-debug-base package containing the actual debugger code; the ruby-debug package contains the CLI (command line interface).

With ruby-debug happily installed, we can now place some hooks in our Rails application. For this example, we’ll use the completed shovell application from the book as an example of the process of debugging with ruby-debug. If you’d like to follow along with this example, you should download shovell, the sample application, before reading any further.

In Rails

Making a Rails application aware of ruby-debug is as simple as adding a line to the application’s config/environments/development.rb file:

# config/environments/development.rb  require "ruby-debug"

It makes sense to make this change to our development environment (as compared to, say, config/environment.rb), as this is the preferred environment in which you should perform your debugging. The aim should be to replicate any errors that occur in other environments, so debugging should rarely occur in a testing or production environment.

ruby-debug is part of EdgeRails
Many developers like being on the bleeding edge, and are happy to develop their applications with a version of Rails that’s not guaranteed to be as stable as the official release, in order to receive the very latest feature additions. This version of the framework is referred to as EdgeRails.

Explicitly adding ruby-debug to your application environment is unnecessary if you’re running EdgeRails — simply start the application server with the --debugger argument to load the ruby-debug library.

With this setting in place, you can fire up your application server from your Rails application’s root folder:

$ ruby script/server

Figure 2 shows the console output that results when we start the Mongrel server.

Debug Your Rails App With ruby-debug

Bricks and Mongrels Only
If your development environment makes use of an alternative server, such as lighttpd with FastCGI, you won’t be able to use it as a debugging platform. You’ll need to be running either WEBrick or Mongrel.

At first glance, the console output doesn’t look much different from what we’re used to seeing when we run this command. However, keep a close eye on this window as we progress through this exercise.

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.

Debug Your Rails App With 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.

Debug Your Rails App With ruby-debug

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, use cont.
  • 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.

Debug Your Rails App With ruby-debug

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.

Debug Your Rails App With ruby-debug

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

Debug Your Rails App With ruby-debug

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.

Debug Your Rails App With ruby-debug

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

Debug Your Rails App With ruby-debug
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

Debug Your Rails App With ruby-debug
  Manually Setting Breakpoints    While using the next command can be useful if you know exactly where in your application to go poking around, it can be less useful in a Rails application. The level at which the stepping occurs can in some circumstances be far too granular, and can result in your stepping through multiple lines of core library files instead of your own code.  To gain a little more control over where the debugger halts execution, you can manually set breakpoints at the locations you desire. Breakpoints can be set by specifying either:  
  • a combination of filename and line number
  • a class name and the name of an instance method or class method
   As a practical example of setting manual breakpoints, we're going to move the halt point from its current location (inside the vote action of StoryController) to the RJS template that's rendered when that same action is requested to render a JavaScript response. We'll do all of this without ever opening a text editor, or stepping over every line between the current point of execution and the code of the RJS template.  The last line of the RJS template at app/views/story/vote.rjs reads:
vote.rjs (excerpt)   

page[:vote_history].replace_html :partial => 'vote',      :collection => @story.latest_votes
 It therefore makes sense to choose the latest_votes instance method of a Story object as our new breakpoint. To do so, execute the following command in the ruby-debug shell:
(rdb) break Story#latest_votes
 You can now let go of the current breakpoint by typing the cont command in the ruby-debug shell. Execution will resume until the @story.latest_votes call is executed, at which point the application will pause again. To verify that we're paused exactly where we expect to be, type list. Figure 11 confirms that I've stopped my application at the beginning of the latest_votes method. Debug Your Rails App With ruby-debug
 Source-code Reloading A Rails application, when run in development mode, automatically adopts all changes that are made to the source files without requiring you to restart the application server. ruby-debug includes a similar feature to avoid stale code passages from being displayed in the stack traces and listings output by the list command. If you can afford it (performance-wise), type the following at the ruby-debug prompt:
(rdb) set autoreload
 With this setting, ruby-debug will automatically reload your Ruby scripts from disk whenever necessary. If this appears to slow down your development progress significantly, you can instead periodically invoke the reload command whenever you think you're getting stale representations of your code. TextMate Integration Those developers who develop their Rails applications on a Mac using the TextMate editor will be pleased to know that the author of ruby-debug happens to be a fan of TextMate as well. Fortunately, ruby-debug ships with some nice hooks that you can use to integrate the debugger with the editor. First of all, you can open the file in which your application is currently paused using the tmate command. This eases the round-trips between your terminal window and your editor quite a bit. I'd also recommend that you install the Ruby Debug Bundle for TextMate. This package gives you ultimate control over setting breakpoints from within TextMate itself. After you've installed the bundle, you'll need to launch your application a little differently. Here's how to start your application server to take advantage of ruby-debug's remote debugging facilities (the $ indicates that we're typing this in an operating system shell):
$ rdebug -sn ./script/server
 Unlike the local debugging facility that we've been using thus far, you can safely minimize the terminal window in which you started your application server -- we need to fire up a separate debugging client (from a new command prompt) to enable communication between TextMate and ruby-debug.  Open a new terminal window and type the following command:
$ rdebug -c
 You should see the message "Connected", as shown in Figure 12. You'll need to leave this window open and accessible, as this will be the window that displays the ruby-debug console output once a breakpoint is encountered. Debug Your Rails App With ruby-debug
As soon as that's accomplished, you can use the bundle's only keyboard shortcut, Cmd-Shift-B, to open up a menu of all the available commands, shown in Figure 13. Debug Your Rails App With ruby-debug
You can use this menu to set a breakpoint at the position of the cursor in the current TextMate editing window. You can also show or delete all breakpoints that have been set, or even interrupt or quit the debugging shell right from the convenience of your text editing window. Conclusion ruby-debug is a worthy successor to the old (and admittedly hackish) technique of debugging your Ruby application using the breakpoint library. Better yet, it's compatible with all the most recent releases of the Ruby language interpreter. ruby-debug comes packed with many welcome shortcuts and powerful navigation commands that make debugging Ruby scripts and Rails applications a joyful and rewarding experience. For further reading on ruby-debug and many helpful articles and links to Ruby resources, I thoroughly recommend you subscribe to Kent Sibilev's weblog.

Replay

Category: programming Time: 2007-10-01 Views: 1
Tags:

Related post

  • Easy Internationalization for Your Rails App with BDD 2012-01-23

    I18n for Your Rails App Easy Internationalization for Your Rails App with BDD Easy Internationalization for Your Rails App with BDD, Part II Easy Internationalization for Your Rails App with BDD, Part III Easy Internationalization for Your Rails App

  • Easy Internationalization for Your Rails App with BDD, Part II 2012-02-08

    I18n for Your Rails App Easy Internationalization for Your Rails App with BDD Easy Internationalization for Your Rails App with BDD, Part II Easy Internationalization for Your Rails App with BDD, Part III Easy Internationalization for Your Rails App

  • Easy Internationalization for Your Rails App with BDD, Part III 2012-03-14

    I18n for Your Rails App Easy Internationalization for Your Rails App with BDD Easy Internationalization for Your Rails App with BDD, Part II Easy Internationalization for Your Rails App with BDD, Part III Easy Internationalization for Your Rails App

  • Easy Internationalization for Your Rails App with BDD, Part IV 2012-03-28

    I18n for Your Rails App Easy Internationalization for Your Rails App with BDD Easy Internationalization for Your Rails App with BDD, Part II Easy Internationalization for Your Rails App with BDD, Part III Easy Internationalization for Your Rails App

  • Authorizing your Rails app with Authority 2012-04-27

    Imagine you're writing a Rails app to organize conferences. As soon as you know what the app can do, you have to start deciding who can do what. Who is allowed to: Decide who will speak at the conference? Edit the presenter schedule? Upload presentat

  • Deploying Your Rails App to the Cloud with Unicorn, Nginx, and Capistrano 2012-05-17

    We all know how easy it is to create a Rails app, but what about when your app is ready for production? The first thing you need to do is to set up your server and install the proper libraries, so fire up a terminal window and SSH into your server, a

  • Pimp Your PHP App With Flex - And Win! 2009-04-30

    Our latest Adobe-sponsored giveaway is live, and up for grabs this time are copies of Flex Builder 3. Just read our brand new article, Pimp Your PHP App With Flex – then take the quiz to enter the draw! You've got 7 days to take the quiz. Enter your

  • When dockerizing a rails app with EC2 container service with ELB do I still need nginx? 2016-01-27

    I have a container with a rails app and puma app server, listening on port 3000. This container is to be deployed to amazons ec2 container service (ECS), and should be autoscaled and load balanced via ELB. How does nginx (if at all) fit into the pict

  • what is the best practice of packing your custom app with custom permission 2013-12-30

    what is the best practice of packing your custom app with custom permission, so that an administrator who install the app doesn't have to do too much work on the security end and all user have access the App Directly --------------Solutions----------

  • 404 not found error on Rails app with Helicon Zoo on Windows Server 2014-07-15

    I'm trying to run Rails App with Helicon Zoo on Windows Server 2008 R2. For the test case I used Redmine 2.3 that goes with the Helicon Zoo module. Redmine has been installed successful, but it is available only from local network. I've configured po

  • How to debug a rails app in docker with pry? 2016-02-04

    I have a rails app running in a docker container in development environment. When I try to debug it with placing binding.pry somewhere in the code and attaching to the container I can see the pry prompt in the output but it doesn't pause on it and I

  • What's the current, "correct" way to bundle gems for a Rails app with system-wide RVM-installed Ruby? 2014-08-08

    I got ruby installed system-wide by doing: \curl -sSL https://get.rvm.io | sudo bash -s stable and then: sudo /usr/local/rvm/bin/rvm install 2.0.0 I'm still not positive that was actually correct, but it seems to have worked. It created an rvm group.

  • Supercharge Your Flex App with ColdFusion Power 2009-05-14

    There are quite a few tutorials here at SitePoint that can help you grasp some of the key principles of creating Rich Internet Applications (RIAs) using Flex and AIR. You'll find that most development undertakings in Flex will involve a back-end appl

  • Adding SMS Capabilities to Your Rails App 2012-07-02

    In a recent project, one of the technical requirements was to integrate an SMS service to a Rails application so that users could validate their accounts using their mobile device and also allow the application to send SMS messages to such users. As

  • Preview Your Rails Mail with Letter Opener 2013-05-06

    In this tutorial I will show you the setup and use of a email-preview gem called Letter-Opener by Ryan Bates. In case you are serious about using Rails and you do not know Ryan's excellent Railscasts, go watch them. The gem we are using here makes it

  • Ratyrate: Add Rating to Your Rails App 2014-09-26

    This tutorial explores how to add rating functionality to a Rails application. It will go through some of the more popular Ruby gems that provide such functionality, as well as how to implement rating from scratch. We will use the same application us

  • Starting Rails App with Phusion Passenger Standalone? 2015-03-08

    I am deploying a Rails application ( Project Fedena ) to a VPS at Digital Ocean. I can use either of mongrel_rails start and ruby script/server to launch my app with no problem. It binds to port :3000 and I can use the application as desired. The pro

  • Rails app with nginx passenger: recv() failed (104: Connection reset by peer) while sending to client 2015-05-27

    Update. June 8 It's most definitely client-side. Something about JQuery/Ajax/Facebox doesn't sit well with IE10/11. Unfortunately updating all the components for JS related things to be more compatible still doesn't help. Nor does window.location.rel

  • Deploy Your Rails App to AWS 2015-08-20

    As developers, we are usually concerned about the development part of any application. We don't think much about the deployment part as we consider it to be a responsibility of the SysAdmins. But many times, we don't have a dedicated SysAdmin availab

iOS development

Android development

Python development

JAVA development

Development language

PHP development

Ruby development

search

Front-end development

Database

development tools

Open Platform

Javascript development

.NET development

cloud computing

server

Copyright (C) avrocks.com, All Rights Reserved.

processed in 1.156 (s). 13 q(s)