[Today’s random Sourcerer profile: https://sourcerer.io/yuki24]

Turning Bugs into Gems: Debugging Ruby Applications

Robert W. Oliver II
Sourcerer Blog
Published in
5 min readNov 8, 2018

--

Ruby is a beautiful language. It’s worst, and best quality is that once you use it, you likely won’t want to use anything else.

That’s good because you can do nearly anything with it. It’s bad because it’s not as commonly used as it should be.

Ruby’s approach to treating everything as an object and meta-programming abilities give it an array of immensely powerful debugging tools and techniques.

Whether you are a beginner or an experienced Ruby developer, working on Ruby applications or Ruby on Rails websites, I aim to share some helpful, late-night-saving tips that will help you find those pesky bugs in your Ruby applications.

Inspector Ruby

The “inspect” method is built into every class and is incredibly useful in peering inside objects. Here’s a trivial example:

irb(main):001:0> a = "Hello, World!"
=> "Hello, World!"
irb(main):002:0> a.inspect
=> "\"Hello, World!\""

In this case, inspect works much like var_dump in PHP. Let’s look at an array:

irb(main):001:0> a = [“Hello”, “World”]
=> [“Hello”, “World”]
irb(main):002:0> a.inspect
=> “[\”Hello\”, \”World\”]”

Helpful, but for even better output in JSON, require the json module and use .to_json on any object:

[1] pry(main)> require 'json'
=> true
[2] pry(main)> a = ["Hello, World!"]
=> ["Hello, World!"]
[3] pry(main)> a.to_json
=> "[\"Hello, World!\"]"

Monkey Patching to the Rescue

Ruby allows for monkey patching — a goofy name for an incredibly powerful technique. At any time during program execution, you can augment existing code by simply redefining it. Consider the following code:

#!/usr/bin/env rubyclass Demo
def initialize
puts "Hello, World!"
end
end
a = Demo.newclass Demo
def initialize
puts "Goodbye, World!"
end
def wave
puts "*waves*"
end
end
b = Demo.newb.wave
a.wave

In this example, we see that the Demo class acts as expected when we create a new object named “a”. But then we “redefine” the class, both changing the initialize method and adding a new method. When we create a new object named “b” it runs the initialize method with the new text.

However, note that object “a” takes on the “wave” method as well, even though it is already defined. Ruby’s monkey patching allows for surgery on any class even if it already has existing objects created from that class.

How does this help us with debugging? By being able to insert new methods or redefine existing methods in classes we can add debugging printouts or override variables or tests on said variables. You can even override methods to include a logging function to log the details of objects and the actions taken upon them to disk.

Being able to do this at any time is useful, but why not put this meta-programming power to even better use with on-demand introspective debugging.

There’s a tool to do just that — Pry.

Prying Into Your App

Pry is an incredible tool for debugging. It’s a full fledged replacement for irb that is great for standalone use or can be easily embedded into your project.

Pry treats scopes like directories, allowing the cd command to be used to navigate between scope layers, and ls to list instance and local variables:

[1] pry(main)> class Demo
[1] pry(main)* @a = "This is a demo."
[1] pry(main)* end
=> "This is a demo."
[2] pry(main)> cd Demo
[3] pry(Demo):1> ls
instance variables: @a
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
[4] pry(Demo):1> cd

True to Ruby’s monkey-patching nature, you can use Pry to edit your code within the execution of your program. Here’s an example:

[1] pry(main)> class Demo
[1] pry(main)* @a = "This is a demo."
[1] pry(main)* end
=> "This is a demo."
[2] pry(main)> cd Demo
[3] pry(Demo):1> ls
instance variables: @a
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
[4] pry(Demo):1> @a
=> "This is a demo."
[5] pry(Demo):1> @a = "Hello, World"
=> "Hello, World"
[6] pry(Demo):1> ls
instance variables: @a
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
[7] pry(Demo):1> @a
=> "Hello, World"
[8] pry(Demo):1> cd ..
[9] pry(main)>

Note at the end I used “cd ..” to move “up” out of the “Demo” class scope and back to the default scope.

The “show-method” function allows you to display the source code of a method:

[1] pry(main)> class Demo
[1] pry(main)* def initialize
[1] pry(main)* puts "Hello, World!"
[1] pry(main)* end
[1] pry(main)* end
=> :initialize
[2] pry(main)> show-method initialize
Error: Cannot locate this method: initialize.
[3] pry(main)> cd Demo
[4] pry(Demo):1> show-method initialize
From: (pry) @ line 2:
Owner: Demo
Visibility: private
Number of lines: 3
def initialize
puts "Hello, World!"
end
[5] pry(Demo):1>

Note that the first command of “show-method” didn’t work because I wasn’t in the “Demo” scope.

If you install the “pry-doc” gem, you can see the C source code for built-in objects:

[1] pry(main)> show-method String#putsFrom: io.c (C Method):
Owner: Kernel
Visibility: private
Number of lines: 8
static VALUE
rb_f_puts(int argc, VALUE *argv, VALUE recv)
{
if (recv == rb_stdout) {
return rb_io_puts(argc, argv, recv);
}
return rb_funcallv(rb_stdout, rb_intern("puts"), argc, argv);
}
[2] pry(main)>

This is extremely useful when trying to debug behavior with a built-in method.

Adding Pry to an Existing Ruby Program

If you enjoy games, especially text-based games, and you haven’t read about Legend of the Sourcerer, go do it now. Open it in a new window. I’ll wait.

Neat, huh? Since it’s command-driven, we can add a command “~” that will break out into a Pry session.

In LOTS, you can use Pry to examine current game variables and even edit the map. While useful for cheating, you could also refer to this handy console when trying to debug new features you add to the game.

Pry integration into LOTS is in the current version on GitHub. Here’s the commit where I integrated Pry. But adding Pry to an existing project is as simple as adding:

require 'pry'

Then put:

binding.pry

Where you’d like to invoke Pry. When this line executes, the session will start immediately.

Using Pry with Rails

There is a gem which handles Pry integration into Rails for you. If you’d rather not change your app and just want to use the Pry console with Rails, you can do so by running

pry -r ./config/environment

This will start a Pry session with your Rails app loaded. Navigate your application class structure with ease with all of that Pry debugging goodness at your fingertips.

Deep in the Ruby Mines

I hope you’ve enjoyed and gleaned some helpful hints from this look into debugging Ruby programs. Armed with Ruby’s powerful meta-programming and introspection and the awesomely astounding Pry gem, your bugs don’t stand a chance.

--

--

Technical writer, fiction author, system administrator, web and embedded developer, and philosopher. https://sourcerer.io/rwoliver2