Refactor option and serializer resolution.

This commit is contained in:
Darren Cheng 2016-07-16 09:05:55 -07:00
parent 37786d6a38
commit ac9466192d
6 changed files with 129 additions and 79 deletions

View file

@ -2,4 +2,6 @@ require 'active_model_serializers'
require 'grape'
require 'grape-active_model_serializers/endpoint_extension'
require 'grape-active_model_serializers/formatter'
require 'grape-active_model_serializers/serializer_resolver'
require 'grape-active_model_serializers/options_builder'
require 'grape-active_model_serializers/version'

View file

@ -11,80 +11,20 @@ module Grape
serializer, options
).to_json
else
Grape::Formatter::Json.call resource, env
Grape::Formatter::Json.call(resource, env)
end
end
def build_options(resource, env)
endpoint = env['api.endpoint']
options = build_options_from_endpoint(endpoint)
options[:scope] = endpoint unless options.key?(:scope)
# ensure we have a root to fallback on
if resource.respond_to?(:to_ary) && !options.key?(:root)
options[:root] = default_root(endpoint)
end
options.merge(meta_options(env))
Grape::ActiveModelSerializers::OptionsBuilder.new(
resource, env
).options
end
def fetch_serializer(resource, options)
# use serializer specified by options
serializer = options[:serializer]
if serializer.nil?
# fetch serializer leverage AMS lookup
serializer = ActiveModel::Serializer.serializer_for(resource)
# if grape version exists, attempt to apply version namespacing
serializer = namespace_serializer(serializer, options[:version])
end
return nil unless serializer
serializer.new(resource, options)
end
def namespace_serializer(serializer, namespace)
"#{namespace.try(:classify)}::#{serializer}".constantize
rescue NameError
serializer
end
def meta_options(env)
options = {}
ams_meta = env['ams_meta'] || {}
meta = ams_meta.delete(:meta)
meta_key = ams_meta.delete(:meta_key)
options[:meta_key] = meta_key if meta && meta_key
options[:meta] = meta if meta
options
end
def build_options_from_endpoint(endpoint)
[
endpoint.default_serializer_options || {},
endpoint.namespace_options,
endpoint.route_options,
endpoint.options,
endpoint.options.fetch(:route_options)
].reduce(:merge)
end
# array root is the innermost namespace name ('space') if there is one,
# otherwise the route name (e.g. get 'name')
def default_root(endpoint)
innermost_scope = if endpoint.respond_to?(:namespace_stackable)
endpoint.namespace_stackable(:namespace).last
else
endpoint.settings.peek[:namespace]
end
if innermost_scope
innermost_scope.space
else
endpoint.options[:path][0].to_s.split('/')[-1]
end
Grape::ActiveModelSerializers::SerializerResolver.new(
resource, options
).serializer
end
end
end

View file

@ -0,0 +1,68 @@
module Grape
module ActiveModelSerializers
class OptionsBuilder
def initialize(resource, env)
self.resource = resource
self.env = env
end
def options
@options ||= (
options = endpoint_options
options.merge!(scope: endpoint) unless options.key?(:scope)
options.merge!(default_root_options) unless options.key?(:root)
options.merge!(meta_options)
options
)
end
private
attr_accessor :resource, :env
def endpoint_options
[
endpoint.default_serializer_options || {},
endpoint.namespace_options,
endpoint.route_options,
endpoint.options,
endpoint.options.fetch(:route_options)
].reduce(:merge)
end
def endpoint
@endpoint ||= env['api.endpoint']
end
# array root is the innermost namespace name ('space') if there is one,
# otherwise the route name (e.g. get 'name')
def default_root_options
return {} unless resource.respond_to?(:to_ary)
if innermost_scope
{ root: innermost_scope.space }
else
{ root: endpoint.options[:path].first.to_s.split('/').last }
end
end
def innermost_scope
if endpoint.respond_to?(:namespace_stackable)
endpoint.namespace_stackable(:namespace).last
else
endpoint.settings.peek[:namespace]
end
end
def meta_options
options = {}
ams_meta = env['ams_meta'] || {}
meta = ams_meta[:meta]
meta_key = ams_meta[:meta_key]
options[:meta] = meta if meta
options[:meta_key] = meta_key if meta && meta_key
options
end
end
end
end

View file

@ -0,0 +1,42 @@
module Grape
module ActiveModelSerializers
class SerializerResolver
def initialize(resource, options)
self.resource = resource
self.options = options
end
def serializer
@serializer ||= (
serializer_klass.new(resource, options) if serializer_klass
)
end
private
attr_accessor :resource, :options
def serializer_klass
serializer_klass = options[:serializer]
serializer_klass ||= namespaced_resource_serializer_klass
serializer_klass
end
def namespaced_resource_serializer_klass
"#{namespace}::#{resource_serializer_klass}".constantize
rescue NameError
resource_serializer_klass
end
def namespace
options[:version].try(:classify)
end
def resource_serializer_klass
@resource_serializer_klass ||= ActiveModel::Serializer.serializer_for(
resource
)
end
end
end
end

View file

@ -18,10 +18,11 @@ describe Grape::Formatter::ActiveModelSerializers do
end
end
let(:env) { { 'api.endpoint' => app.endpoints.first } }
let(:options) { described_class.build_options(nil, env) }
it 'should read serializer options like "root"' do
expect(
described_class.build_options_from_endpoint(app.endpoints.first)
).to include(:root)
expect(options).to include(:root)
end
end
@ -65,9 +66,7 @@ describe Grape::Formatter::ActiveModelSerializers do
end
it 'should read serializer options like "root"' do
expect(
described_class.build_options_from_endpoint(endpoint).keys
).to include(:root)
expect(options).to include(:root)
end
end
end

View file

@ -26,10 +26,11 @@ describe Grape::Formatter::ActiveModelSerializers do
end
end
let(:env) { { 'api.endpoint' => app.endpoints.first } }
let(:options) { described_class.build_options(nil, env) }
it 'should read serializer options like "root"' do
expect(
described_class.build_options_from_endpoint(app.endpoints.first)
).to include(:root)
expect(options).to include(:root)
end
end
@ -72,9 +73,7 @@ describe Grape::Formatter::ActiveModelSerializers do
end
it 'should read serializer options like "root"' do
expect(
described_class.build_options_from_endpoint(endpoint).keys
).to include(:root)
expect(options).to include(:root)
end
end
end