Namespace inferred serializer resolution.

This commit is contained in:
Darren Cheng 2016-07-21 18:46:16 -07:00
parent f76ec6dcff
commit 629afb9887
13 changed files with 198 additions and 22 deletions

View file

@ -1,5 +1,9 @@
inherit_from: .rubocop_todo.yml
AllCops:
Exclude:
- Guardfile
- grape-active_model_serializers.gemspec
Style/BlockDelimiters:
Enabled: false

View file

@ -18,7 +18,3 @@ Style/Documentation:
# Configuration parameters: Exclude.
Style/FileName:
Enabled: false
# Offense count: 4
Style/RegexpLiteral:
MaxSlashes: 0

View file

@ -24,5 +24,5 @@ Gem::Specification.new do |gem|
gem.add_development_dependency 'rack-test'
gem.add_development_dependency 'rake'
gem.add_development_dependency 'guard-rspec'
gem.add_development_dependency 'rubocop', '0.28.0'
gem.add_development_dependency 'rubocop'
end

View file

@ -9,7 +9,7 @@ module Grape
attr_accessor :controller_name
def namespace_options
if self.respond_to?(:inheritable_setting)
if respond_to?(:inheritable_setting)
inheritable_setting.namespace
else
settings[:namespace] ? settings[:namespace].options : {}
@ -17,7 +17,7 @@ module Grape
end
def route_options
if self.respond_to?(:inheritable_setting)
if respond_to?(:inheritable_setting)
inheritable_setting.route
else
options[:route_options]

View file

@ -9,7 +9,7 @@ module Grape
def options
@options ||= (
options = endpoint_options
options.merge!(scope: endpoint) unless options.key?(:scope)
options[:scope] = endpoint unless options.key?(:scope)
options.merge!(default_root_options) unless options.key?(:root)
options.merge!(meta_options)
options

View file

@ -8,7 +8,7 @@ module Grape
def serializer
@serializer ||= (
serializer_klass.new(resource, options) if serializer_klass
serializer_class.new(resource, options) if serializer_class
)
end
@ -16,26 +16,57 @@ module Grape
attr_accessor :resource, :options
def serializer_klass
serializer_klass = options[:serializer]
serializer_klass ||= namespaced_resource_serializer_klass
serializer_klass
def serializer_class
serializer_class = resource_defined_class
serializer_class ||= collection_class
serializer_class ||= options[:serializer]
serializer_class ||= namespace_inferred_class
serializer_class ||= version_inferred_class
serializer_class ||= resource_serializer_class
serializer_class
end
def namespaced_resource_serializer_klass
"#{namespace}::#{resource_serializer_klass}".constantize
rescue NameError
resource_serializer_klass
def resource_defined_class
resource.serializer_class if resource.respond_to?(:serializer_class)
end
def namespace
def collection_class
return nil unless resource.respond_to?(:to_ary)
ActiveModel::Serializer.config.collection_serializer
end
def namespace_inferred_class
return nil unless options[:for]
namespace = options[:for].to_s.deconstantize
"#{namespace}::#{resource_serializer_klass}".safe_constantize
end
def version_inferred_class
"#{version}::#{resource_serializer_klass}".safe_constantize
end
def version
options[:version].try(:classify)
end
def resource_serializer_klass
@resource_serializer_klass ||= ActiveModel::Serializer.serializer_for(
resource
)
@resource_serializer_klass ||= [
resource_namespace,
"#{resource_klass}Serializer"
].compact.join('::')
end
def resource_klass
resource.class.name.demodulize
end
def resource_namespace
klass = resource.class.name.deconstantize
klass.empty? ? nil : klass
end
def resource_serializer_class
ActiveModel::Serializer.serializer_for(resource)
end
end
end

View file

@ -1,5 +1,5 @@
module Grape
module ActiveModelSerializers
VERSION = '1.4.0'
VERSION = '1.4.0'.freeze
end
end

View file

@ -0,0 +1,102 @@
require 'spec_helper'
# asserts serializer resolution order:
# 1. resource_defined_class # V1::UserSerializer
# 2. collection_class # CollectionSerializer
# 3. options[:serializer] # V2::UserSerializer
# 4. namespace_inferred_class # V3::UserSerializer
# 5. version_inferred_class # V4::UserSerializer
# 6. resource_serializer_class # UserSerializer
# 7. missing resource # nil
describe Grape::ActiveModelSerializers::SerializerResolver do
let(:resolver) { described_class.new(resource, options) }
let(:resource) { User.new }
let(:options) {
{
serializer: options_serializer_class, # options defined
for: V3::UsersApi, # namespace inference
version: 'v4' # version inference
}
}
# resource defined
let(:resource_defined?) { true }
let(:defined_serializer_class) { V1::UserSerializer }
# options defined
let(:options_serializer_class) { V2::UserSerializer }
let(:serializer) { resolver.serializer }
before do
if resource_defined?
allow(resource).to receive(:respond_to?).and_call_original
allow(resource).to receive(:respond_to?).with(:to_ary) { true }
allow(resource).to receive(:serializer_class) { defined_serializer_class }
end
end
context 'resource defined' do
it 'returns serializer' do
expect(serializer).to be_kind_of(defined_serializer_class)
end
end
context 'not resource defined' do
let(:resource_defined?) { false }
context 'resource collection' do
let(:resource) { [User.new] }
let(:serializer_class) { ActiveModel::Serializer::CollectionSerializer }
it 'returns serializer' do
expect(serializer).to be_kind_of(serializer_class)
end
end
context 'not resource collection' do
context 'specified by options' do
it 'returns specified serializer' do
expect(serializer).to be_kind_of(V2::UserSerializer)
end
end
context 'not specified by options' do
let(:options) { super().except(:serializer) }
context 'namespace inferred' do
it 'returns inferred serializer' do
expect(serializer).to be_kind_of(V3::UserSerializer)
end
end
context 'not namespace inferred' do
let(:options) { super().except(:for) }
context 'version inferred' do
it 'returns inferred serializer' do
expect(serializer).to be_kind_of(V4::UserSerializer)
end
end
context 'not version inferred' do
let(:options) { super().except(:version) }
context 'ASM resolved' do
it 'returns serializer' do
expect(serializer).to be_kind_of(UserSerializer)
end
end
context 'not ASM resolved' do
let(:resource) { nil }
it 'returns nil' do
expect(serializer).to eq(nil)
end
end
end
end
end
end
end
end

View file

@ -0,0 +1,13 @@
class UsersApi < Grape::API
resource :users do
desc 'all users'
get do
[User.new]
end
desc 'specified user'
get '/:id' do
User.new
end
end
end

View file

@ -0,0 +1,15 @@
module V3
class UsersApi < Grape::API
resource :users do
desc 'all users'
get do
[User.new]
end
desc 'specified user'
get '/:id' do
User.new
end
end
end
end

View file

@ -0,0 +1,5 @@
module V2
class UserSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :email
end
end

View file

@ -0,0 +1,5 @@
module V3
class UserSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :email
end
end

View file

@ -0,0 +1,5 @@
module V4
class UserSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :email
end
end