Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for collection serializers. #61

Merged
merged 1 commit into from
Jul 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

### 1.5.0 (Next)

* [#61](https://github.com/ruby-grape/grape-active_model_serializers/pull/61): Adds support for collection serializers - [@drn](https://github.com/drn).
* [#60](https://github.com/ruby-grape/grape-active_model_serializers/pull/60): Namespace serializer inference - [@drn](https://github.com/drn).
* [#59](https://github.com/ruby-grape/grape-active_model_serializers/pull/59): Refactor option and serializer resolution - [@drn](https://github.com/drn).
* [#57](https://github.com/ruby-grape/grape-active_model_serializers/pull/57): Solve line length linter issues - [@drn](https://github.com/drn).
* [#54](https://github.com/ruby-grape/grape-active_model_serializers/pull/54): Adding support for ASM v0.10. Drops support for ASM v0.9 - [@drn](https://github.com/drn).

### 1.4.0 (July 14, 2016)
Expand All @@ -11,8 +15,8 @@

### v1.3.2 (February 27, 2015)

* [#39](https://github.com/ruby-grape/grape-active_model_serializers/pull/39): Look for namespace and other options to configure serializers - [@jwkoelewijn](https://github.com/jwkoelewijn).
* [#40](https://github.com/ruby-grape/grape-active_model_serializers/pull/40): Use env to pass AMS meta around - [@dblock](https://github.com/dblock).
* [#39](https://github.com/ruby-grape/grape-active_model_serializers/pull/39): Look for namespace and other options to configure serializers - [@jwkoelewijn](https://github.com/jwkoelewijn).

### v1.3.1 (November 20, 2014)

Expand Down
48 changes: 37 additions & 11 deletions lib/grape-active_model_serializers/serializer_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,40 @@ def initialize(resource, options)
end

def serializer
@serializer ||= (
serializer_class.new(resource, options) if serializer_class
)
serializer_class.new(resource, serializer_options) if serializer_class
end

private

attr_accessor :resource, :options

def serializer_class
serializer_class = resource_defined_class
serializer_class ||= collection_class
serializer_class ||= options[:serializer]
return @serializer_class if defined?(@serializer_class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting. Is it different from just checking @serializer_class? (aka nil)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serializer_class can end up being nil, so if it evaluates to nil and called again, I didn't want the lookup chain to run again

@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
end

def serializer_options
if collection_serializer? && !options.key?(:serializer)
options.merge(each_serializer_option)
else
options
end
end

def collection_serializer?
serializer_class == ActiveModel::Serializer.config.collection_serializer
end

def each_serializer_option
serializer_class = options[:each_serializer]
serializer_class ||= namespace_inferred_class
serializer_class ||= version_inferred_class
serializer_class ||= resource_serializer_class
serializer_class
serializer_class ? { serializer: serializer_class } : {}
end

def resource_defined_class
Expand All @@ -36,12 +53,13 @@ def collection_class
end

def namespace_inferred_class
return nil unless options[:for]
return nil unless options.key?(:for)
namespace = options[:for].to_s.deconstantize
"#{namespace}::#{resource_serializer_klass}".safe_constantize
end

def version_inferred_class
return nil unless options.key?(:version)
"#{version}::#{resource_serializer_klass}".safe_constantize
end

Expand All @@ -57,14 +75,22 @@ def resource_serializer_klass
end

def resource_klass
resource.class.name.demodulize
resource_class.name.demodulize
end

def resource_namespace
klass = resource.class.name.deconstantize
klass = resource_class.name.deconstantize
klass.empty? ? nil : klass
end

def resource_class
if resource.respond_to?(:to_ary)
resource.try(:klass) || resource.compact.first.class
else
resource.class
end
end

def resource_serializer_class
ActiveModel::Serializer.serializer_for(resource)
end
Expand Down
64 changes: 54 additions & 10 deletions spec/grape/active_model_serializers/serializer_resolver_spec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
require 'pry'
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
# 2. collection_class # CollectionSerializer, V2::UserSerializer
# 3. options[:serializer] # V3::UserSerializer
# 4. namespace_inferred_class # V4::UserSerializer
# 5. version_inferred_class # V5::UserSerializer
# 6. resource_serializer_class # UserSerializer
# 7. missing resource # nil

Expand All @@ -15,15 +16,15 @@
let(:options) {
{
serializer: options_serializer_class, # options defined
for: V3::UsersApi, # namespace inference
version: 'v4' # version inference
for: V4::UsersApi, # namespace inference
version: 'v5' # version inference
}
}
# resource defined
let(:resource_defined?) { true }
let(:defined_serializer_class) { V1::UserSerializer }
# options defined
let(:options_serializer_class) { V2::UserSerializer }
let(:options_serializer_class) { V3::UserSerializer }

let(:serializer) { resolver.serializer }

Expand Down Expand Up @@ -51,12 +52,55 @@
it 'returns serializer' do
expect(serializer).to be_kind_of(serializer_class)
end

context 'each serializer' do
let(:options) {
super().except(:serializer).merge(
each_serializer: V2::UserSerializer
)
}
let(:each_serializer) { serializer.send(:options)[:serializer] }

context 'each_serializer option' do
it 'returns expected serializer' do
expect(each_serializer).to eq(V2::UserSerializer)
end
end

context 'no each_serializer option' do
let(:options) { super().except(:each_serializer) }

context 'namespace inferred' do
it 'returns expected serializer' do
expect(each_serializer).to eq(V4::UserSerializer)
end
end

context 'not namespace inferred' do
let(:options) { super().except(:for) }

context 'version inferred' do
it 'returns expected serializer' do
expect(each_serializer).to eq(V5::UserSerializer)
end
end

context 'not version inferred' do
let(:options) { super().except(:version) }

it 'returns nil' do
expect(each_serializer).to eq(nil)
end
end
end
end
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)
expect(serializer).to be_kind_of(V3::UserSerializer)
end
end

Expand All @@ -65,7 +109,7 @@

context 'namespace inferred' do
it 'returns inferred serializer' do
expect(serializer).to be_kind_of(V3::UserSerializer)
expect(serializer).to be_kind_of(V4::UserSerializer)
end
end

Expand All @@ -74,7 +118,7 @@

context 'version inferred' do
it 'returns inferred serializer' do
expect(serializer).to be_kind_of(V4::UserSerializer)
expect(serializer).to be_kind_of(V5::UserSerializer)
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module V3
module V4
class UsersApi < Grape::API
resource :users do
desc 'all users'
Expand Down
5 changes: 5 additions & 0 deletions spec/support/serializers/v5/user_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module V5
class UserSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :email
end
end