Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
169: Debugging with Pry and byebug
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:
- Copy the code below into a file named
loyalty_debug.rb. - Install the gems
gem install pry byebugif you haven't already. - Insert a
binding.pryorbyebugcall inside thecalculate_pointsmethod to inspect thecustomerobject and thetotal_spentvariable. - 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)}"
There are no comments for now.