0

What's Ruby Exceptions?

This article will discuss about exceptions in ruby ​​(ruby exceptions). At first glance, we find this to be a simple concept. However, there are some points to keep in mind In this article we will show and find easy example to make you get clearly and easay understatnd about ruby exceptions. Exception in ruby

If you do not know about ruby ​​exceptions, try the following example:

puts a # => undefined local variable or method `a' for main:Object (NameError)

In the example above we see a 1 variable is not defined and so there will be bugs returns "undefined local variable or method a" and its type is "NameError" If you want the code on the run, we used begin...rescue to Catch exception:

begin
  puts a
rescue
  puts 'Something bad happened'
end

We can catch different exceptions rescue by the following:

begin
  puts a
rescue NameError => e
  puts e.message
end

# or

def foo
  begin
    # logic
  rescue NoMemoryError, StandardError => e
    # process the error
  end
end

Table inheritance of the exception in Ruby

One important thing to know about ruby exception is that by default it rescue will catch all inherited errors from StandardError. So it will not catch NotImplementedError or NoMemoryError in the first rescue example. To know what exceptions we can catch by rescue we look at the following table:

  • Exception
    • NoMemoryError
    • ScriptError
      • LoadError
      • NotImplementedError
      • SyntaxError
    • SecurityError
    • SignalException
      • Interrupt
      • SyntaxError
    • StandardError - default for rescue
      • ArgumentError
      • UncaughtThrowError
      • EncodingError
      • FiberError
      • IOError
      • EOFError
      • IndexError
        • KeyError
        • StopIteration
      • LocalJumpError
      • NameError
        • NoMethodError
      • RangeError
        • FloatDomainError
      • RegexpError
      • RuntimeError - default for raise
      • SystemCallError
        • Errno :: *
      • ThreadError
      • TypeError
      • ZeroDivisionError
    • SystemExit
    • SystemStackError

Access Exception object

We can also define variables to access the Exception object:

def foo
  begin
    raise 'here'
  rescue => e
    e.backtrace # ["test.rb:3:in `foo'", "test.rb:10:in `<main>'"]
    e.message # 'here'
  end
end

You can find out more about the methods of the Exception object here.

Ensure

Ruby provides us with a very interesting keyword to work with that exception ensure. Regardless of whether or not the exception is happening, Ruby will definitely execute the code inside ensure. And often this keyword is used to close the database connection or to delete temporary files.

begin
  puts a
rescue NameError => e
  puts e.message
ensure
  # clean up the system, close db connection, remove tmp file, etc
end

There is one important thing that we need to know about ensure, that is if we use return in ensure without defining rescue it ensure will block the exception. We will be clear through the following example:

def foo
  begin
    raise 'here'
  ensure
    puts 'processed'
  end
end

foo 
# processed
# => `foo': here (RuntimeError)
def foo
  begin
    raise 'here'
  ensure
    return 'processed'
  end
end

puts foo # => processed

There are no exceptions RuntimeErrorthis time! Ruby has returned "processed" words ensureand does not make exceptions! Raise

Now we will learn how to push exceptions from the code. The module Kernel has methods raise that allow exceptions. There is an alias method that raise is similar to , fail but usually we will see that raise is used more.

If we call raise without params then it will have the following error:

raise # => `<main>': unhandled exception

In this case there will be no error message for dev so usually we will add the error params to the raise:

raise 'Could not read from database' # => Could not read from database (RuntimeError)

Now we will see raise the error message returned and the exception type is RuntimeError raised by the exception as the default raise RuntimeError. We can also precisely define the exception we want to push as follows:

raise NotImplementedError, 'Method not implemented yet'
# => Method not implemented yet (NotImplementedError)

It is interesting raise to call the #exception method for any class when you access it. In this case it calls NotImplementedError # exception. This allows us to add exception support to any class. The main requirement is that the #exception method is required:

class Response
  def exception(message = 'HTTP Error')
    RuntimeError.new(message)
  end
end

response = Response.new
raise response # => HTTP Error (RuntimeError)

Another interesting thing about exception is that when an exception occurs, Ruby stores it in the global variable is $!

$! # => nil
begin
  raise 'Exception'
rescue
  $! # <RuntimeError: Exception>
end
$! # => nil

We can see that the exception is stored in the variable $!only when it happens outside and it still has the value nil.

Retry

Ruby gives us a way to run the code inside begin again one more time. Imagine that we have a service that in some cases does not return the requested data. The solution is that we can wrap the service into a loop, but the other way is to use the keyword retry, which will execute the code inside the cursor one more time.

tries = 0
begin
  tries += 1
  puts "Trying #{tries}..."
  raise 'Did not work'
rescue
  retry if tries < 3
  puts 'I give up'
end
# Trying 1...
# Trying 2...
# Trying 3...
# I give up

The above code is quite simple. If the code inside begin ejects an exception then we will do it again. This idea is very interesting, but I find that for better error handling in 3rd service there is a solution called Circuit Braker.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí