A Beginner’s Guide to Debugging in Ruby
Every programmer, regardless of their level of expertise or the programming language they work with, has encountered bugs at some point, often facing a myriad of them on a daily basis.
When starting to learn a new programming language like Ruby, it's important to focus on two main things: understanding the common types of errors you might come across, and knowing the tools you can use to fix them. Let's explore these aspects of Ruby together.
But before we explore these tools, let's establish a foundation by understanding some typical types of errors and their corresponding messages in Ruby.
Decoding Common Error Types
Name Errors
Name errors crop up when a name is either undefined or invalid, encompassing variable or method names. Suppose we have a straightforward class named Dog, featuring a single instance method, bark:
class Dog
def bark
puts "Woof!"
end
end
Now, let's say we instantiate this class with a new instance called Bob:
# Creating a new instance
bob = Dog.new
# Calling the new instance
Bob
Attempting to call Bob (with an uppercase 'B') would result in a name error:
lib/dog.rb:11:in `<main>': uninitialized constant Bob (NameError)
The Ruby error message helpfully pinpoints the location of the error (line 11 in the file) and provides a description. Rectifying this could be as simple as changing the uppercase “Bob” to lowercase “bob”.
Syntax Errors
Syntax errors occur when there's an issue with the syntax itself. For instance, in the previous example, if we were to omit the .end keyword from our bark method:
class Dog
def bark
puts "Woof!"
end
The resulting error message would indicate something like:
lib/dog.rb:5: syntax error, unexpected end-of-input, expecting `end'
Ruby conveniently specifies the location (line 5 of our file) and offers a helpful clue for rectifying the syntax error. Simply appending another end keyword should set things right.
Type Errors
Type errors arise when Ruby encounters an object of an unexpected type. One common example involves attempting a mathematical operation on a string and an integer:
2 + "2" => lib/math.rb:7:in `+': String can't be coerced into Integer (TypeError)
Ruby politely alerts you to the impossibility of adding a string and an integer. Substituting the “2” with the integer 2 should resolve the issue smoothly.
Argument Errors
Argument errors occur when method arguments are incorrect, such as the wrong number or unacceptable types. Further resources are provided for deeper understanding. Now, let's explore debugging techniques.
Debugging Toolkit
Outputting with Puts
In debugging, printing or displaying code segments is fundamental. In Ruby, options like puts, print, p, and pp are available, with puts adding a new line at the end of the string.
def add_numbers(num1, num2)
sum = num1 + num2
puts "this is the sum: #{sum}"
end
add_numbers(1,2)
Note that while puts and print display the statement in the terminal, they return nil.
In a more intricate example, you could utilize puts to confirm the functionality of an enumerating method, such as squaring each integer in an array:
new_arr = [1,2,3,4].map do |n|
n**2
puts n
end
---
1
2
3
4
=>[nil, nil, nil, nil]
P and pp delve deeper into data, with P invoking the .inspect method for readability and pp "pretty-printing" complex data using .pretty_inspect. Here are some pros and cons of using puts for debugging.
Pros:
Cons:
领英推荐
Interactive Ruby (IRB)
IRB, or Interactive Ruby, is a built-in program displaying real-time results of Ruby statements, functioning as a REPL. Initiating an IRB session is as simple as typing "irb" in your terminal after installing Ruby.
Upon doing so, you'll be greeted by the IRB session:
2.7.2 :001 >
From here, you can feed various Ruby code snippets like so:
2.7.2 :001 > new_arr = [1,2,3,4]
=> [1, 2, 3, 4]
2.7.2 :002 > new_arr.first
=> 1
While this is fantastic for experimenting in Ruby, how does one employ it for debugging their own code? I'm glad you asked! Here's how:
2.7.2 :001 >require 'dogs.rb' => true
Now, you have access to your code and can experiment with different scenarios, test code snippets, and more.
2.7.2 :002 > nancy = Dog.new => #<Dog:0x00007fe072a952e0>
If you're dealing with multiple files, I recommend setting up a dedicated console file. This allows you to require each of your files in one location:
require_relative '../lib/file_1'
require_relative '../lib/file_2'
require_relative '../lib/file_3'
require_relative '../lib/file_4'
require "irb"
IRB.start(__FILE__)
To exit an IRB session, simply type exit in your terminal.
Pros:
Cons:
Pry
Think of Pry as IRB on steroids, or as I affectionately call it, fancy IRB. The initial step involves installing Pry. Execute the following command in your terminal:
gem install pry
After installing Pry, utilize binding.pry to set breakpoints and inspect the current scope in your code. Ensure 'require 'pry'' is added at the top of your file for Pry integration.
#dogs.rb
---
require 'pry'
class Dog
attr_reader :name, :breed, :age
def initialize(name:, breed:, age:)
@name = name
@breed = breed
@age = age
binding.pry
end
end
bob = Dog.new(name: "Bob", breed: "Lab", age: 3)
stanley = Dog.new(name: "Stanley", breed: "Dachshund", age: 8)
Execute the file in your terminal — ruby dogs.rb — and a Pry session will commence:
From: .../dogs.rb:10 Dog#initialize:
6: def initialize(name:, breed:, age:)
7: @name = name
8: @breed = breed
9: @age = age
=>10: binding.pry
11: end
From: .../dogs.rb:10 Dog#initialize:
6: def initialize(name:, breed:, age:)
7: @name = name
8: @breed = breed
9: @age = age
=>10: binding.pry
11: end
You can now interact with the current scope:
[1] pry(#<Dog>)> self.name
=> "Bob"
[2] pry(#<Dog>)> self.breed
=> "Lab"
[3] pry(#<Dog>)> exit
Remember to type exit when you're prepared to proceed to the next Pry session.
From: .../dogs.rb:10 Dog#initialize:
6: def initialize(name:, breed:, age:)
7: @name = name
8: @breed = breed
9: @age = age
=> 10: binding.pry
11: end
[1] pry(#<Dog>)> self.name
=> "Stanley"
[2] pry(#<Dog>)> exit
Creating two instances of our Dog class triggered breakpoints with binding.pry, showcasing its utility. Pry is invaluable for navigating Ruby's complexities, with additional resources linked for further learning.
Pros:
Hope this helps you fix some bugs!
Software Engineer @ Vodafone Egypt | ITI Graduate
7 个月Very Helpful , keep up the outstanding work ya omnia ??????