A Beginner’s Guide to Debugging in Ruby

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.

  • Outputting with Puts
  • Interactive Ruby (IRB)
  • Pry

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:

  • Quick feedback
  • No additional requirements or files needed for use

Cons:

  • Can become unwieldy when dealing with complex datasets or code
  • Vigilance required to ensure that no puts statements inadvertently serve as return values, unless intended

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:

  1. Navigate in the terminal to the directory housing your code.
  2. Initiate the IRB session: irb
  3. Add your file as a requirement in the IRB session:

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:

  1. In your project directory, create a file, perhaps named console.rb.
  2. In this file, require all the files relevant to your project, and initiate an IRB session upon running the file:

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__)        

  1. Execute the file in your terminal (ruby console.rb), and an IRB session should commence in your terminal.

To exit an IRB session, simply type exit in your terminal.

Pros:

  • Swift feedback
  • Minimal setup required

Cons:

  • Entering IRB spawns a new environment each time, leading to loss of code from previous sessions
  • Inability to inspect scope mid-method or loop

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:

  • Greater flexibility — binding.pry can be employed anywhere in your code, including within methods or loops
  • Cons:
  • Requires installation of the pry gem and its addition as a requirement
  • Exercise caution when using binding.pry in protracted loops!

Hope this helps you fix some bugs!

Ahmed Nasser

Software Engineer @ Vodafone Egypt | ITI Graduate

7 个月

Very Helpful , keep up the outstanding work ya omnia ??????

要查看或添加评论,请登录

社区洞察

其他会员也浏览了