Skip to content

Instantly share code, notes, and snippets.

@pdobb
Last active August 29, 2015 14:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pdobb/d4f0b2375bdc210d24e6 to your computer and use it in GitHub Desktop.
Save pdobb/d4f0b2375bdc210d24e6 to your computer and use it in GitHub Desktop.
Custom API Responder
# app/models/api_v2_responder.rb
# This approach was heavily inspired by
# http://josephhsu.com/post/32746478033/rails-responders-api-versioning.
# Overriding the #to_json method wasn't right though, as it removed half of the
# awesome functionality of respond_with.
#
# @see https://github.com/rails/rails/blob/v3.2.17/actionpack/lib/action_controller/metal/responder.rb
class ApiV2Responder < ActionController::Responder
# Override the original `#display` method to wrap the passed in resource in
# the correspondingly named Presenter.
def display(resource, given_options = {})
controller.render given_options.merge!(options).merge!(format => present(resource))
end
private
# Present the resource, or resources, if possible.
def present(model_or_models)
presenters = Array.wrap(model_or_models).map do |model|
if (presenter = determine_presenter(model))
presenter.new(model)
else
model
end
end
# Ensure that an array of presenters is returned for :index pages, even if
# only one presented resource exists.
return presenters if options[:template] == "index" || presenters.many?
presenters.first
end
# Try and find a correspondingly named presenter class for the passed in
# resource.
def determine_presenter(model)
"Api::V2::#{model.class.name}Presenter".constantize
rescue NameError
nil
end
end
# app/models/api_presenter_delegator.rb
# Facilitates the Presenter objects in use by the API.
#
# @see http://devblog.avdi.org/2012/01/31/decoration-is-best-except-when-it-isnt/
# When to use Presenters
class ApiPresenterDelegator < SimpleDelegator
# Redefine `#to_json` so that calling `#to_json` on a Presenter object will
# call the presenter's version of `#as_json` instead of simply delegating to
# the presented model's version of `as_json`.
def to_json(*args)
as_json.to_json(*args)
end
def presented?
true
end
end
# app/presenters/api/v2/foo_report_presenter.rb
module Api
module V2
class FooReportPresenter < ApiPresenterDelegator
def as_json(options = {})
{
root_obj_name: {
id: id,
user: {
id: user_id
},
}
}.deep_merge(options)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment