In this post I will cover a few ways to debug code inside Ruby gems in your application.
I'll use actionview
in Rails as an example, but you can apply these techniques to debug any gem.
Debugging in Ruby 🐞
There are two main debugging styles in Ruby:
-
Puts Debugging consists in adding
puts
statements in the target code, execute the script or application, and use the output to understand what's going on.def javascript_include_tag(*sources) puts "javascript_include_tag(#{sources.inspect})"
-
Interactive Debugging enables pausing the program at a specific instruction, examining the current state in a REPL, and doing step-by-step execution.
def javascript_include_tag(*sources) binding.pry # or binding.irb or byebug
One of many advantages of interactive debugging is that it becomes possible to
easily navigate into a library's internals by using step
or break
.
To debug a library using puts debugging, you'll need to do things differently.
Opening Gems 💎⛏
You can run bundle open
to open a gem in your favorite text editor.
The
EDITOR
environment variable will be used to infer the preferred text editor. I like using Sublime Text or Visual Studio Code to navigate the file tree.
For example, running bundle open actionview
will open the version of the actionview
gem specified in the nearest Gemfile.lock
.
Editing In-Place ✍️
In contrast with compiled languages—where libraries are distributed as binaries—in Ruby most libraries are distributed as plain .rb
files which are human-readable.
And human-writable! You can modify the code in a library, restart your app, and your changes will be picked up! 🤩
This is extremely useful when tracking down bugs—you can add puts
or breakpoints as needed, or even modify the behavior of the library.
Run
gem pristine :library:
once you are done to undo any changes. Otherwise, you risk relying on behavior that is local and unversioned 🙀
Using a Local Library 🔗
When you need to make extensive changes a nicer flow is to clone the library, and then point to your local copy.
For example, to fix a bug in actionview
you would clone the
Rails repo, open it in your editor to make changes, and then update the
Gemfile
in your project to specify the dependency using path
.
# Assuming rails was cloned in the same parent directory
gem 'actionview', path: '../rails/actionview'
If a dependency is specified using a Git repository, prefer local paths in Bundler.
Just as in the previous section, it's important to restart the program you intend to debug after making changes.
Why is it necessary to restart the application? 🤔
Most Ruby programs use require
to load a library into memory, and will use that cached version during their entire running time.
Restarting the program, such as a script or a web development server, ensures that any changes you have made to the library are loaded!
Additional Resources 📖
I hope this summary has been useful! A similar guide for JavaScript is available 😃
Please refer to the following documentation for more information:
pry-byebug
: Step-by-step debugging in Prybreak
: A debugger written in Ruby that integrates with IRB and Pry- Debugging Cheat Sheet: Nice resource if you prefer puts debugging