Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sinatra Error Handling in Ruby

I have a simple Sinatra rest and I am having trouble trapping an error. I also admit I am fairly new to Ruby and Sinatra.

When I raise and error in the post endpoint I want to report the incoming document. I need to either 1) handle the error within the post result (where I have access to @incoming) or 2) pass the incoming document to the error and report it there.

What is a better option, option 1 or option 2?

  • If I stay with option 1, how do I prevent error from picking up the error (as it seems to be doing now)
  • If I go to option 2, how do I pass incoming to error?

Below is a sample of my code:

post ('/result') do 

  begin  
  @incoming = JSON.parse(request.body.read)

  //do something that causes an error
  rescue
     e = env['sinatra.error']
     url = request.url
     ip = request.ip

     @actlogpassblock = {  :message => e.message,
                        :path => url,
                        :ip => ip,
                        :timestamp => Time.new,
                        :type => "500",
                        :sub => "RES",
                        :payload => @incoming
                      }        
    action_log.insert(@actlogpassblock)
    status 500 
  end
end

error do
   status 500 
   e = env['sinatra.error']
   url = request.url
   ip = request.ip
   backtrace = "Application error\n#{e}\n#{e.backtrace.join("\n")}"

  @actlogpassblock = {  :message => e.message,
                        :path => url,
                        :ip => ip,
                        :timestamp => Time.new,
                        :type => "500",
                        :backtrace => backtrace
                      }        
  action_log.insert(@actlogpassblock)
  {:result => 'Ah Shucks! Something went wrong'}.to_json
end
like image 437
tjrburgess Avatar asked Aug 14 '14 02:08

tjrburgess


1 Answers

If I stay with option 1, how do I prevent error from picking up the error (as it seems to be doing now)

According to the docs:

The error handler is invoked any time an exception is raised from a route block...

However, that only applies to uncaught exceptions. Try this:

require 'sinatra'
set :show_exceptions, false

get '/' do
  begin
    raise ZeroDivisionError
  rescue ZeroDivisionError
    "rescue clause"
  end
end

error do
  "sinatra error handler" 
end

Then try this:

get '/' do
  raise ZeroDivisionError
end

error do
  "sinatra error handler" 
end

Also, you can tailor the error handler to only catch certain exceptions thereby avoiding some exceptions, e.g.

error IndexError do ...

or

error MyCustomException do ...

or

error 400..510 do

But for the catch all version:

error do

you can't stop that from executing when an uncaught exception occurs in a route block...unless:

The error handlers will only be invoked, however, if both the Sinatra :raise_errors and :show_exceptions configuration options have been set to false...

:raise_errors defaults to true in the "test" environment and to false on other environments.

:show_exceptions value defaults to true in the "development" environment and to false on other environments

The author of Sintra has said: "This [behavior] is intentional. The idea is that error blocks will hide the issue and you usually don't want to do this in development mode.

https://github.com/sul-dlss/sdr-services-app/blob/master/Sinatra-error-handling.md

If I go to option 2, how do I pass incoming to error?

An @variable that is created inside a route block can be seen inside an error block. Try this:

require 'sinatra'
set :show_exceptions, false

get '/' do
  @incoming = "hello world"    #create @variable
  raise ZeroDivisionError
end

error ZeroDivisionError do
  @incoming                    #retrieve @variable
end

After entering the url http://localhost:4567 in your browser, you should see "hello world" on the returned web page.

The reason that works is because an @variable attaches itself to whatever object is self at the time the @variable is created; likewise when an @variable is summoned, it is retrieved from whatever object is self at that time. When Sinatra executes either the route block or the error block it sets self to the same object.

Option 2 seems nice because it separates the error handling code from the application code.

like image 51
7stud Avatar answered Oct 24 '22 00:10

7stud