Error handling ruby

The simple guide to exception handling in Ruby. ✓ Harden your app against unexpected errors ✓ Respond more effectively ✓ Adapt to any issue that may arise.

Exception Handling in Ruby

In Ruby, error handling works like this; all exceptions and errors are extensions of the Exception class. While this may seem intuitive, exception handling in Ruby is a touch more nuanced than you might expect thanks to the designed hierarchy of Ruby exceptions.

The begin-rescue

Similar to PHP’s try-catch handler, Ruby’s exception handling begins with the begin-rescue block. In a nutshell, the begin-rescue is a code block in Ruby that can be used to deal with raised exceptions without interrupting the Ruby program execution. In other words, you can begin to execute a block of code, and rescue any exceptions that are raised.

Rescuing Exceptions

In Ruby by default, begin-rescue rescues every instance of the StandardError class. This includes no method errors, type errors, runtime errors, and every custom error that is intended to be rescued within a Ruby application (see Raising Exceptions in Ruby for more information). To rescue every StandardError, simply wrap the designated section of code in a begin-rescue block:

begin
  # ...
rescue => e
  # ...
end

In Ruby when a StandardError exception is raised within the begin block, an instance of it will be passed to the rescue block as the variable e (for more information about the structure of Ruby’s Exception class, see Raising Exceptions in Ruby).

Rescuing Specific Exceptions

While rescuing every exception raised in your Ruby app is great for simplistic implementations—such as generalizing API error responses—best practice is to rescue for specific exceptions. To do this, let’s rewrite the generic Ruby begin-rescue block above to specifically rescue StandardError exceptions:

begin
  # ...
rescue StandardError => e
  # ...
end

Although the difference may be subtle, by following the rescue command with a class name, then only exceptions of the defined type will be rescued. For example, if we wanted to rescue all argument errors, we could structure our begin-rescue block like this:

begin
  # ...
rescue ArgumentError => e
  # ...
end

But, what if we want to rescue more than one exception type? Much like an if-elsif-else chain, a begin-rescue block can have multiple rescues, which when combined with a check for the StandardError class, allows you to logically adapt to any and all issues that may arise:

begin
  # ...
rescue ArgumentError => e
  # ...
rescue TypeError => e
  # ...
rescue => e
  # ...
end

Rescuing All Exceptions

While it may be tempting to rescue every child of the Exception class, it is generally considered bad practice due to the way the Ruby exception hierarchy is structured. The reason for this is that, while all Ruby exceptions and errors are an extension of the Exception class, many of them are reserved for use internally by Ruby. For example, SignalException::Interrupt is used to signal that Ctrl-C has been detected during the execution of a script.

If you were to rescue every child of the Exception class, then the Interrupt exception wouldn’t work as expected. That said, if you do want to rescue every exception that is raised in Ruby, the same begin-rescue block can be used to specifically rescue all Exception exceptions:

begin
  # ...
rescue Exception => e
  # ...
end

Using the above begin-rescue block in Ruby will rescue every exception and error that is raised, from interrupts to syntax errors, and even memory errors, so use it sparingly and with caution.

How to check Ruby syntax to identify exceptions

rescueclauses are used to tell Ruby which exception or types of exceptions we want to handle. The syntax for therescuestatement is:

begin
    # may raise an exception
rescue AnException
    # exception handler
rescue AnotherException
    # exception handler
else
    # other exceptions
ensure
    # always executed
end

The code betweenbeginandrescueis where a Ruby exception can occur. If an exception is encountered, the code inside therescueclause gets executed. For eachrescueclause, the raised Ruby exception is compared against each parameter and the match succeeds if the exception in the clause is the same as or a superclass of the thrown exception.

If the thrown Ruby exception does not match any of the specified exception types, theelse block gets executed. Theensureblock is always executed whether a Ruby exception occurs or not.

As an example:

#!/usr/bin/ruby
begin
   file = open("/tmp/myfile")
rescue Errno::ENOENT
   p "File not found"
else
   p "File opened"
end

In the above example, a file is attempted to be opened in thebeginblock. Therescueblock catches a “File not found” Ruby exception in case the file is not found at the location. If the file is found, theelseblock gets executed.

Running the above Ruby code produces the following result if the file is not found:

"File not found"

If the file is found, the following output is produced:

"File not found"

Exception Handling in Ruby on Rails

Generally speaking, the begin-rescue block works as intended in Ruby on Rails. That said, in order to better handle the specific use cases that can come up in the Rails architecture, additional methods have been made available for use within a Ruby on Rails application.

The rescue_from

The rescue_from directive is an exception handler that rescues exceptions raised in controller actions. The rescue_from directive rescues the specified exceptions raised within a controller, and reacts to those exceptions with a defined method. For example, the following controller rescues User::NotAuthorized exceptions and passes them to the deny_access() method:

class ApplicationController < ActionController::Base
  rescue_from User::NotAuthorized, with: :deny_access

  protected
    def deny_access(exception)
    # ...
    end
end

The advantage to rescue_from is that it abstracts the exception handling away from individual controller actions, and instead makes exception handling a requirement of the controller. This not only makes exception handling within controllers more readable, but also more regimented.

Exception Handling in Sinatra

While the additional exception handling within Ruby on Rails is focused on controller exceptions, Sinatra offers a few additional ways to deal with raised exceptions.

Logging Exceptions with dump_errors

Enabled by default, dump_errors is a Sinatra setting that allows exception backtraces to be written directly to STDERR. In the context of a development server, this information can be incredibly valuable, but might be more difficult to act upon in a production environment. When used in conjunction with traditional log aggregation and analysis techniques, however, this is a great way to collect exception data as it happens without needing to reproduce it.

Propagating Exceptions with raise_errors

By default, exceptions raised within Sinatra do not leave the application. What this means is that, when an exception is raised, it is rescued and mapped to internal error handlers. By enabling the raise_errors setting, these exceptions are raised outside of the application, allowing the server handler or Rack middleware to deal with exceptions.

Enable Classy Error Pages with show_exceptions

When working in a development environment, being able to quickly react to exceptions is crucial, which is why exception backtraces and environment information are on-screen by default in these environments. While this setting is turned off in production environments, it can be enabled or disabled by updating the show_exceptions setting.

Custom Error Handling

While Sinatra has built-in support for graceful error handling (error handler), it is sometimes desirable to write custom logic to handle raised errors. To do this, an error block can be used in the same way as a rescue block. For example, to rescue a User::NotAuthorized error, the following directive would work in Sinatra:

error User::NotAuthorized do
  # ...
end

Similarly, if we wanted to rescue all exceptions that are raised in a Sinatra application, we could use the following error handler:

error do
  # ...
end

Check ruby syntax

If you ever need to check the syntax of your Ruby code here is a great way to do so without executing the Ruby code.

This will check the syntax without executing the program:

ruby -c filename.rb

In Ruby, exception handling is a process which describes a way to handle the error raised in a program. Here, error means an unwanted or unexpected event, which occurs during the execution of a program, i.e. at run time, that disrupts the normal flow of the program’s instructions. So these types of errors were handled by the rescue block. Ruby also provides a separate class for an exception that is known as an Exception class which contains different types of methods.
The code in which an exception is raised, is enclosed between the begin/end block, so you can use a rescue clause to handle this type of exception.

Syntax:

begin
    raise
      # block where exception raise

    rescue
      # block where exception rescue
end

Example:

def raise_and_rescue     

  begin

    puts 'This is Before Exception Arise!'

    raise 'Exception Created!'

    puts 'After Exception' 

  rescue    

    puts 'Finally Saved!'    

end    

puts 'Outside from Begin Block!'    

end    

raise_and_rescue   

Output:

This is Before Exception Arise!
Finally Saved!
Outside from Begin Block!

Explanation: In the above program, an exception is raised in the begin block (raise block) which interrupt the flow of execution of the program. To overcome this situation, use the rescue block to handle the raised exception. When rescue block is used, it handles the exception and continues the execution of the program.

Note: Multiple rescue clauses can be used in the same program which means if an exception is not handled by the first rescue clause, then another rescue clause will definitely handle the exception. If no rescue clause match, or if an exception occurs outside the begin/end block, then Ruby moves up to the stack and looks for an exception handler in the caller.

The statements used in Exceptions are:

  1. retry Statement: This statement is used to execute the rescue block again from the beginning after capturing the exception.

    Syntax:

    begin
        # block where exception raise
    
    rescue
        # block where an exception is captured
    
    retry
    
        # this statement restarts the rescue
        # block from the beginning
    end
    

    Example:

      begin

        raise 'Exception Created!'

        puts 'After Exception' 

      rescue    

        puts 'Finally Saved!'

    retry

    end    

    Output:

    Finally Saved!
    Finally Saved!
    Finally Saved!
    Finally Saved!
    Finally Saved!
    Finally Saved!
    .
    .
    .
    .
    

    Note: Be careful while using retry statement because it may result into infinite loop.

  2. raise Statement: This statement is used to raise an exception.

    Syntax:

    raise

    This syntax is used to re-raise the current exception. It is generally used by the exception handler where an exception is interrupted before passing it on.

    raise "Error Message"

    This syntax is used to create a RuntimeError exception and it raises up the call stack.

    raise ExceptionType, "Error Message"

    In this syntax, the first argument is used to create an exception and then set the message in the second argument.

    raise ExceptionType, "Error Message" condition

    In this syntax, the first argument is used to create an exception and then set the message in the second argument. You can also set a condition statement to raise an exception.

    Example:

    begin

        puts 'This is Before Exception Arise!'

           raise 'Exception Created!'

        puts 'After Exception' 

    end

    Output:

    This is Before Exception Arise!
    Exception Created!
    
  3. ensure Statement: This statement ensures that required instructions will execute at the end of the code, whether the exception raise or raised exception is rescued, or the program terminates due to an uncaught exception. This block always gives output. This block is placed over the rescue block.

    Syntax:

    begin
         # exception raise
    
    rescue
        # exception rescue
    
    ensure
        # this block always executes
    end
    

    Example:

      begin

        raise 'Exception Created!'

        puts 'After Exception' 

      rescue    

        puts 'Finally Saved!'

    ensure

       puts 'ensure block execute'

    end    

    Output:

    Finally Saved!
    ensure block execute
    
  4. else Statement: This statement is present in between the rescue block and ensure block. This block only executes when no exception is raised.

    Syntax:

    begin
      rescue
        # exception rescue
      
      else
        # this block executes when no exception raise
    
      ensure
        # this block always executes
    end
    

    Example:

     begin

        puts 'no Exception raise' 

        rescue    

            puts 'Finally Saved!'

       else

            puts 'Else block execute because of no exception raise'

       ensure

          puts 'ensure block execute'

    end    

    Output:

    no Exception raise
    Else block execute because of no exception raise
    ensure block execute
    
  5. Catch and Throw in Exception Handling

    In Ruby, catch and throw blocks are the lightweight mechanism for error handling and used to jump from the exception when there is no additional work is available in the program.
    The catch block is used to jump out from the nested block and the block is labeled with a name. This block works normally until it encounters with the throw block. catch and throw method will work faster than raise and rescue method. When the throw statement is encountered, Ruby will check the call stack for the catch statement with the related symbol. The throw statement never executes and always return nil.

    Syntax:

    throw :label_name
        # this block will not be executed
    
    catch :label_name do
        # matching catch will be executed when the throw block encounter
    end
    

    You can also apply a condition in catch and throw statement as shown below:

    throw :label_name condition
         # this block will not be executed
    
    catch :label_name do
         # matching catch will be executed when the throw block encounter
    end
    

    Example:

    def catch_and_throw(value)

      puts value

      a = readline.chomp

      throw :value_e if a == "!"

      return a

    end

    catch :value_e do

      number = catch_and_throw("Enter Number: ")

    end

    Input:

    Enter Number: 1
    

    Output:

    1
    

    Input:

    Enter Number: !
    

    Output:

    nil
    

    Explanation: In the above example the catch_and_throw method is used to print the value enter in the catch statement. If the user enters a number, then it will print number, but if the user enters !, then it will give you nil. Because the throw statement has effectively never executed.

 on
November 19, 2019

What is exception handling?

Software systems can be quite prone to error conditions. Systems that involve user interaction are more vulnerable to exceptions as they attract errors at multiple fronts. Errors can take many forms — syntactical errors, network errors, form input errors, invalid authentication errors etc. If not accounted for, these can affect user experience and can even manifest as security loopholes, enabling attackers to compromise the system.

Exception handling is used to prevent system failures and to save the user from lousy error messages.

In most cases, when an error condition occurs, the programming language forces the program to terminate and therefore program control is lost. Exception handling allows you (or the program) to not lose control and to be able to deal with what happens after the error has occurred. This gives you some room to make amends — to show affable error messages, to release some resources or to conveniently redirect the user to a relevant page. 

Exception handling helps you clear up the mess, without making it look like one.

It is the process of accommodating error conditions and responding to them programmatically, resulting in execution of a presumably alternate, but already planned sequence of code.

Exception handling in Ruby

In the vast majority of languages, exception handling is performed using “try, throw and catch” mechanisms. When the try block is encountered, the compiler becomes super aware in the lookout for an exception (thrown either manually, or by the language itself) and instructs the catch block to deal with it. Ruby, however decided to go with different names for their conventional exception handling system. 

I find the Ruby terminology for dealing with exceptions a lot more poetic. 

There is a beginning, followed by errors raised and eventually rescued. And like all good things, it comes to an end, unless you want to retry.

begin
    # raise exception here

rescue
    # raised exception handled here

retry
    # retrying the code in the begin block


ensure
    # Always executed

end

Exception handling in Ruby primarily consists of —

  • Begin — end block
  • Raise
  • Rescue
  • Retry
  • Ensure

Begin — end block, raise, rescue and ensure are analogous to the programming concepts of try, throw, catch and finally. Let’s try to understand them.

Note: Ruby also provides throw and catch functions for handling exceptions, which are lightweight counterparts of raise and rescue that do not build stack traces of the exceptions. Even though raise and rescue based mechanisms are more prevalent among Ruby developers, we’ll see how Ruby’s throw and catch mechanisms can help later in this post.

2.1 Begin — end block

The error prone part of your code comes in the begin block. The corresponding exception handling, through the raise and rescue mechanisms, also happens inside the begin block.

You can use begin blocks to sectionalize the different types of errors that are likely to show up. Differently erroneous codes can be put into different begin — end blocks and can accordingly be handled.

begin
    # code likely to give error comes in this block
    # raising and rescuing something
end

begin
    # raising and rescuing something else
end

Note: The body of a Ruby method can also act as begin-end block and thus does not require an explicit ‘begin’ call.

def raise_and_rescue
  # begin call not necessary 
  # raising and rescuing
end  

2.2 Raise

An exception can either be raised manually by the developer, using the raise command, or automatically by the language itself. Exceptions are raised automatically on incurring a syntactical error, like a variable not declared, a function not defined or an invalid math operation etc.

Based on your application, custom exception conditions can be catered to by manually raising errors. 

A very common use case would be a signup webpage that doesn’t submit the form until you have entered (and/or re-entered) a valid password. This sort of client-side proofing can save you from an unnecessary HTTP request to your server which might either affect database integrity or respond with an error message, which will need to be handled later. 

Let’s see how we can raise an exception in Ruby — 

begin  
    # if some condition is met or not met ->   
    raise "I am the error message!"
    # below lines are not executed    
end  

Here we are just raising an exception, not handling it (yet). This results in the program being terminated where the exception is raised, and as a result, the lines below are not executed. A language-generated error message is output as shown below — 

Ruby1.png

To prevent this, we can handle the exceptions in our own way, by using rescue blocks.

2.3 Rescue to the rescue

Fire.gif

Rescue blocks are like catch blocks that take control of the program after an exception has been raised. Raised exceptions can be followed by one or more rescue blocks to handle the exceptions based on their type. A generic rescue call looks like this — 

begin
    # if some condition is met or not met ->
    raise 'I am the error message!'

    puts 'I am not executed.'
rescue    
    puts 'Rescue to the rescue!'
end    

puts 'Amicably exiting ..'  

Once the exception is handled, execution resumes from after the begin block.

Ruby2.png

The rescue call defaults to a StandardError parameter. Rescue calls can however be made to handle specific exceptions. Some common Ruby errors (also common across other languages) are — 

  • IndexError
  • NameError
  • RangeError
  • TypeError
  • ZeroDivisionError
  • RuntimeError

Rescue blocks can be made to cater to certain types (classes) of exceptions.

In this way, raised exceptions can be followed by multiple rescue clauses, each responsible for handling a different type of exception. Let’s see how we can do this in code — 

begin  
  # error raised here 
rescue IndexError  
  # dealing in one way
rescue RangeError  
  # dealing in another way  
end 

Arguments can be used with the rescue clause to act as placeholders for the rescued exception — 

begin  
    raise 'I am the error message!'  
rescue StandardError => e  
    # inspecting the raised exception
    puts e.message

    # information about where the exception was raised  
    puts e.backtrace.inspect  
end 

Ruby3.png

The output tells how the exception was raised in line 2 of our code.

Specific types (classes) of exceptions can be raised even with custom error messages. For example —

begin
raise ZeroDivisionError, "Custom error message"
rescue ZeroDivisionError => e  
    # inspecting the raised exception
    puts e.message  
    puts e.backtrace.inspect
end 

Ruby4.png2.4 Else

Sometimes it’s possible that there was nothing to rescue from, that no exception was raised. The else clause can be used in this case as such — 

begin  
rescue StandardError => e  
    # inspecting the raised exception
    puts e.message  
    puts e.backtrace.inspect
else
    puts 'No exception to capture?'
end 

Ruby5.pngMagic variables

When exceptions are raised, Ruby assigns two variables — $! and $@ with information about the raised exception.

  • $! stores the exception message
  • $@ stores the backtrace
begin
raise ZeroDivisionError, "Custom error message"
rescue ZeroDivisionError => e
    # inspecting the raised exception
    puts $! # equivalent to e.message
    puts $@ # equivalent to e.backtrace.inspect
end 

Ruby6.png
2.5 Retry

Well, what if you don’t want to give up? You might want to keep trying till it works. 

undefined

This could be a common case when network connections are inconsistent. 

As I type this post on a Google document, at my home, on a poor internet connection, I can see the webpage continuously trying to keep me connected to the internet.

Till now we have seen how rescue blocks can be used to deal with what happens after an exception has occurred. The retry clause allows us to run the begin block again. This can come in handy after we have fixed what caused the exception to happen in the first place. 

Let’s see how retry can help, with a rather simple example. 

We know division of a number by zero is undefined. In the example below, we try to divide a by b (a / b). Initially the denominator, b is zero and therefore the division would raise a ZeroDivisionError. In the rescue block, we change the denominator to 1 and retry the whole thing.

a = 10
b = 0
begin
puts a/b
rescue ZeroDivisionError => e  
    # inspecting the raised exception
    puts e.message  
    puts e.backtrace.inspect
    b = 1
    retry
else
    puts 'No exception to capture?'
    puts 'Division was successful!'
end 

Ruby7.png

2.6 Ensure

Ensure is like the finally block that we have in other programming languages. It is there to always run at the end of each begin block. Regardless of any control sequence that the program ends up following, the ensure block is always executed at the end of it’s parent begin block. 

begin
    # if some condition is met or not met ->
    raise 'I am the error message!'
rescue    
    puts 'Rescue to the rescue!'
ensure
    puts 'Always executed at the end.'
end    

Ruby8.pngEnsure blocks are executed even if there isn’t a rescue block to handle a thrown exception. 

begin
    # if some condition is met or not met ->
    puts 'Begin!'
    raise 'I am the error message!'
ensure
    puts 'Executed even when there is no rescue clause'
end 

Ruby9.png

Note how in this case, even though the ensure block is executed at the end, the raised exception manifests as a runtime error after the ensure block.

Ensure blocks are generally used to free up any resources after an exception has been raised eg. closing database connections, closing file handlers etc. Without such blocks, the program abruptly terminates without clearing the resources.

Throw and catch in Ruby — lightweight alternatives

It’s not a good idea to exceptionally over-handle your code. Extensive raising and rescuing can take a toll on the system’s performance. 

Each time an exception is raised, a stack trace is built that helps in debugging the error. Too many of such handled exceptions can soon become a terrible bottleneck.

Turns out there is a lightweight alternative to the conventional approach that allows you to smoothly transfer program control out of complex nested blocks. Throw clauses and catch blocks in Ruby are linked through a common label name that both share.

throw :labelname
# ..
catch :labelname do
# ..
end

You can also use conditional throw calls as such — 

throw :labelname if a > b
# ..
catch :labelname do
# ..
end

Throw and catch in Ruby are quite different from conventional throw-catch mechanisms in other languages. In Ruby, they are used more for terminating execution quickly, and not as much for handling exceptions. 

It can come in handy when you are working with nested for loops and you want to get out of the computationally expensive mess when a condition is met, without having to use multiple break statements. Throw helps program control to move around swiftly to help programs execute faster, in a more efficient manner, compared to slow conventional begin/raise/rescue mechanisms that are more concerned about the raised exception, where it happened etc.

The way that throw and catch are used is that throw calls are made from inside catch blocks, transferring control from deep inside a nested construct, back to the level of the catch block in the code.

catch(:get_me_out) do
  # arbitrary nested for loop
  for a in 1..5 do
    for b in 1..5 do
      # arbitrary condition
      if a + b == 7
        # throwing to get out of the nested loops
        throw(:get_me_out)
      end
    end
  end
end

puts 'Good to go!'

Ruby10.png

A throw function can also be called with a custom message as an argument, which the catch block ends up returning after something is thrown.

thrown_msg = catch(:get_me_out) do

  # arbitrary nested for loop
  for a in 1..5 do
    for b in 1..5 do

      # arbitrary condition
      if a + b == 7

        # throwing to get out of the nested loops
        throw(:get_me_out, 'Inside this mess, get me out')
      end
    end
  end
end

puts thrown_msg
puts 'Good to go!'

Ruby11.png

Custom Exception class in Ruby

We can create our own Exception classes that cater to specific requirements in our projects. This can be done by inheriting from the provided StandardError class.

class MyCustomError < StandardError
end

raise MyCustomError

Ruby12.png

It’s good to have an error message to accompany the raised exception. We can do this by overriding the initialize method for our custom class with a default msg argument.

class MyCustomError < StandardError
  def initialize(msg="Custom error message")
    # to override the method
    super
  end
end

raise MyCustomError

Ruby13.png

Wrapping it up

wrap.gif

In this post, we looked at —

  • What is exception handling (in general)
  • Exception handling in Ruby —
    • Begin — end block
    • Raise
    • Rescue
    • Else
    • Retry
    • Ensure
  • Throw and catch in Ruby
  • Custom exception classes

All in all, we learnt how Ruby prefers a different terminology for dealing with exceptions. Except for the retry function, the begin / raise / rescue paradigm resonates with the conventional try / throw / catch setup used across most other programming languages. Throw and catch in Ruby however help in quickly getting yourself out of complex constructs. We also saw how we can create our application-specific custom Exception classes that inherit from the StandardError class.

Try ScoutAPM

Thanks for reading our blog. Now that you have a handle on handling exceptions, you need the best way to find them in your application.  Sign up here today for a free trial or contact us with any queries you might have  Want to show off Scout to your fellow developers? Click here for some free ScoutAPM swag.

Содержание

  1. Ruby | Exception handling
  2. Error Handling in Ruby: Part I
  3. What’s an error in Ruby ?
  4. How to throw & catch an error ?
  5. Errors & magic variables
  6. How to Handle Exceptions in Ruby
  7. Exception Handling in Ruby
  8. The begin-rescue
  9. Rescuing Exceptions
  10. Rescuing Specific Exceptions
  11. Rescuing All Exceptions
  12. How to check Ruby syntax to identify exceptions
  13. Exception Handling in Ruby on Rails
  14. The rescue_from
  15. Exception Handling in Sinatra
  16. Logging Exceptions with dump_errors
  17. Propagating Exceptions with raise_errors
  18. Enable Classy Error Pages with show_exceptions
  19. Custom Error Handling
  20. Check ruby syntax
  21. Exception Handling in Ruby
  22. What is exception handling?
  23. Exception handling in Ruby
  24. 2.1 Begin — end block
  25. 2.2 Raise
  26. 2.3 Rescue to the rescue
  27. 2.4 Else
  28. 2.5 Retry
  29. 2.6 Ensure
  30. Throw and catch in Ruby — lightweight alternatives
  31. Custom Exception class in Ruby
  32. Wrapping it up
  33. Try ScoutAPM

Ruby | Exception handling

In Ruby, exception handling is a process which describes a way to handle the error raised in a program. Here, error means an unwanted or unexpected event, which occurs during the execution of a program, i.e. at run time, that disrupts the normal flow of the program’s instructions. So these types of errors were handled by the rescue block. Ruby also provides a separate class for an exception that is known as an Exception class which contains different types of methods.
The code in which an exception is raised, is enclosed between the begin/end block, so you can use a rescue clause to handle this type of exception.

Syntax:

Example:

Output:

Explanation: In the above program, an exception is raised in the begin block (raise block) which interrupt the flow of execution of the program. To overcome this situation, use the rescue block to handle the raised exception. When rescue block is used, it handles the exception and continues the execution of the program.

Note: Multiple rescue clauses can be used in the same program which means if an exception is not handled by the first rescue clause, then another rescue clause will definitely handle the exception. If no rescue clause match, or if an exception occurs outside the begin/end block, then Ruby moves up to the stack and looks for an exception handler in the caller.

The statements used in Exceptions are:

Источник

Error Handling in Ruby: Part I

In this article, we’re going to explore the following topics:

  • what’s an error in Ruby ?
  • how to throw & catch an error ?
  • errors & magic variables

What’s an error in Ruby ?

As Ruby is a fully object-oriented language, an error is an instance of a class that includes the Exception class in its ancestor chain

That’s why an error is commonly called an “exception” in Ruby.

NB: feel free to have a look to the Ruby Object Model: Part I article if you’re unfamiliar with the ancestor chain in Ruby.

As an error is a class, Ruby provides a bunch of instance methods to deal with an error. These methods are defined in the Exception class and used by all of its children.

For example, the Exception#message method returns the message associated to the error when the Exception#backtrace method returns the error backtrace. Feel free to browse the Exception official documentation for further information.

How to throw & catch an error ?

The Kernel#raise method is in charge of raising errors in Ruby

In the first call to Kernel#raise , we can see that the method raises a RuntimeError — as no error class is explicitly specified as argument. On the other hand, in the second call to Kernel#raise the raised exception is a NoMethodError — as we defined it as first argument of the method call.

These exceptions can be caught by a rescue clause within a begin. end block

Here, the rescue clause caught a NoMethodError exception raised by the call to Kernel#raise within the begin. end block. The begin. end block is used to compartmentalize blocks of code depending on the possible errors that can occur for each of them

Note that in the first begin. end block, the 3 / 0 operation internally raised the ZeroDivisionError via the Integer#/ method.

Feel free to read the Numbers and Class Hierarchy in Ruby article if you are unfamiliar with the Integer class and its ancestor chain.

It’s same for the second begin. end block where the BasicObject#method_missing Ruby hook method raised the NoMethodError .

Feel free to read the Metaprogramming: Ruby Hook Methods article if you are unfamiliar with the hook methods in Ruby.

Errors & magic variables

Ruby provides 2 magic variables that are set when an exception is raised:

  • The $! variable that contains the raised exception
  • The $@ variable that contains the exception backtrace

These variables are cleared — set to nil — when the rescue block ends.

Источник

How to Handle Exceptions in Ruby

Exception Handling in Ruby

In Ruby, error handling works like this; all exceptions and errors are extensions of the Exception class. While this may seem intuitive, exception handling in Ruby is a touch more nuanced than you might expect thanks to the designed hierarchy of Ruby exceptions.

The begin-rescue

Similar to PHP’s try-catch handler, Ruby’s exception handling begins with the begin-rescue block. In a nutshell, the begin-rescue is a code block in Ruby that can be used to deal with raised exceptions without interrupting the Ruby program execution. In other words, you can begin to execute a block of code, and rescue any exceptions that are raised.

Rescuing Exceptions

In Ruby by default, begin-rescue rescues every instance of the StandardError class. This includes no method errors, type errors, runtime errors, and every custom error that is intended to be rescued within a Ruby application (see Raising Exceptions in Ruby for more information). To rescue every StandardError , simply wrap the designated section of code in a begin-rescue block:

In Ruby when a StandardError exception is raised within the begin block, an instance of it will be passed to the rescue block as the variable e (for more information about the structure of Ruby’s Exception class, see Raising Exceptions in Ruby).

Rescuing Specific Exceptions

While rescuing every exception raised in your Ruby app is great for simplistic implementations—such as generalizing API error responses—best practice is to rescue for specific exceptions. To do this, let’s rewrite the generic Ruby begin-rescue block above to specifically rescue StandardError exceptions:

Although the difference may be subtle, by following the rescue command with a class name, then only exceptions of the defined type will be rescued. For example, if we wanted to rescue all argument errors, we could structure our begin-rescue block like this:

But, what if we want to rescue more than one exception type? Much like an if-elsif-else chain, a begin-rescue block can have multiple rescues , which when combined with a check for the StandardError class, allows you to logically adapt to any and all issues that may arise:

Rescuing All Exceptions

While it may be tempting to rescue every child of the Exception class, it is generally considered bad practice due to the way the Ruby exception hierarchy is structured. The reason for this is that, while all Ruby exceptions and errors are an extension of the Exception class, many of them are reserved for use internally by Ruby. For example, SignalException::Interrupt is used to signal that Ctrl-C has been detected during the execution of a script.

If you were to rescue every child of the Exception class, then the Interrupt exception wouldn’t work as expected. That said, if you do want to rescue every exception that is raised in Ruby, the same begin-rescue block can be used to specifically rescue all Exception exceptions:

Using the above begin-rescue block in Ruby will rescue every exception and error that is raised, from interrupts to syntax errors, and even memory errors, so use it sparingly and with caution.

How to check Ruby syntax to identify exceptions

rescue clauses are used to tell Ruby which exception or types of exceptions we want to handle. The syntax for the rescue statement is:

The code between begin and rescue is where a Ruby exception can occur. If an exception is encountered, the code inside the rescue clause gets executed. For each rescue clause, the raised Ruby exception is compared against each parameter and the match succeeds if the exception in the clause is the same as or a superclass of the thrown exception.

If the thrown Ruby exception does not match any of the specified exception types, the else block gets executed. The ensure block is always executed whether a Ruby exception occurs or not.

In the above example, a file is attempted to be opened in the begin block. The rescue block catches a “File not found” Ruby exception in case the file is not found at the location. If the file is found, the else block gets executed.

Running the above Ruby code produces the following result if the file is not found:

If the file is found, the following output is produced:

Exception Handling in Ruby on Rails

Generally speaking, the begin-rescue block works as intended in Ruby on Rails. That said, in order to better handle the specific use cases that can come up in the Rails architecture, additional methods have been made available for use within a Ruby on Rails application.

The rescue_from

The rescue_from directive is an exception handler that rescues exceptions raised in controller actions. The rescue_from directive rescues the specified exceptions raised within a controller, and reacts to those exceptions with a defined method. For example, the following controller rescues User::NotAuthorized exceptions and passes them to the deny_access() method:

The advantage to rescue_from is that it abstracts the exception handling away from individual controller actions, and instead makes exception handling a requirement of the controller. This not only makes exception handling within controllers more readable, but also more regimented.

Exception Handling in Sinatra

While the additional exception handling within Ruby on Rails is focused on controller exceptions, Sinatra offers a few additional ways to deal with raised exceptions.

Logging Exceptions with dump_errors

Enabled by default, dump_errors is a Sinatra setting that allows exception backtraces to be written directly to STDERR . In the context of a development server, this information can be incredibly valuable, but might be more difficult to act upon in a production environment. When used in conjunction with traditional log aggregation and analysis techniques, however, this is a great way to collect exception data as it happens without needing to reproduce it.

Propagating Exceptions with raise_errors

By default, exceptions raised within Sinatra do not leave the application. What this means is that, when an exception is raised, it is rescued and mapped to internal error handlers. By enabling the raise_errors setting, these exceptions are raised outside of the application, allowing the server handler or Rack middleware to deal with exceptions.

Enable Classy Error Pages with show_exceptions

When working in a development environment, being able to quickly react to exceptions is crucial, which is why exception backtraces and environment information are on-screen by default in these environments. While this setting is turned off in production environments, it can be enabled or disabled by updating the show_exceptions setting.

Custom Error Handling

While Sinatra has built-in support for graceful error handling (error handler), it is sometimes desirable to write custom logic to handle raised errors. To do this, an error block can be used in the same way as a rescue block. For example, to rescue a User::NotAuthorized error, the following directive would work in Sinatra:

Similarly, if we wanted to rescue all exceptions that are raised in a Sinatra application, we could use the following error handler:

Check ruby syntax

If you ever need to check the syntax of your Ruby code here is a great way to do so without executing the Ruby code.

This will check the syntax without executing the program:

Источник

Exception Handling in Ruby

What is exception handling?

Software systems can be quite prone to error conditions. Systems that involve user interaction are more vulnerable to exceptions as they attract errors at multiple fronts. Errors can take many forms — syntactical errors, network errors, form input errors, invalid authentication errors etc. If not accounted for, these can affect user experience and can even manifest as security loopholes, enabling attackers to compromise the system.

Exception handling is used to prevent system failures and to save the user from lousy error messages.

In most cases, when an error condition occurs, the programming language forces the program to terminate and therefore program control is lost. Exception handling allows you (or the program) to not lose control and to be able to deal with what happens after the error has occurred. This gives you some room to make amends — to show affable error messages, to release some resources or to conveniently redirect the user to a relevant page.

Exception handling helps you clear up the mess, without making it look like one.

It is the process of accommodating error conditions and responding to them programmatically, resulting in execution of a presumably alternate, but already planned sequence of code.

Exception handling in Ruby

In the vast majority of languages, exception handling is performed using “try, throw and catch” mechanisms. When the try block is encountered, the compiler becomes super aware in the lookout for an exception (thrown either manually, or by the language itself) and instructs the catch block to deal with it. Ruby, however decided to go with different names for their conventional exception handling system.

I find the Ruby terminology for dealing with exceptions a lot more poetic.

There is a begin ning, followed by errors raise d and eventually rescue d. And like all good things, it comes to an end , unless you want to retry .

Exception handling in Ruby primarily consists of —

Begin — end block, raise, rescue and ensure are analogous to the programming concepts of try, throw, catch and finally. Let’s try to understand them.

Note: Ruby also provides throw and catch functions for handling exceptions, which are lightweight counterparts of raise and rescue that do not build stack traces of the exceptions. Even though raise and rescue based mechanisms are more prevalent among Ruby developers, we’ll see how Ruby’s throw and catch mechanisms can help later in this post.

2.1 Begin — end block

The error prone part of your code comes in the begin block. The corresponding exception handling, through the raise and rescue mechanisms, also happens inside the begin block.

You can use begin blocks to sectionalize the different types of errors that are likely to show up. Differently erroneous codes can be put into different begin — end blocks and can accordingly be handled.

Note: The body of a Ruby method can also act as begin-end block and thus does not require an explicit ‘begin’ call.

2.2 Raise

An exception can either be raised manually by the developer, using the raise command, or automatically by the language itself. Exceptions are raised automatically on incurring a syntactical error, like a variable not declared, a function not defined or an invalid math operation etc.

Based on your application, custom exception conditions can be catered to by manually raising errors.

A very common use case would be a signup webpage that doesn’t submit the form until you have entered (and/or re-entered) a valid password. This sort of client-side proofing can save you from an unnecessary HTTP request to your server which might either affect database integrity or respond with an error message, which will need to be handled later.

Let’s see how we can raise an exception in Ruby —

Here we are just raising an exception, not handling it (yet). This results in the program being terminated where the exception is raised, and as a result, the lines below are not executed. A language-generated error message is output as shown below —

To prevent this, we can handle the exceptions in our own way, by using rescue blocks.

2.3 Rescue to the rescue

Rescue blocks are like catch blocks that take control of the program after an exception has been raised. Raised exceptions can be followed by one or more rescue blocks to handle the exceptions based on their type. A generic rescue call looks like this —

Once the exception is handled, execution resumes from after the begin block.

The rescue call defaults to a StandardError parameter. Rescue calls can however be made to handle specific exceptions. Some common Ruby errors (also common across other languages) are —

  • IndexError
  • NameError
  • RangeError
  • TypeError
  • ZeroDivisionError
  • RuntimeError

Rescue blocks can be made to cater to certain types (classes) of exceptions.

In this way, raised exceptions can be followed by multiple rescue clauses, each responsible for handling a different type of exception. Let’s see how we can do this in code —

Arguments can be used with the rescue clause to act as placeholders for the rescued exception —

The output tells how the exception was raised in line 2 of our code.

Specific types (classes) of exceptions can be raised even with custom error messages. For example —

2.4 Else

Sometimes it’s possible that there was nothing to rescue from, that no exception was raised. The else clause can be used in this case as such —

When exceptions are raised, Ruby assigns two variables — $! and $@ with information about the raised exception.

  • $! stores the exception message
  • $@ stores the backtrace

2.5 Retry

Well, what if you don’t want to give up? You might want to keep trying till it works.

This could be a common case when network connections are inconsistent.

As I type this post on a Google document, at my home, on a poor internet connection, I can see the webpage continuously trying to keep me connected to the internet.

Till now we have seen how rescue blocks can be used to deal with what happens after an exception has occurred. The retry clause allows us to run the begin block again. This can come in handy after we have fixed what caused the exception to happen in the first place.

Let’s see how retry can help, with a rather simple example.

We know division of a number by zero is undefined. In the example below, we try to divide a by b (a / b). Initially the denominator, b is zero and therefore the division would raise a ZeroDivisionError. In the rescue block, we change the denominator to 1 and retry the whole thing.

2.6 Ensure

Ensure is like the finally block that we have in other programming languages. It is there to always run at the end of each begin block. Regardless of any control sequence that the program ends up following, the ensure block is always executed at the end of it’s parent begin block.

Ensure blocks are executed even if there isn’t a rescue block to handle a thrown exception.

Note how in this case, even though the ensure block is executed at the end, the raised exception manifests as a runtime error after the ensure block.

Ensure blocks are generally used to free up any resources after an exception has been raised eg. closing database connections, closing file handlers etc. Without such blocks, the program abruptly terminates without clearing the resources.

Throw and catch in Ruby — lightweight alternatives

It’s not a good idea to exceptionally over-handle your code. Extensive raising and rescuing can take a toll on the system’s performance.

Each time an exception is raised, a stack trace is built that helps in debugging the error. Too many of such handled exceptions can soon become a terrible bottleneck.

Turns out there is a lightweight alternative to the conventional approach that allows you to smoothly transfer program control out of complex nested blocks. Throw clauses and catch blocks in Ruby are linked through a common label name that both share.

You can also use conditional throw calls as such —

Throw and catch in Ruby are quite different from conventional throw-catch mechanisms in other languages. In Ruby, they are used more for terminating execution quickly, and not as much for handling exceptions.

It can come in handy when you are working with nested for loops and you want to get out of the computationally expensive mess when a condition is met, without having to use multiple break statements. Throw helps program control to move around swiftly to help programs execute faster, in a more efficient manner, compared to slow conventional begin/raise/rescue mechanisms that are more concerned about the raised exception, where it happened etc.

The way that throw and catch are used is that throw calls are made from inside catch blocks, transferring control from deep inside a nested construct, back to the level of the catch block in the code.

A throw function can also be called with a custom message as an argument, which the catch block ends up returning after something is thrown.

Custom Exception class in Ruby

We can create our own Exception classes that cater to specific requirements in our projects. This can be done by inheriting from the provided StandardError class.

It’s good to have an error message to accompany the raised exception. We can do this by overriding the initialize method for our custom class with a default msg argument.

Wrapping it up

In this post, we looked at —

  • What is exception handling (in general)
  • Exception handling in Ruby —
    • Begin — end block
    • Raise
    • Rescue
    • Else
    • Retry
    • Ensure
  • Throw and catch in Ruby
  • Custom exception classes

All in all, we learnt how Ruby prefers a different terminology for dealing with exceptions. Except for the retry function, the begin / raise / rescue paradigm resonates with the conventional try / throw / catch setup used across most other programming languages. Throw and catch in Ruby however help in quickly getting yourself out of complex constructs. We also saw how we can create our application-specific custom Exception classes that inherit from the StandardError class.

Try ScoutAPM

Thanks for reading our blog. Now that you have a handle on handling exceptions, you need the best way to find them in your application. Sign up here today for a free trial or contact us with any queries you might have . Want to show off Scout to your fellow developers? Click here for some free ScoutAPM swag.

Источник

Learn more on ruby with our tutorial Setting Up Automated Testing with RSpec on SitePoint.

Alert - Finely crafted line icons

Imagine you’re riding a bike. Now, imagine the designers of that bike built it so it rides smoothly only on roads without bumps and encountering one would result in the entire bicycle breaking! You wouldn’t want that, would you? Yet this is how thousands of software developers design their software every single day. They put error handling in as an afterthought, dealing with it only when it’s inevitable.

The truth is, it’s not their fault. Most of the material on this subject is very basic, covering simple things like raising an error, rescuing it, different error types and…that’s about it. This article will attempt to go deeper than that. I assume you’re familiar with the basics of error handling (using raise, begin/rescue, what StandardError is, error inheritance). That’s the only prerequisite for reading this article. Let’s begin.

What Did We Do Before Raising/Handling Exceptions?

Before exceptions were invented, the primary method of communication that something in the program has failed was through error return codes. As time passed, people looked at ways to clearly distinguish between what their program does and what would happen if it didn’t do what it was supposed to (return codes were far from ideal for this purpose) do. Thus, the invention of language constructs like:

  • raise
  • rescue
  • begin/end (Many other languages use different wording, like try/catch or throw, but the idea behind it remains the same.)

There are opposing views to using exceptions and error handling in the first place. Some of these points make sense and we’ll discuss them later in the article. For now, let’s get you familiar with some of the ways of handling errors in Ruby that can help you manage them better.

Cleanup Before Crashing

Often we have no idea when our program is going to crash. What if we needed to do some cleanup operations before our program ends (due to an error)? That’s where at_exit comes to the rescue:

puts 'Hello'
at_exit do
  puts 'Exiting'
end
raise 'error'

This program will print both “Hello” and “Exiting”. The code inside at_exit will execute when the program exits (whether it’s normally or with an exception). If you want it to run only when an exception is raised, use the global variable $! in Ruby. The default value of $! is nil, it is set to reference the exception object when an error occurs:

puts 'Hello'
at_exit do
  if $! # If the program exits due to an exception
    puts 'Exiting'
  end
end
raise 'error' # Try running this code with this line and then remove it. See when "Exiting" will print.

People use at_exit for all sorts of things, like making your own error crash logger (printing the message in $! to a file), communicating to another app that the running application is no longer running and so on.

You can also pass a Proc object to at_exit instead of a block, just like with any other Ruby method that accept blocks:

puts 'Hello'
to_execute = Proc.new { puts 'Exiting' }
at_exit(&to_execute) # & converts the Proc object to a block

Exit With a Bang

Let’s assume you’ve implemented at_exit in your program. There are instances, however, when you don’t want the block inside it to execute. What should you do in that case? Should you delete/comment out everything inside at_exit and run your program again?

There’s a better way to add and remove code all the time. In Ruby, there’s a method called exit! which is a “dangerous version” of exit. The major “danger” is that when you call it, there are two things happening that wouldn’t ordinarily happen if using the regular version:

  • The program will quit without executing the code block inside at_exit.
  • The exit code is being set to 1 instead of 0 as it is with the regular (non-bang) version.

Simple Error Logging Anywhere In Your Code

I’ll share my favorite method of implementing your own exception logger (If you’re interested in more ways of doing this, I highly recommend Exceptional Ruby by Avdi Grimm, the most comprehensive resource on this subject.) This approach can be used for all sorts of purposes once you grasp the main idea.

We can use a thread-local variable in Ruby to store our error messages:

(Thread.current[:errors] ||= []) << 'Any error message goes here'

Thread.current[:errors] as a variable is available anywhere in your code (I’m normally against variables with global scope, with rare exceptions like this one. Think of this variable as a very simple global error handler.) Technically, thread-local variables have a thread scope, which should be meaningless if you’re not writing multi-threaded code. If you do, know that the name of the variable says it all; it is local to a thread (thread = your program if you’re not working with multiple threads).

You can use the above expression anywhere in your code, from any method.

To avoid repeating code, I will wrap the Thread.current expression into a separate method (be sure the method is available from anywhere in your code):

def report_error(error_message)
  (Thread.current[:errors] ||= []) << "#{error_message}"
end

Let’s say we have some_random_method which crashes and we want to log the details:

def some_random_method
  begin
    raise 'an error!'
  rescue => error
    report_error("#{error.class} and #{error.message}")
  end
end

some_random_method
p Thread.current[:errors] #=> ["RuntimeError and an error!"]

For now, the errors are being stored in thread-local variables. We want to store them in a file. Let’s write log_errors method that will do this:

def log_errors
  File.open('errors.txt', 'a') do |file|
    (Thread.current[:errors] ||= []).each do |error|
      file.puts error
    end
  end
end

This is not enough, though. We want this method to execute once the program exits, and it doesn’t matter if it exits with or without an error. As we’ve seen earlier, this is pretty easy to do: just put the method call in at_exit:

at_exit { log_errors }

Be Careful With Your Rescue Code

One thing I have learned the hard way is that you can never be too careful with the code you put into rescue. What if the code inside it fails and produces an exception? Sure, you can nest rescues, but that defeats the whole purpose of the statement!

Imagine if your program was responsible for, say, constructing a building. Then a fire occurs. As a response to it, a fireman comes to the rescue:

begin
  # building the building
rescue Fire
  Fireman.dispatch
end

We want to make sure the fireman does his job perfectly. We don’t want for him to catch fire by having faulty equipment! Also, we don’t want for the fireman’s vehicle to break down on the way to the building. We want everything to be flawless, and that includes 0% failure rate. That should be the ultimate goal with your code inside the rescue statement. As I’ve mentioned, you can go deeper and get someone to rescue the rescuer, which could lead to problems.

Never Rescue Exception, Never Rescue Broadly

After reading 2 or 3 articles on the basics of Ruby exception handling, you’re bound to see the advice on never rescuing Exception. Exception is the root of the exception class library, the “mother of all exceptions.” I want to go even further with this advice and recommend you never rescue broadly. That includes ignoring broad classes like StandardError (StandardError itself has over 300 child classes descending from it). So basically, by rescuing StandardError, you’re handling 300 potential failure cases. Now tell me, if you have a rescue block handling 300 possible failure cases, what’s the probability of the rescue block failing itself? Imagine giving a fireman the same equipment to deal with single floor houses and an 100-story building! Not a good idea.

So, what’s the solution? Instead of going broad, try to rescue specific errors (which don’t have 100+ children exceptions). Which leads me to my next point…

Types of Exceptions (By Probability of Happening)

Many people preach things like “exceptions should be exceptional” without realizing the word “exceptional” is frequently misunderstood. I propose a better way of categorizing a particular exception, by the probability of it actually happening:

  • Possible to happen. This is what many people mean when they say an exception should be “exceptional”. An exception that would happen under a situation that is far from what you can expect.
  • Likely to happen. You can predict with fair accuracy that under some situation, this exception will eventually happen.
  • Must (is required to) happen The exception will happen, and you can create the situation for that quite easily.

Let’s tackle the third (last) type first. If your exception is required to happen or has a very high probability of happening, re-think if you even need to raise it in the first place. If you’re raising, are you using this exception for control flow? This is a bad idea. For example, raising an exception if a user entered incorrect input instead of prompting again (using some type of loop). If you’re rescuing an exception that happens a lot and is raised under some library, see if you can wrap the whole logic into some sort of validation method where you’ll get a return value instead of constantly crashing.

If the exception is possible to happen but not likely, do you really need to handle it? Do you have to foresee and distinguish every possible case? This is a main reason why so many people rescue StandardError or Exception. They don’t want their program to fail under any circumstance. In my experience (and that of many other people I’ve talked to) this creates more problems than it solves. My recommendation here is to simply run your program often enough and see where it fails. See the type of exceptions raised and when they happen, say, more than twice, deal with it.

Likely to happen. This is the sweet spot when rescuing happens. You know the type of exception, you know how often/when it occurs by running your program often enough. You have an idea how often it occurs and when, so you can handle it safely.

My advice is to not rescue exceptions for which you have no idea of whether or not they will happen. If they happened once or twice and you’ve been running your program for months, examine the underlying reason for it. Ask “why” until you eventually get to an answer that would allow you to fix your program/system logic and prevent that error from ever happening again.

I wouldn’t give the same advice when you raise exceptions though. Before you do it, take a look at a few alternatives.

Alternatives to Raising Exceptions

Writing a simple raise is easy. The consequences are far from desirable, though. Your program will stop. Are you sure you want to do that? There are a few alternatives you can implement to make your program continue normally.

A Custom Strategy

You can instruct Ruby to use a custom strategy for rescuing an exception (the default would be raise). Suppose you have this code (in case you’re confused about where the begin clause is, each method definition is an implicit begin/end statement, meaning the def itself is the begin):

def some_method(some_argument, error_strategy = method(:raise))
  raise "#{some_argument}"
rescue
  error_strategy.call
end

some_method(1) # '1' (RuntimeError)

This code will raise a RuntimeError with the message as our argument, which is 1. Now, keeping the method definition intact, try the following:

error_handler = Proc.new { puts 'I rescued it!' }
some_method(1, error_handler)

Now, the program will end with no error and “I rescued it!” will print to the console. We just changed our default “strategy” for handling errors in the method by passing a proc to the object. If the method(:raise) part is unfamiliar to you, see this article.

Use a Value That Will Allow the Program to Continue

Let’s say you have a method that’s supposed to return an array. While calling the method, you have an exception that pops up from time to time. You want to continue running though, instead of crashing your program all the time. Why not return an empty array? If the result of your method is then used to iterate over something, an empty array will make the subsequent code not iterate over anything. A sample code would make this easier to understand:

def method_that_should_return_an_array
  this_method_call_will_raise_an_error('some argument')
rescue
  [] # your method will return this
end

Raise nil

I think returning nil instead of raising an exception is an often overused strategy in the Ruby community. Raise your hand if you got that “cannot call X method on NilClass” error and was frustrated to find which part of your code returned an unexpected nil that caused all hell to break loose. To combat this problem, many people would do something like this:

some_method_that_might_return_nil || raise 'nil returned'

This code snippet is a short way to raise an exception if some expression in the same line returns nil.

When you return nil, it’s a good idea to make sure the code later will check and handle it (else you’ll get a bunch of unexpected “cannot call X method on NilClass” errors):

my_value = some_method_that_might_raise_nil

if my_value.nil?
  # do something
else
  # do something else
end

Take the advice in this article as a set of principles, not rules on what to do. Try it and see if it works for your particular circumstances. My purpose here was to expand your way of thinking about exception handling and give you new ideas and tools you can go and implement right now in your program. I hope my mission was accomplished.

Learn more on ruby with our tutorial Setting Up Automated Testing with RSpec on SitePoint.

K-Sato

Table of contents

  • Basic Syntax
  • Relevant methods
  • Ensure
  • Retry
  • Rescue modifier
  • Exception in methods
  • Multiple Exceptions
  • Custom Exception

Basic Syntax

The basic syntax of ruby’s exception handling looks like this.

begin 
  # Code that potentially raises an error  
rescue => e # variable
  # Execute if there was an error between begin and rescue.
end

Enter fullscreen mode

Exit fullscreen mode

Example

begin 
  raise 
rescue => e
  p e #=> RuntimeError
end

Enter fullscreen mode

Exit fullscreen mode

Relevant methods

Method Explanation
Object#class Returns the class of the receiver.
Kernel#raise Raise an error.
Exception#backtrace Returns the backtrace of the error.
Exception#message Returns the error message.

Example

begin 
  raise StandardError.new("Message")
rescue => e
  p e.class #=> StandardError
  p e.message #=> "Message"
  p e.backtrace #=> ["(repl):4:in `<main>'", "/run_dir/repl.rb:41:in `eval'", "/run_dir/repl.rb:41:in `run'", "/run_dir/repl.rb:57:in `handle_eval'", "/run_dir/repl.rb:170:in `start'", "/run_dir/repl.rb:177:in `start'", "/run_dir/repl.rb:181:in `<main>'"]
end

Enter fullscreen mode

Exit fullscreen mode

Ensure

You can run something regardless of the existence of an error with ensure

Example

begin 
  "no Error"
rescue => e
  p e.message
ensure 
  p "Ensured" #=> Ensured
end

Enter fullscreen mode

Exit fullscreen mode

Example2

begin 
  raise StandardError.new('error')
rescue => e
  p e.message #=> error
ensure 
  p "Ensured" #=> Ensured
end

Enter fullscreen mode

Exit fullscreen mode

Retry

Literally re-try the execution.

Example

file = ARGV[0]

begin
  # Open a file.
  io = File.open( file )
rescue
  # If there was an error during opening the file, this part gets executed.

  sleep( 10 )
  # Goes back to begin and re-try the execution.
  retry
end

Enter fullscreen mode

Exit fullscreen mode

Rescue modifier

You can write begin ~ rescue in one sentence just like if ~ end in Ruby.

Example

raise "Error" rescue p "rescued" #=> rescued

Enter fullscreen mode

Exit fullscreen mode

Exception in methods

You don’t need to write begin and end to do exception handling in methods.

Example

def method
 raise 'error'
 rescue => e 
  p e.message #=> error
 ensure
  p 'ensured' #=> ensured
end
method #=> "error"

Enter fullscreen mode

Exit fullscreen mode

Multiple Exceptions

You can write rescue multiple times for each corresponding error.

begin 
  rescue Exception1, Exception2 => e
  # For Exception1 or Exception2
  rescue Exception3 => e
  # For Exception3
  rescue => e
   # For other errors.
end

Enter fullscreen mode

Exit fullscreen mode

Example

begin 
  raise StandardError
  rescue StandardError, RangeError   
    p 'Standard or Ranage Error'
  rescue RuntimeError
    p 'Runtime Error'
  rescue => e
    p 'some other error'
end

#=> "Standard or Ranage Error"

Enter fullscreen mode

Exit fullscreen mode

Custom Exception

Simply Follow the steps below.

(1) Make a new Class

class CustomError < StandardError
end

Enter fullscreen mode

Exit fullscreen mode

(2) Add a message

class CustomError < StandardError
  def initialize(msg="My Error")
    super
  end
end

Enter fullscreen mode

Exit fullscreen mode

(3) Add custom data attributes to your exception

class CustomError < StandardError
  attr_reader :atr
  def initialize(msg="My Error", atr="atrribute")
    @atr = atr
    super(msg)
  end
end

Enter fullscreen mode

Exit fullscreen mode

Example

class CustomError < StandardError
  attr_reader :atr

  def initialize(msg="My Error", atr="atrribute")
    @atr = atr
    super(msg)
  end
end


begin
  raise CustomError.new("CustomMessage", "Custom Attribute")
rescue => e
  p e.message #=> "CustomMessage"
  p e.atr  #=> "Custom Attribute"
end

Enter fullscreen mode

Exit fullscreen mode

References

  • Custom exceptions in Ruby — Honeybadger Developer Blog

Понравилась статья? Поделить с друзьями:
  • Error handling response typeerror cannot read properties of undefined reading direction
  • Error handling nestjs
  • Error handling jsf
  • Error handling in node js
  • Error handling file saving did the server never start