Warning: Creating default object from empty value in /homepages/39/d161420129/htdocs/p373.net/wp/wp-content/themes/p373b/admin/functions.php on line 183
Patterns: Exception Factory(or is it a Proxy?)

Patterns: Exception Factory(or is it a Proxy?)

Let’s say you are writing a library that wraps around an API, say Yammer.  Your library probably uses a lower level library to manage the http requests, say Faraday.  There are a multitude of problems that may arise while communicating with their api.  For example, the user could have revoked their token, or there could be an internal server error.  Faraday will raise all of these as one or a couple of type of exceptions.  So in your library you will want to wrap these exceptions and probably raise more specific exceptions with better error messages so that client applications of your library can catch them specifically.

One way of handling this is to put the logic that determines what exception to raise right where you catch Faraday’s exception, but this isn’t very modular and extensible.  Here’s an example:

    def request(method, path, options, format)
      begin
        ....do all sorts of request stuff that might raise an exception...
      rescue Faraday::Error::ClientError => e
       #status code is given to us by faraday
       case e.status_code
       when 401
         raise Yammer::401Exception
       when 404
         raise Yammer::404Exception
       ...and so on...
        end
      end
    end

Or…you could put all the logic into an exception proxy(or factory) class which encapsulates all that logic for you, like so:

  class ErrorProxy
    def self.new(exception)
      return self.determine_exception(exception)
    end

    def self.determine_exception(exception)
      exception = exception
      message = exception.message
      status = exception.response[:status]
      headers = exception.response[:headers]

      case status.to_i
      when 400
        Yammer::BadRequest.new(error_message(message), headers)
      when 401
        Yammer::Unauthorized.new(error_message(message), headers)
      when 403
        Yammer::Forbidden.new(error_message(message), headers)
      when 404
        Yammer::NotFound.new(error_message(message), headers)
      when 406
        Yammer::NotAcceptable.new(error_message(message), headers)
      when 500
        Yammer::InternalServerError.new(error_message("Something is technically wrong."), headers)
      when 502
        Yammer::BadGateway.new(error_message("Yammer is down or being upgraded."), headers)
      when 503
        Yammer::ServiceUnavailable.new(error_message("(__-){ Yammer is over capacity."), headers)
      end
    end

    private
    def self.error_message(msg)
      "There was an error with Yammer: "+msg
    end
  end

And then:

    def request(method, path, options, format)
      begin
        ...
      rescue Faraday::Error::ClientError => e
        raise Yammer::ErrorProxy.new(e)
      end
    end

So client libraries can now easily do something like:

begin
  @yammer_client.users
rescue Yammer::Unauthorized => e
 ..handle exception
end

Ahh, much nicer.  Enjoy.

    This entry was posted in Coding, Ruby/Rails, Technology and tagged , , . Bookmark the permalink. Both comments and trackbacks are currently closed.