Recipe 10.12. Debugging Your Code Interactively with ruby-debug


Problem

Contributed by: Christian Romney

You want to use a fine-grained, interactive debugger to track down problems in your code.

Solution

While the breakpointer module that ships with Rails is a great quick and dirty tool for inspecting your application, it's not a full-featured debugger. One promising alternative is ruby-debug. The ruby-debug gem is a fast, console-based debugger that works very well with Rails applications and gives you more power and flexibility than the breakpointer module. For this recipe, create a simple Rails application called blog:

$ rails blog             

This would be a good time to create a database for this application and configure database.yml, too. The next thing you'll need to do is install ruby-debug using RubyGems. Open a terminal window, and execute the following command:

$ sudo gem install ruby-debug             

You'll need some simple code against which to use the debugger, so generate a simple model called Post:

$ ruby script/generate model Post             

Now, edit the migration file created by the Rails generator:

db/migrate/001_create_posts.rb:

class Post < ActiveRecord::Base; end class CreatePosts < ActiveRecord::Migration   def self.up     create_table :posts do |t|       t.column :title, :string       t.column :published_at, :datetime       t.column :updated_at, :datetime       t.column :content, :text       t.column :content_type, :string       t.column :author, :string     end          Post.new do |post|       post.title = 'Rails Cookbook'       post.updated_at = post.published_at = Time.now       post.author = 'Christian'       post.content = <<-ENDPOST         <p>           Rob Orsini's Rails Cookbook is out. Run, don't walk,            and get yourself a copy today!         </p>       ENDPOST       post.content_type = 'text/xhtml'       post.save     end   end   def self.down     drop_table :posts   end end

Migrate your database to create the table and sample post with the following command:

$ rake db:migrate             

The next thing you'll need is a controller and view. Generate these with the following command:

$ ruby script/generate controller Blog index             

Now you need to edit a few files to stitch together this little application. Begin with the controller:

app/controllers/blog_controller.rb:

class BlogController < ApplicationController   def index     @posts = Post.find_recent     render :action => 'index'   end end

Next, the Post model needs the find_recent class method defined:

app/models/post.rb:

class Post < ActiveRecord::Base   # Find no more than 10 posts published within the last 7 days.   def self.find_recent     cutoff_date = 7.days.ago.to_formatted_s(:db)     options = {       :conditions => [ "published_at >= ?", cutoff_date ],        :limit => 10     }     find(:all, options)   end end

Lastly, add a simple view to display the posts:

app/views/blog/index.rhtml:

<h1>Recent Posts</h1> <div >   <ul>     <% for post in @posts %>     <li>       <div >         <h2><%= post.title %></h2>         <h3 >posted by <%= post.author %></h3>         <div >           <%= post.content  %>         </div>         </div>       </li>     <% end %>   </ul>  </div>

With all the pieces in place, it's time to start debugging. The simplest way to continue is to run the WEBrick server with rdebug. The following command will do the trick:

$ rdebug script/server webrick             

ruby-debug loads the server script and prints the filename of the entry point. It also prints the first line of executable code within that file and presents you with a debugger prompt indicating the current stack level:

./script/server:2: require File.dirname(__FILE__) + '/../config/boot' (rdb:1)

At the prompt, set a breakpoint on line five of the BlogController class:

break BlogController:5

Alternatively, you could set the breakpoint conditionally by adding an if expression at the end of the break command. Next, tell the debugger to continue loading the server by typing:

run

Now, point your favorite browser at the application to hit the breakpoint. The following URL worked for me: http://localhost:3000/blog.

You should now be presented with a prompt that looks something like this:

Breakpoint 1 at BlogController:5 ./script/../config/../app/controllers/blog_controller.rb:5: render :action => 'index' (rdb:2)

Try pretty-printing the value of the @posts variable:

                pp @posts             

Now, let's print the list of current breakpoints, delete all current breakpoints, add a new breakpoint on the index method of BlogController, set a watch on the @posts variable, and continue running the application. Enter these commands in succession.

                        break delete break BlogController#index display @posts run             

Next, you'll want to refresh the page in your browser to hit the new breakpoint. The debugger stops at the first line of the index method. Type the following command to advance to the next line:

                next             

This command advances the debugger to the next line of code. This time, let's step into the find_recent method call with the step instruction. Notice that the debugger is now inside the Post class. The ability to step through your code interactively is one of the main advantages of r uby-debug over the breakpointer module. You could advance through the rest of this method with repeated calls to next, or you could move back up the stack one level with the up command. Of course, you can also type run to advance to the next breakpoint.

Discussion

We've only scratched the surface of the commands you can issue to the debugger. To view the full list of commands, type help at the (rdb) prompt. Once you've got a list of available commands, you can get more information on any command by typing help command_name. For example:

                help catch             

If you're developing on a Mac, you will also like the tmate command, which opens the current file in TextMate. If you don't have TextMate, are developing on another platform, or simply want to view the source code without opening an external editor, the list command displays the current line as well as the previous and next four lines of code.

One of the coolest features of ruby-debug is ability to debug remotely. On the host machine, simply add a few command-line options to the rdebug invocation letting ruby-debug know which IP address and port to listen on:

$ rdebug -s -h 192.168.0.20 -p 9999 script/server webrick             

Then, from another machine, or another console on the same machine, type:

$ rdebug -c -h 192.168.0.20 -9 9999             

You should now be connected to the remote debugger and be able to issue the same commands discussed above. This is especially useful for connecting ad hoc debugger sessions because you may not know where to set a breakpoint until you encounter some exception during the development of your application.

See Also

  • For more information on TextMate, see Section 2.6"

  • For more information on the breakpointer module, see Section 10.3"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net