If there is any problem in your code at run time , say your code at some point of time try to divide a number with 0, ruby will throw exception and so breaking your code and bringing down your application. As a developer, you should visualize the possibility of such exception and handle them gracefully within begin/end block.You can read more about ruby exception class here at ruby-doc.org.The different exception thrown by ruby is as below in hierarchical order.
NOTE : creating and raising custom exception is explained in this post
RUBY EXCEPTION HIERARCHY
NoMemoryError ScriptError LoadError NotImplementedError SyntaxError SignalException Interrupt StandardError ArgumentError IOError EOFError IndexError LocalJumpError NameError NoMethodError RangeError FloatDomainError RegexpError RuntimeError SecurityError SystemCallError SystemStackError ThreadError TypeError ZeroDivisionError SystemExit fatal
wherever you see possibility of occurrence of any of these exception in your code, you should wrap that part of code in begin end block and try to rescue it gracefully. Descendants of class Exception are used to communicate between raise methods and rescue statements in begin/end blocks. Exception objects carry information about the exception—its type (the exception’s class name), an optional descriptive string, and optional traceback information. We can handle an exception in following ways.
NOTE : ruby use raise method internally to trigger any of the above exception. You may use raise anywhere in your code to trigger your own exception explicitly.
Exception handling examples :
I’ am just specifying the syntax of begin rescue ensure and end blocks.
Here, you are aware, what type of above error your code may throw. this format is useful only if you know beforehand what exception your code may throw
begin ***** your code which you think may raise Say ArgumentError **** rescue ArgumentError ****** write your handling code for ArgumentError****** end
Here, we will handle one exception in one way, some other in other way and so on i,e you can use rescue any no of time you want in the begin/end block
begin ***** your code which you think may raise Say ArgumentError and ZeroDivisionError which you want to handle differently while in some common ways for all other**** rescue ArgumentError ****** write your handling code for ArgumentError****** rescue ZeroDivisionError ****** write your handling code for ZeroDivisionError****** else ****** write your handling code for any other type of exception****** end
Here we will handle any of the above exception thrown by our code. This approach is not supported by many as it will rescue your code from all the exception and you will never know what going wrong and where is the fault point. The general view is to let exception to explode loud and then fix them, in the above two examples it is more practical as you have visualized the possible exception your code may throw and so you have provided the safety net. more discussion on this is available here
begin ***** your code which you think may raise any exception**** rescue Exception ****** write your handling code ****** end
Exception object also return information about the line in begin block where exception occur with its backtrace method and what is the nature or type of exception in message method. You can use them to debug your code.
NOTE : Use backtrace and message only for debugging. If you commit the code with these two method whenever an exception occur production log may have all the information about database details, application stack etc depending on type of exception thrown. better to remove these line once you have done debugging of your code
begin ***** your code which you think may raise any exception**** rescue Exception => e puts e.backtrace # will print the line where the error occur puts e.message # will print type of the exception and other detail related to it ****** write your handling code ****** end
Here, we will use ensure to do some clean up work after we have rescued the exception. say you code allow a user to open and edit a file, you wrap your code in begin rescue end block to handle any exception, but then here, still you want that despite of any exception, that file may be closed, so ensure keyword will be used
begin my_file = File.open("xyz.rb") ***** your code which you think may raise any exception**** rescue Exception ****** write your handling code ****** ensure my_file.close #after rescuing the exception, the control will jump to the ensure block to do our cleanup stuff or any other thing we want to happen despite a exception end
RAISING EXCEPTION IN RUBY
You can raise any exception explicitly by using raise method. The raise method is from the Kernel module. By default, raise creates an exception of the RuntimeError class. To raise an exception of a specific class, you can pass in the class name as an argument to raise.
def my_method ***I am doing something**** raise "I am raising this exception" #It will throw exception of default type RuntimeError, you can pass specific type as argument ***** doing something else******* end
def my_method ***I am doing something**** raise ZeroDivisionError, "I am raising this exception" # It will throw exception of type ZeroDivisionError ***** doing something else******* end
You can use raise to handle some specific case for any method which is called by many other methods. let say in user model there is a my_files method and you do not want blacklisted users to access the files , so you can raise a exception in this method which may be handle by different methods calling them.
In model class User < ActiveRecordBase def my_files **** some code****** raise ZerodivisionError, " you are not authorized" if self.blacklisted == true #this exception will be handled by you in different methods calling this method by putting it in begin end block **** other code ******* end end In controller class UserController < ApplicationController def user_activities ******some code ****** begin ******some more code ****** current_user.my_files# it will throw exception if current_user is blacklisted rescue ZeroDivisionError # you are rescuing the error redirect_to home_path # you are redirecting user to home page..you may handle it any way you want end ******some more code ****** end end
Custom Exception in Ruby
If the types of exception mentioned above in the hierarchical order do not suit your needs you can define your own exception and use it exactly same as the existing exceptions.let us create a file my_custom_exception.rb in lib folder and add below line to it
class BlacklistedUserError < StandardError end
So, now we can raise BlacklistedUserError in above case instead of ZeroDivisonError which make our
code more readable.
For more detail on creating, raising and handling custom exception see this post.