Understand how to read Ruby error messages and use them to fix programs and build programs.
So far, we've been introduced to some common errors and learned to read and understand error messages that appear as a consequence of running Ruby programs and test suites. This lesson provides a closer look at some common types of errors. Right now, it may be the case that not all of these error messages make sense or seem meaningful to you. After all, we've only handled a few real programs at this point.
This is meant to be a resource for you to refer back to as you start building more complex programs and have to debug them. There are a few important take-aways from this and the previous two lessons:
-
Don't be afraid of broken programs! It's easy to get frustrated when your program breaks. The tendency of a lot of beginners is to jump right back into the code when a test fails or an error comes up as a consequence of running a program, without reading the error messages. Error messages are there to guide you. They contain important information about the location and type of problem you are encountering. Embrace them and get comfortable reading them––don't run away from them.
-
Pay attention to the helpful part of error messages. Check out the line number and the type of error that you're receiving. This will point you in the right direction. Let's take a look at an example from an earlier lab.
Failures:
1) Not having any errors and being all green ZeroDivisionError raises a ZeroDivisionError for dividing by zero
Failure/Error: expect{
expected no Exception, got #<TypeError: nil can't be coerced into Fixnum> with backtrace:
# ./lib/a_division_by_zero_error.rb:3:in `/'
# ./lib/a_division_by_zero_error.rb:3:in `<top (required)>'
# ./spec/no_ruby_errors_spec.rb:30:in `load'
# ./spec/no_ruby_errors_spec.rb:30:in `block (4 levels) in <top (required)>'
# ./spec/no_ruby_errors_spec.rb:29:in `block (3 levels) in <top (required)>'
# ./spec/no_ruby_errors_spec.rb:29:in `block (3 levels) in <top (required)>'
This is some of the output we recieved after running our test suite with the learn
or rspec
command on this lab. There is a lot going on there! BUT––we know what to look for now.
We pay attention to the text right after the Failure/Error:
.
It reads: expect{ expected no Exception, got #<TypeError: nil can't be coerced into Fixnum>
Now we know we are dealing with a TypeError and that something in our program is nil
. But what? Well, let's take a look at the next line: # ./lib/a_division_by_zero_error.rb:3:in
/'`
That is telling us that our error is likely originating on line 3 of this file, lib/a_division_by_zero_error
.
The rest of the error message is very likely just noise.
NameErrors are caused when a given name is invalid or undefined. Whenever the Ruby interpreter encounters a word it doesn't recognize, it assumes that word is the name of a variable or a method. If that word was never defined as either a variable or a method, it will result in a name error.
Syntax errors are pretty self explanatory: they're the result of incorrect syntax. Thankfully, they're usually followed by a guess about the location of the error. For instance:
2.times do
puts "hi"
Will result in:
2: syntax error, unexpected end-of-input, expecting keyword_end
Here, Ruby is saying that on line 2, there is a missing end
(every do
keyword must be followed by some code and then an end
keyword). Always read the full details of syntax errors and look for line numbers, which usually appear at the beginning of the error message.
Let's say you want to calculate the amount that each coworker owes for a lunch order on Seamless. The order came to $64.25 for you and your three coworkers and you all agreed to split the total evenly so you write:
total = "64.25"
num_of_people = 4
price_per_person = total / num_of_people
And you get:
3:in `<main>': undefined method `/' for "64.25":String (NoMethodError)
This error happened because you defined total
as a string, not as a number, and Ruby doesn't know how to divide a string by a number. It's like telling Ruby to divide the word "lemon" by 7—it has no idea what to do. There are two ways around this error:
- Option One: You could change
total
into a float from the start by removing the quotes:
total = 64.25
num_of_people = 4
price_per_person = total / num_of_people
# => 16.0625
- Option Two: You could change
total
from a string to a float in the division step:
total = "64.25"
num_of_people = 4
price_per_person = total.to_f / num_of_people
# => 16.0625
The NoMethodError
will often occur when you have a variable set to nil
(essentially, nothing or no value), without realizing it. Most attempts to use a method on something that equals nil
will result in the NoMethodError
. This is because you are asking an object, nil
in this case, to do something it does not know how to do. In other words, you are calling a method that is not defined for that particular object.
For example:
missing_value = nil
missing_value.length
This will produce the following error
NoMethodError: undefined method `length' for nil:NilClass
Argument errors occur when methods are passed either too few or too many arguments. For instance, let's say you have a simple method, called calculate_interest
which takes the value of a loan and finds the amount of interest that accumulates over the loan's first year given that the annual interest rate is 5.25%:
def calculate_interest(loan_amount)
loan_amount * 0.0525
end
You want to see what happens so you call it below:
def calculate_interest(loan_amount)
loan_amount * 0.0525
end
puts calculate_interest
What results is:
1:in `calculate_interest': wrong number of arguments (0 for 1) (ArgumentError)
This is because you called on the method calculate_interest
which takes one argument, loan_amount
, without passing it a value for loan_amount
. To call on this method, you must pass it a number. For instance, let's say you're considering taking out a loan of $9,400 to buy a fancy La Marzocco espresso machine for your cafe.
def calculate_interest(loan_amount)
loan_amount * 0.0525
end
puts calculate_interest(9400)
Now that you passed the method a value for loan_amount, it will calculate the first year's interest, $493.50.
When you try and do a mathematical operation on two objects of a different type, you will recieve a TypeError. For example if you try and add a string to an integer, Ruby will complain.
1 + "1"
Will produce the following error:
TypeError: String can't be coerced into Fixnum
Another common TypeError is when you try and index into an array with a variable that doesn't evaluate to an integer.
index = "hello"
array = [1,2,3]
array[index]
Will produce the following error:
TypeError: no implicit conversion of String into Integer
Ruby is telling you that it is trying to convert the string you passed as the index to the []
method into an integer, but it can't.
There are many other errors that can occur in Ruby, this just covered the most common errors encountered when beginning to code in Ruby. Ruby errors are pretty descriptive and they are there to help you out, so always see if the error is offering you a hint about your code.