arve
arve

Reputation: 801

ruby when/how to use rescue/ensure?

I have a class to call geo location api. sample url below with Json response something like this

{ "location": { "lat": 31.0, "lng": 14.0 }, "accuracy": 112.4 }

I want to catch any errors, such as 503, 404 or any other errors that might be caused by error in request object. what is the proper way to this with rescue in ruby. also. if there is any error, can i return a message from the ensure block?

def findlocation
  begin
    location = httparty.get("https://www.googleapis.com/geolocation/v1/geolocate?key=#{API_KEY}")
  rescue 
    # do something after catching error
  ensure
    return { "message" : "unable to find location" }
  end 
end

Upvotes: 0

Views: 2392

Answers (2)

max
max

Reputation: 102218

In general exceptions should be used for exceptional events. Not regular application flow.

When catching exceptions always be specific and only rescue exceptions that you know what to do with.

begin 
  pats "hello world"
rescue 
  nil
end

This example shows a serious flaw in the code in yout question - you created a black hole that swallows the NoMethodError that would have told us that there is a typo in the code. This makes debugging extremely difficult. This anti-pattern is known as Pokémon Exception Handling (Gotta catch em' all).

ensure just ensures that the code is run no matter if the code raised an exception or not. Its used to for example guarantee that the method closes a file handler that it has opened or rolls back a transaction. Its a really big hammer that should be used very sparingly.

HTTParty does not actually raise exceptions when the response code is a "error" code - because its not an exceptional event. Its a part of normal application flow when dealing with HTTP requests. Shit happens. HTTParty raises exceptions if you can't reach the server at all or you can't even reach the network. Those are exceptional events.

class GeolocationClient
  include HTTParty 
  base_uri "https://www.googleapis.com/geolocation/v1"
  format :json
  attr_accessor :options

  def initialize(api_key:)
    @options = {
      api_key: api_key
    }
  end

  def geolocate
    begin 
      response = self.class.get("/geolocate", options)
      if response.successful?
        response 
      else
        logger.info("Geolocation API call was unsuccessful. Status code: #{response.code}")
        handle_unsuccessful_request
      end
    rescue HTTParty::Error => e 
      logger.warn(e.message)
      handle_unsuccessful_request
    end
  end

  private

  def handle_unsuccessful_request
    { "message" : "unable to find location" }
  end
end

response.successful? tests if the response is in the 2xx "happy range". Use a switch statement if you want handle each code or range of codes separately.

Upvotes: 4

theterminalguy
theterminalguy

Reputation: 1941

there are severals way to handle this. First I want to assume you are using the HTTParty library. If that's the case then you should be calling it as HTTParty and not httparty.

Lets say you want to resuce each individual error you can do

response = HTTParty.get("https://www.googleapis.com/geolocation/v1/geolocate?key=#{API_KEY}")

case response.code
  when 503
    # do something, Internal Server error
  when 404
    # do something, Page Not found
  when 500...600
    # do something, unexpected error
end

However the above cases assumes you were someohow able to talk to the requested url. If in any case you were unable to talk to the server and you want to rescue from a general HTTParty error you can do

def findlocation
  begin
    response = HTTParty.get("https://www.googleapis.com/geolocation/v1/geolocate?key=#{API_KEY}")
  rescue HTTParty::Error => e 
    # Something is wrong 
    puts "#{e.message}"
end

ensure allows you to run a piece of code whether or not an exception occurs. So if you want a piece of code to be executed regardless of whether you were able to talk to the Google API, put that in the ensure block

Upvotes: 2

Related Questions