Ruby 3.1 adds error highlighting gem


When working with any programming language, developers deal with error stack traces whenever an exception or an error gets raised. The stack trace gracefully points out the error location and provides information about program subroutines.

When it comes to Ruby, we are aware of the different errors and exceptions raised. Let’s check an example for the same.

// test.rb file
class Addition
  attr_reader :num1, :num2

  def initialize(num1, num2)
    @num1 = num1
    @num2 = num2
  end

  def result
    num1 + num2
  end
end

Addition.new(1, "2").result
Terminal$ ruby test.rb
test.rb:10:in `+': String can't be coerced into Integer (TypeError)
    from test.rb:10:in `result'
    from test.rb:14:in `<main>'

As seen in the above example, IRB pointed out the line number where the error occurred.

(irb):10:in `+': String can't be coerced into Integer (TypeError)

Before

The above error was pretty easy to identify and fix since the input was incorrect. Let’s take an example where we want to parse some API response and fetch user data from the nested JSON object.

// test.rb file
class ParseResponse
  attr_reader :data

  def initialize(data)
    @data = data
  end

  def user_name
    data[:result].first[:first_name]
  end
end

data = { result: {} }
ParseResponse.new(data).user_name
Terminal$ ruby test.rb
Traceback (most recent call last):
    1: from test.rb:14:in `<main>'
test.rb:9:in `user_name': undefined method `[]' for nil:NilClass (NoMethodError)

The error raised undefined method [] for nil:NilClass does not point out whether data[:result] is nil or data[:result].first is nil. For more complex and nested JSON responses, it becomes difficult to identify which param is nil.

After

Ruby 3.1 ships with error_highlight gem and will be required when a Ruby process starts up. We don’t have to add any explicit setup configuration for setting up this gem.

When we execute the above code using Ruby 3.1.0, we can see at which point the data was nil.

Terminal$ ruby test.rb
test.rb:9:in `user_name': undefined method `[]' for nil:NilClass (NoMethodError)

    data[:result].first[:first_name]
                       ^^^^^^^^^^^^^
    from test.rb:14:in `<main>'

The ^^^^^^^^^^^^^ part specifies that .first was nil.

If we send the data as empty, the error highlight will point under .first as shown below:

Terminal$ ruby test.rb
test.rb:9:in `user_name': undefined method `first' for nil:NilClass (NoMethodError)

    data[:result].first[:first_name]
                 ^^^^^^
    from test.rb:14:in `<main>'

Custom Formatter

The gem provides us with a facility to add our formatter. We need to override the #message_for method and use assign it to ErrorHighlight.formatter=.

formatter = Object.new
def formatter.message_for(spot)
  marker = " " * spot[:first_column] + "*" * (spot[:last_column] - spot[:first_column] - 1)

  "\n\n#{ spot[:snippet] }#{ marker }"
end

ErrorHighlight.formatter = formatter

When we apply this formatter to our above example the error highlight, we use * instead of ^.

Terminal$ ruby test.rb
test.rb:9:in `user_name': undefined method `first' for nil:NilClass (NoMethodError)

    data[:result].first[:first_name]
                 ******
    from test.rb:14:in `<main>'

Disable error_highlight

We can also disable the error highlight gem by passing --disable-error_highlight to the ruby command.

Terminal$ ruby --disable-error_highlight test.rb
test.rb:9:in `user_name': undefined method `[]' for nil:NilClass (NoMethodError)
    from test.rb:14:in `<main>'

NOTE: error_highlight gem works only on MRI and requires Ruby 3.1 or later.

Join Our Newsletter