What's Ruby Exceptions?
Bài đăng này đã không được cập nhật trong 7 năm
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