Justin Schier
Justin Schier

Reputation: 524

How should I return Sinatra HTTP errors from inside a class where HALT is not available?

I have a large backend API for my native app that's built in Sinatra, that also serves some admin web pages. I'm trying to dry up the codebase and refactor code into classes inside the lib directory.

My API clients expect a status and a message, such as 200 OK, or 404 Profile Not Found. I'd usually do this with something like halt 404, 'Profile Not Found'.

What's the easiest way of halting with an HTTP status code and a message from inside a class?


Old Wet Code

post '/api/process_something'
  halt 403, 'missing profile_id' unless params[:profile_id].present?
  halt 404, 'offer not found' unless params[:offer_id].present?
  do_some_processing
  200
end

New Dry Code

post '/api/process_something'
  offer_manager = OfferManager.new
  offer_manager.process_offer(params: params)
end

offer_manager.rb

class OfferManager
  def process_offer(params:)
    # halt 403, 'missing profile_id' unless params[:profile_id].present?
    # halt 404, 'offer not found' unless params[:offer_id].present?
    # halt doesn't work from in here
    do_some_processing
    200
  end
end 

Upvotes: 1

Views: 1306

Answers (1)

Anthony
Anthony

Reputation: 15967

This question is probably better for CodeReview but one approach you can see in an OO design here is a 'halt' path and a 'happy' path. Your class just needs to implement a few methods to help this be consistent across all your sinatra routes and methods.

Here's one approach, and it would be easy to adopt this kind of interface across other classes using inheritance.

post '/api/process_something' do
  offer_manager = OfferManager.new(params)
  # error guard clause
  halt offer_manager.status, offer_manager.halt_message if offer_manager.halt?

  # validations met, continue to process
  offer_manager.process_offer
  # return back 200
  offer_manager.status
end


class OfferManager
  attr_reader :status, :params, :halt_message

  def initialize(params)
    @params = params
    validate_params
  end

  def process_offer
    do_some_processing
  end

  def halt?
    # right now we just know missing params is one error to halt on but this is where
    # you could implement more business logic if need be
    missing_params?
  end

  private

  def validate_params
    if missing_params?
      @status = 404
      @halt_message = "missing #{missing_keys.join(", ")} key(s)"
    else
      @status = 200
    end
  end

  def do_some_processing
    # go do other processing
  end

  def missing_params?
    missing_keys.size > 0
  end

  def missing_keys
    expected_keys = [:profile_id, :offer_id]
    params.select { |k, _| !expected_keys.has_key?(k) }
  end
end

Upvotes: 1

Related Questions