Vade
Vade

Reputation: 2029

modular Sinatra App, setting error handling & configuration globally

I am using Sinatra to build a small Ruby API, and I would like to get some of the errors and configurations set to work at a global level so that i don't need to set them at the start of each of the classes.

My structure is this:

content_api.rb

require 'sinatra/base'
require 'sinatra/namespace'
require 'sinatra/json'
require 'service_dependencies'
require 'api_helpers'
require 'json'

module ApiApp
  class ContentApi < Sinatra::Base

    helpers Sinatra::JSON
    helpers ApiApp::ApiHelpers
    include ApiApp::ServiceDependencies

    before do
      content_type :json
    end

    get '/' do
      content = content_service.get_all_content
      content.to_json
    end

    get '/audio' do
      package =content_service.get_type 'Audio'
      package.to_json
    end

    get '/video' do
      package =content_service.get_type 'Video'
      package.to_json
    end

    get '/document' do
      package =content_service.get_type 'Document'
      package.to_json
    end

  end
end

config.ru:

$LOAD_PATH.unshift *Dir[File.join(File.dirname(__FILE__), '/src/**')]
$LOAD_PATH.unshift *Dir[File.join(File.dirname(__FILE__), '/src/api/**')]

require 'content_api'
require 'package_api'
require 'utility_api'
require 'sinatra/base'


configure do
  set :show_exceptions => false
end

error { |err| Rack::Response.new([{'error' => err.message}.to_json], 500, {'Content-type' => 'application/json'}).finish }

Rack::Mount::RouteSet.new do |set|
    set.add_route ApiApp::ContentApi, {:path_info => %r{^/catalogue*}}, {}, :catalogue
    set.add_route ApiApp::PackageApi, {:path_info => %r{^/package*}}, {}, :package
    set.add_route ApiApp::UtilityApi, {:path_info => %r{^/health_check*}}, {}, :health_check
  end

When I run this, it will run okay, but when I force a 500 error (shut down MongoDb) I get a standard html type error that states:

<p id="explanation">You're seeing this error because you have
enabled the <code>show_exceptions</code> setting.</p>

If I add the

configure do
      set :show_exceptions => false
    end

    error { |err| Rack::Response.new([{'error' => err.message}.to_json], 500, {'Content-type' => 'application/json'}).finish }

inside the content_api.rb file, then I get a JSON error returned as I would like to receive. however, as I am building this modular, I don't want to be repeating myself at the top of each class.

Is there a simple way to make this work?

Upvotes: 3

Views: 2081

Answers (2)

ian
ian

Reputation: 12251

As an alternative, inheritance:

class JsonErrorController < Sinatra::Base
  configure do
    set :show_exceptions => false
  end

  error { |err| Rack::Response.new([{'error' => err.message}.to_json], 500, {'Content-type' => 'application/json'}).finish }

end

class ContentApi < JsonErrorController
  # rest of code follows…
end

From Sinatra Up and Running p73:

Not only settings, but every aspect of a Sinatra class will be inherited by its subclasses. This includes defined routes, all the error handlers, extensions, middleware, and so on.

Upvotes: 2

matt
matt

Reputation: 79733

The simplest way to do this would be to reopen Sinatra::Base and add the code there:

class Sinatra::Base
  set :show_exceptions => false

  error { |err|
    Rack::Response.new(
      [{'error' => err.message}.to_json],
      500,
      {'Content-type' => 'application/json'}
    ).finish
  }
end

This is probably not advisable though, and will cause problems if your app includes other non-json modules.

A better solution might be to create a Sinatra extension that you could use in your modules.

module JsonExceptions

  def self.registered(app)
    app.set :show_exceptions => false

    app.error { |err|
      Rack::Response.new(
        [{'error' => err.message}.to_json],
        500,
        {'Content-type' => 'application/json'}
      ).finish
    }
  end
end

You would then use it by registering it in your modules:

# require the file where it is defined first
class ContentApi < Sinatra::Base
  register JsonExceptions
  # ... as before
end

Upvotes: 3

Related Questions