Skip to content

Namespaced serializers #1338

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

Closed
Closed
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
5 changes: 4 additions & 1 deletion lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ def self.serializer_lookup_chain_for(klass)
resource_namespace = klass.name.deconstantize
serializer_class_name = "#{resource_class_name}Serializer"

chain.push("#{name}::#{serializer_class_name}") if self != ActiveModel::Serializer
if self != ActiveModel::Serializer
chain.push("#{name}::#{serializer_class_name}")
chain.push(*name.deconstantize.split('::').each_with_object([]) { |element, array| array.push((array.last ? array.last + '::' : '') + element) }.reverse.map { |k| k + "::#{serializer_class_name}" })
Copy link
Contributor

Choose a reason for hiding this comment

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

A slightly shorter version that leads to the same result:

klass.to_s.split('::').reverse.reduce([]) { |a, e| a + [[e] + Array(a.last)] }.reverse

end
Copy link
Member

Choose a reason for hiding this comment

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

@beauby another reason why search should be a detect (first found) rather than functional style without a stream to take from :) as it is, it's making more calculations than it needs to.

chain.push("#{resource_namespace}::#{serializer_class_name}")

chain
Expand Down
43 changes: 43 additions & 0 deletions test/serializers/associations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,49 @@ def false
assert_equal(expected, hash)
end
end

class NamespaceNestedSerializersTest < Minitest::Test
Copy link
Member

Choose a reason for hiding this comment

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

This snippet from the PR description would also make a great test showing the old behavior and the behavior this PR introduces. It wasn't obvious to me from the examples why you'd want to 'step back' one level in the serializer lookup.

It simply modifies Serializer#serializer_lookup_chain_for to add all
of the possible levels of namespaces based on the current serializer.
With this patch for example if we have:

class Public::V1::PostSerializer
belongs_to :author
end

class Public::V1::AuthorSerializer
end
we will wind up with the following items in the lookup chain for an
author when serializing a Post with Public::V1::PostSerializer:

['Public::V1::PostSerializer::AuthorSerializer',
'Public::V1::AuthorSerializer', 'Public::AuthorSerializer',
'::AuthorSerializer']

Post = Class.new(::Model)
Comment = Class.new(::Model)
Author = Class.new(::Model)
Description = Class.new(::Model)

module SerializerNamespace
class PostSerializer < ActiveModel::Serializer
has_many :comments
belongs_to :author
has_one :description
end
CommentSerializer = Class.new(ActiveModel::Serializer)
AuthorSerializer = Class.new(ActiveModel::Serializer)
DescriptionSerializer = Class.new(ActiveModel::Serializer)
end

def setup
@comment = Comment.new
@author = Author.new
@description = Description.new
@post = Post.new(comments: [@comment],
author: @author,
description: @description)
@post_serializer = SerializerNamespace::PostSerializer.new(@post)
end

def test_associations_namespaced_resources_new
@post_serializer.associations.each do |association|
case association.key
when :comments
assert_instance_of(SerializerNamespace::CommentSerializer, association.serializer.first)
when :author
assert_instance_of(SerializerNamespace::AuthorSerializer, association.serializer)
when :description
assert_instance_of(SerializerNamespace::DescriptionSerializer, association.serializer)
else
flunk "Unknown association: #{key}"
end
end
end
end
end
end
end
10 changes: 10 additions & 0 deletions test/serializers/serializer_for_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ class SerializerTest < ActiveSupport::TestCase
module ResourceNamespace
Post = Class.new(::Model)
Comment = Class.new(::Model)
Author = Class.new(::Model)

class PostSerializer < ActiveModel::Serializer
class CommentSerializer < ActiveModel::Serializer
end
end

class AuthorSerializer < ActiveModel::Serializer
end
end

class MyProfile < Profile
Expand Down Expand Up @@ -128,6 +132,12 @@ def test_serializer_for_nested_resource_with_lookup_disabled
end
assert_equal nil, serializer
end

def test_serializer_for_namespace_nested_resource
author = ResourceNamespace::Author.new
serializer = ResourceNamespace::PostSerializer.serializer_for(author)
assert_equal(ResourceNamespace::AuthorSerializer, serializer)
end
end
end
end
Expand Down