Last active
December 31, 2015 18:49
-
-
Save jsmestad/8029732 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Tradesman | |
module Controller | |
class ApiResponder < ActionController::Responder | |
SerializerWrapper = Struct.new(:serializer, :object) do | |
def serializable_hash(options={}) | |
instance = serializer.new(object, options) | |
if instance.respond_to?(:serializable_hash) | |
instance.serializable_hash | |
else | |
instance.as_json options | |
end | |
end | |
end | |
# http://api.rubyonrails.org/classes/ActionController/Responder.html | |
# | |
# def respond(*) | |
# responds(resource, options) | |
# # if paginated? | |
# # controller.headers['X-Records'] = resource.total_entries.to_s | |
# # controller.headers['X-Pages'] = resource.total_pages.to_s | |
# # controller.headers['X-Per-Page'] = resource.per_page.to_s | |
# # end | |
# # super | |
# end | |
def to_json#(object, options={}) | |
# binding.pry | |
# | |
# if resource.respond_to?(:to_ary) | |
# normalise_object(resource, options) | |
# else | |
# end | |
render json: render_json(normalise_object(resource, options), options)#, options | |
end | |
# Given a json object or encoded json, will encode it | |
# and set it to be the output of the given page. | |
def render_json(json, options={}) | |
# Setup data from options | |
# self.status = options[:status] if options[:status] | |
# self.content_type = options[:content_type] if options[:content_type] | |
options = options.slice(*RENDERING_OPTIONS) | |
# Don't convert raw strings to JSON. | |
json = encode_to_json(json) unless json.respond_to?(:to_str) | |
# Encode the object to json. | |
# self.status ||= :ok | |
# self.content_type ||= "application/vnd.artemis.v1+json" #Mime::JSON | |
# self.response_body = json | |
# headers['Content-Length'] = Rack::Utils.bytesize(json).to_s | |
end | |
# def paginated? | |
# resource.respond_to?(:total_entries) && | |
# resource.respond_to?(:total_pages) && | |
# resource.respond_to?(:per_page) | |
# end | |
# | |
def self.version | |
controller.api_version | |
end | |
def self.collection?(object) | |
object.is_a?(Array) || object.respond_to?(:to_ary) | |
end | |
def self.normalise_object(object, options = {}) | |
# First, prepare the object for serialization. | |
object = normalise_to_serializer object, options | |
# Convert the object using a standard grape-like lookup chain. | |
if object.is_a?(Array) || object.is_a?(Set) | |
object.map { |o| normalise_object(o, options) } | |
elsif object.respond_to?(:serializable_hash) | |
object.serializable_hash options | |
elsif object.respond_to?(:as_json) | |
object.as_json options | |
else | |
object | |
end | |
end | |
def self.normalise_to_serializer(object, options) | |
serializer = options.delete(:serializer) || serializer_name(object, options) | |
return object unless serializer | |
SerializerWrapper.new(serializer, object) | |
end | |
# def to_json | |
# # if collection?(resource) | |
# # resource.map { |r| } | |
# # else | |
# # end | |
# # presenter = options.delete(:serializer) || serializer(controller.api_version) | |
# if resource.respond_to?(:to_ary) | |
# options[:root] ||= controller.controller_name | |
# end | |
# puts resource | |
# puts options | |
# # binding.pry | |
# render json: serialized(options).to_json, status: options[:status] | |
# end | |
private | |
RENDERING_OPTIONS = [:status, :content_type] | |
def normalise_object(object, options = {}) | |
self.class.normalise_object object, options.except(*RENDERING_OPTIONS).reverse_merge(default_serializer_options) | |
end | |
def default_serializer_options | |
{ | |
url_options: controller.url_options | |
# root: false | |
} | |
end | |
def encode_to_json(object) | |
ActiveSupport::JSON.encode object | |
end | |
# Look for something versioned first, then try for default UserSerializer | |
# | |
def self.serializer_name(object, options) | |
name = object.class.name | |
begin | |
Object::const_get("#{name}#{version.to_s.capitalize}Serializer") | |
rescue NameError | |
Object::const_get("#{name}Serializer") | |
end | |
rescue NameError | |
nil | |
end | |
def serialized(options) | |
presenter = options.delete(:serializer) | |
opts = { | |
url_options: controller.url_options, | |
scope: options.delete(:scope) | |
} | |
opts[:root] = options.delete(:root) if options.has_key?(:root) | |
if s = presenter || serializer(version) | |
s.new(resource, opts) | |
else | |
resource | |
end | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Tradesman | |
class Railtie < Rails::Railtie | |
require 'active_support/i18n' | |
config.i18n.railties_load_path << File.expand_path('../locale/en.yml', __FILE__) | |
# initializer "tradesman.url_helpers" do |app| | |
# ActiveSupport.on_load(:tradesman) do | |
ActionDispatch::Routing::Mapper.send :include, Tradesman::Routing | |
# end | |
# end | |
# initializer "tradesman.setup_testing" do |app| | |
# ActiveSupport.on_load(:tradesman) do | |
# include ActionController::Testing if Rails.env.test? | |
# end | |
# end | |
# initializer "tradesman.error_handling" do |app| | |
# require 'rails-api/action_controller/api' | |
# ActiveSupport.on_load(:tradesman) do | |
ActiveSupport.on_load(:action_controller) do | |
ApplicationController.send :responder=, Tradesman::Controller::ApiResponder | |
include Tradesman::Controller::ErrorHandling | |
include Tradesman::Controller::Versioning | |
include Tradesman::Controller::StrongParameters | |
include ActionController::ImplicitRender | |
end | |
# end | |
initializer "tradesman.activerecord_hooks" do | |
if defined?(ActiveRecord) | |
require 'tradesman/active_record' | |
end | |
end | |
# rake_tasks do | |
# load "tradesman/tasks/tradesman.rake" | |
# end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Tradesman | |
module Routing | |
# Scopes a set of given api routes, allowing for option versions. | |
# @param [Hash] options options to pass through to the route e.g. `:module`. | |
# @option options [Array<Integer>, Integer] :versions the versions to support | |
# @option options [Array<Integer>, Integer] :version the single version to support | |
# @raise [ArgumentError] raised when the version isn't provided. | |
def tradesman(options = {}, &blk) | |
defaults = {format: 'json'} | |
versions = (Array(options.delete(:versions)) + Array(options.delete(:version))).flatten.map(&:to_s) | |
versions.each do |version| | |
raise ArgumentError, "Got invalid version: '#{version}'" unless version =~ /\A\d+\Z/ | |
end | |
versions_regexp = /(#{versions.uniq.join("|")})/ | |
raise ArgumentError, 'please provide atleast one version' if versions.empty? | |
options = options.deep_merge({ | |
constraints: RoutingConstraints.new(versions: versions_regexp, default: !!options.delete(:default)), | |
defaults: defaults | |
}) | |
scope options, &blk | |
end | |
alias api tradesman | |
end | |
class RoutingConstraints | |
def initialize(options) | |
@version = options[:version] | |
@versions = options[:versions] | |
@default = options[:default] | |
end | |
def matches?(req) | |
if @versions.present? | |
@default || !!req.headers['Accept'][/#{Tradesman.versioning_format}/, 1].match(@versions) | |
else | |
@default || !!req.headers['Accept'][/#{Tradesman.versioning_format}/, 1].match(@version) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Tradesman | |
module Controller | |
module StrongParameters | |
extend ActiveSupport::Concern | |
included do | |
if defined?(ActionController::StrongParameters) | |
include ActionController::StrongParameters | |
map_error! ActionController::ParameterMissing, Tradesman::BadRequest | |
map_error! ActionController::UnpermittedParameters, Tradesman::BadRequest | |
end | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Tradesman | |
module Controller | |
module Versioning | |
extend ActiveSupport::Concern | |
included do | |
class_attribute :_version_range | |
end | |
module ClassMethods | |
def version(version) | |
version = version..version if version.is_a?(Integer) | |
self._version_range = version | |
before_filter :verify_api_version | |
end | |
end | |
protected | |
def accept_header_version | |
request.headers['Accept'][/#{Tradesman.versioning_format}/, 1].to_i | |
end | |
def version | |
if !instance_variable_defined?(:@version) | |
@version = begin | |
version = accept_header_version | |
version.presence && Integer(version) | |
rescue ArgumentError | |
nil | |
end | |
end | |
@version | |
end | |
def verify_api_version | |
error! :invalid_version unless version.present? && _version_range.include?(version) | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment