This is what I ended up with, through a combination of reading the Grape documentation, Googling and reading some of the pull requests on github. Basically, after declaring :json
format (to get all the other default goodies that come with it), I over-ride the output formatters with new ones that add jsend's wrapper layer. This turns out much cleaner to code than trying to wrap Grape's #present
helper (which doesn't cover errors well), or a rack middleware solution (which requires de-serialising and re-serialising JSON, plus takes lots of extra code to cover errors).
require "grape"
require "grape-entity"
require "json"
module JSendSuccessFormatter
def self.call object, env
{ :status => 'success', :data => object }.to_json
end
end
module JSendErrorFormatter
def self.call message, backtrace, options, env
# This uses convention that a error! with a Hash param is a jsend "fail", otherwise we present an "error"
if message.is_a?(Hash)
{ :status => 'fail', :data => message }.to_json
else
{ :status => 'error', :message => message }.to_json
end
end
end
class Thing
def initialize llama_name
@llama_name = llama_name
end
attr_reader :llama_name
end
class ThingPresenter < Grape::Entity
expose :llama_name
end
class MainService < Grape::API
prefix 'api'
version 'v2'
format :json
rescue_from :all
formatter :json, JSendSuccessFormatter
error_formatter :json, JSendErrorFormatter
resource :thing do
get do
thing = Thing.new 'Henry'
present thing, :with => ThingPresenter
end
end
resource :borked do
get do
error! "You broke it! Yes, you!", 403
end
end
end