Skip to Content
Course content

169: Debugging with Pry and byebug

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I'll be honest: when I first started with Ruby, my only debugging tool was puts. I would pepper my code with puts "GET HERE 1" and puts "VALUE IS: #{x}", run the script, and then spend ten minutes scrubbing through a wall of text in the terminal. It's tedious, and frankly, it's a waste of your time. Today, we're moving past that. We're going to use Pry and Byebug to actually "pause" our code while it's running so we can poke around inside the live environment.

A Shopping Cart that Can't Do Math

Let's build a simple shopping cart. I want this cart to take a list of items and apply a discount if the total is over $100. I've written the basic logic, but for some reason, my totals are coming out weirdly low. Here is the code I'm working with:

require 'pry'
require 'byebug'

class ShoppingCart
  attr_accessor :items

  def initialize
    @items = []
  end

  def add_item(name, price)
    @items << { name: name, price: price }
  end

  def calculate_total(discount_code = nil)
    total = 0
    @items.each do |item|
      total += item[:price]
    end

    if discount_code == "SAVE20"
      total -= total * 0.20
    end

    total
  end
end

cart = ShoppingCart.new
cart.add_item("Mechanical Keyboard", 120)
cart.add_item("Gaming Mouse", 50)

puts "Final Total: #{cart.calculate_total("SAVE20")}"

Wait, I just ran this and it's giving me 136.0. That seems right (170 minus 20%), but I want to add a "bulk discount" logic that's causing me grief. I'll add a loop that applies an extra 5% if there are more than 3 items. I'll write it quickly—and this is where I usually mess up.

# Inside calculate_total...
if @items.length > 3
  @items.each do |item|
    total -= item[:price] * 0.05
  end
end

Now, when I run it with 4 items, the total is way lower than it should be. I can't tell if the 20% discount is clashing with the 5% bulk discount or if I'm subtracting too much. Instead of guessing, let's use Pry.

Dropping into the Middle of the Action

Pry is essentially a REPL (Read-Eval-Print Loop) that you can drop anywhere in your code. I'm going to place binding.pry right before the discount logic. This tells Ruby: "Stop everything and give me a terminal right here."

def calculate_total(discount_code = nil)
  total = 0
  @items.each { |item| total += item[:price] }

  binding.pry # Execution stops here!

  if discount_code == "SAVE20"
    total -= total * 0.20
  end
  # ... bulk discount logic ...
end

When I run the script now, the program pauses. I'm not just looking at a log; I'm inside the calculate_total method. I can type total to see its current value, or @items to see the array. I can even change the value of total on the fly to see how the rest of the method reacts. It's like having an X-ray machine for your logic.

Walking Through the Loop Line by Line

Pry is great for snapshots, but Byebug is for the journey. While Pry lets me look around, Byebug lets me move forward one line at a time. I'll swap binding.pry for byebug to investigate that bulk discount loop.

Once the program hits the byebug line, I have a few key commands:

  • next: Execute the current line and move to the next one.
  • step: Dive into a method call on the current line.
  • continue: Stop debugging and run the program until it finishes or hits another breakpoint.

As I hit next, I notice something embarrassing. In my bulk discount loop, I'm subtracting 5% of the item price for every single item, but I'm doing it after the 20% discount has already been applied to the total. But wait—I look closer at my loop and realize I've accidentally written total -= total * 0.05 inside the loop instead of item[:price] * 0.05. I'm subtracting 5% of the entire total for every single item in the cart!

# My mistake:
@items.each do |item|
  total -= total * 0.05 # Oops! This compounds the discount exponentially.
end

Because I was using Byebug, I could actually watch the total variable plummet with every next command. I didn't have to guess; I saw the crime happening in real-time. I fix the line to total -= item[:price] * 0.05, restart the script, and the math finally checks out.




📋 Practical Task

Debug the Loyalty Points Calculator

You have been handed a piece of code that calculates loyalty points for a customer. The business rule is: customers get 1 point for every dollar spent, but if they are a "Gold" member, they should get 2 points per dollar. However, the current code is returning 0 points for everyone regardless of their status.

Your Task:

  1. Copy the code below into a file named loyalty_debug.rb.
  2. Install the gems gem install pry byebug if you haven't already.
  3. Insert a binding.pry or byebug call inside the calculate_points method to inspect the customer object and the total_spent variable.
  4. Identify why the points are calculating as 0 and fix the logic.
require 'pry'
require 'byebug'

def calculate_points(customer, total_spent)
  points = 0
  
  # The bug is somewhere in here
  if customer[:status] == "Gold"
    points = total_spent * 2
  elsif customer[:status] == "Silver"
    points = total_spent * 1.5
  else
    points = total_spent * 1
  end

  return points.to_i
end

# Test Case
user = { name: "Alice", status: "Gold " } # Note the subtle issue here
spent = 100

puts "Points earned: #{calculate_points(user, spent)}"
Rating
0 0

There are no comments for now.

to be the first to leave a comment.