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. ...
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.