codedecoder

breaking into the unknown…

raising custom exception in ruby rails

4 Comments

I have explained handling ruby exception in this post. Read this post for exception handling basics. Here, we will look into creating and raising custom exception in ruby. So, when we need custom exception ??. Well, I give an example from my recent experience for user registration or creation for my application. It will help you to understand when and how to create custom exception, how to raise it and how to rescue it in your code logic. The requirement, is that, the user need to be registered on my site, only if his account also get created on Openam and a certificate created for him on Citadel.

So basically, it should be a transaction of four independent calls.

=> user registration on my site called myfinance.com

=> user account creation on Openam

=> certificate creation on citadel

=> send email to user with login detail

If any of them fail, I need to rollback all other.

This was my approach:

=> I will create custom exception for Openam , Citadel, Mailer etc

=> I will create openam_api.rb, citadel_api.rb, sendmail.rb  file, containing the core logic of different feature

=> I will wrap the openam and citadel call in begin rescue end block

=> within rescue block I will raise custom exception I have defined  for Openam , Citadel etc

=> I will catch the exception object in create action of my controller and do the rollback stuff

STEP 1: creating custom exception

Here, You need to understand that, Your custom class, is just like any other class, say for example your user model.

It look like this

class User < ActiveRecord::Base

# you define attribute_accessor

# you define methods

end

You can do all these stuffs in your exception class also and access its attributes and methods on its instance.Here, the instance variable of exception is e in the below code structure

begin

# some code to be rescued

rescue Exception => e

# e is the instance of exception being cached so can call all attributes and methods on e

end

The difference is that, here your class inherit from StandardError rather then ActiveRecord::Base.

Lets, create a file my_custom_exception.rb in lib folder, so that it get compiled when ever the server is started. The file content look as below

class OpenamRegistrationFailedError < StandardError
  attr_accessor :failed_action
  def initialize
    @failed_action = OpenAMRegistration
  end
end

class CitadelCertificateCreationFailedError < StandardError
  attr_accessor :failed_action
  def initialize
    @failed_action = CitadelCertificateGeneration
  end
end

class MailerFailedError < StandardError
  attr_accessor :failed_action
  def initialize
    @failed_action = SendEmail
  end
end

So, here I have defined a no of class in the same file, as I do not have much code to write in each class, but you can put each of them in a separate file as the normal convention is. Anyway, Here what Iam trying to do is to set the name of the class  which implement the rollback in a particular case, say If I got CitadelCertificateCreationFailedError exception then failed_action attribute will contain, CitadelCertificateGeneration class set to it and since, I know which strategy failed, I will call rollback on all earlier strategies succeeded before this.

STEP 2: The code raising above custom exception

openam_api.rb file will look like this

class OpenAMRegistration

  def perform(user)
     begin
      # code which implement registration to openam
     rescue Exception => e
      # if some exception occur in begin block, controll will 
      #come here and we will raise our openam custom exception as below
      raise OpenamRegistrationFailedError.new, "OpenaAM User registration failed"
     end
  end

  def rollback(user)
     # code which will delete the user created on openam
  end

end

Similarly, citadel_api.rb file will look like this

class CitadelCertificateGeneration

  def perform(user)
     begin
      # code which implement certificate creation on citadel
     rescue Exception => e
      # if some exception occur in begin block, controll will 
      #come here and we will raise our ciatdel custom exception as below
      raise CitadelCertificateCreationFailedError.new, "Citadel certificate creation failed"
     end
  end

  def rollback(user)
     # code which will delete the certificate created on Citadel
  end

end

sendmail.rb file have below content

class SendEmail

  def perform(user)
     begin
      # code which implement sending mail to user
     rescue Exception => e
      # if some exception occur in begin block, controll will 
      #come here and we will raise our email failure custom exception as below
      raise MailerFailedError.new, "Citadel certificate creation failed"
     end
  end

end

See that, in all the above code, we are raising an instance of Exception class, so that we can access all attributes and methods of the exception class being raised from e . see in the next step , the use of  e.failed_action . Similarly, you can call any method or attributes you have defined in your custom exception class.

STEP 3: Defining our create action in users_controller

require 'openam_api'
require 'citadel_api'
require 'sendmail'

class UsersController < ApplicationController

# defining a array of strategies I need to follow after my user creation
STRATEGIES = [OpenAMRegistration, CitadelCertificateGeneration, SendEmail]

def create
  @user = User.new(params[:user])
  if @user.save
    STRATEGIES.each do |strategy|
      begin
        strategy.perform(@user) # it will call perform methods of each strategy
      rescue Exception => e
        STRATEGIES.each do |action|
          if e.failed_action.to_s == action.to_s # e.failed_action give the 
                            #name of class which raised the exception and failed
            break # we will break the roll back loop as since exception occur in 
             #this class, other strategy after it are not called so no need of  
             # rollback for them
          end
          action.new.rollback(@user)
        end
      end
    end
    flash[:notice] = "User created Successfully"
  else
    flash[:notice] = "User creation failed"
  end
  redirect_to root_path
end

end

So, in the above code be are looping over each STRATEGIES and calling perform method on them which perform different function according to class in which it is defined. If any of them failed due to exception, we get into rescue block and again loop over each STRATEGIES and call rollback method on it, breaking the loop at the strategy which raised the Exception, thus if Sendmail failed i,e mail failed to deliver, openam and citadel will be rolled  back, if citadel failed only openam will be rolled back .

Hope, You have now basic idea of creating, using, and raising custom exception in Ruby 🙂

Advertisements

Author: arunyadav4u

over 7 years experience in web development with Ruby on Rails.Involved in all stage of development lifecycle : requirement gathering, planing, coding, deployment & Knowledge transfer. I can adept to any situation, mixup very easily with people & can be a great friend.

4 thoughts on “raising custom exception in ruby rails

  1. Why not rescue your own exceptions?

    rescue OpenamRegistrationFailedError
    # perform rescue stuff

  2. Pingback: Exception Handaling in Ruby Rails | Milap.Tech

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s