Skip to content

Commit b44eee1

Browse files
committed
Namespaced serializers
Adds support for finding serializers based on the "current" serializers namespace to support things like serializer versioning. Without this you have to be explicit in naming which serializer to use in all of your serializer associations. That's OK usually (albeit annoying) but it breaks polymorphism since you can't really specify the right serializer ahead of time. I came across a bunch of PRs such as #1225 that talk about different versions of namespacing based on modules or controllers or whatever and some of them even talk about supporting serializer namespaces but I have been unable to find one that actually does this part of it. If there is already work being done for this, feel free to ignore this PR but we needed this for our current work. This PR builds on top of the work done by @beauby in #1225 to add support for namespaced serializer lookups. @beauby's work only allowed for actual nesting of serializers withing a namespaced serializer (e.g. for overrides) but did not actually walk up the tree to siblings or serializers farther up the inheritance/namespace tree. 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: ```ruby 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: ```ruby ['Public::V1::PostSerializer::AuthorSerializer', 'Public::V1::AuthorSerializer', 'Public::AuthorSerializer', '::AuthorSerializer'] ```
1 parent aa43848 commit b44eee1

File tree

3 files changed

+57
-1
lines changed

3 files changed

+57
-1
lines changed

lib/active_model/serializer.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,10 @@ def self.serializer_lookup_chain_for(klass)
195195
resource_namespace = klass.name.deconstantize
196196
serializer_class_name = "#{resource_class_name}Serializer"
197197

198-
chain.push("#{name}::#{serializer_class_name}") if self != ActiveModel::Serializer
198+
if self != ActiveModel::Serializer
199+
chain.push("#{name}::#{serializer_class_name}")
200+
chain.push(*name.deconstantize.split('::').each_with_object([]) { |element, array| array.push((array.last ? array.last + '::' : '') + element) }.reverse.map { |k| k + "::#{serializer_class_name}" })
201+
end
199202
chain.push("#{resource_namespace}::#{serializer_class_name}")
200203

201204
chain

test/serializers/associations_test.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,49 @@ def test_associations_namespaced_resources
207207
end
208208
end
209209
end
210+
211+
class NamespaceNestedSerializersTest < Minitest::Test
212+
Post = Class.new(::Model)
213+
Comment = Class.new(::Model)
214+
Author = Class.new(::Model)
215+
Description = Class.new(::Model)
216+
217+
module SerializerNamespace
218+
class PostSerializer < ActiveModel::Serializer
219+
has_many :comments
220+
belongs_to :author
221+
has_one :description
222+
end
223+
CommentSerializer = Class.new(ActiveModel::Serializer)
224+
AuthorSerializer = Class.new(ActiveModel::Serializer)
225+
DescriptionSerializer = Class.new(ActiveModel::Serializer)
226+
end
227+
228+
def setup
229+
@comment = Comment.new
230+
@author = Author.new
231+
@description = Description.new
232+
@post = Post.new(comments: [@comment],
233+
author: @author,
234+
description: @description)
235+
@post_serializer = SerializerNamespace::PostSerializer.new(@post)
236+
end
237+
238+
def test_associations_namespaced_resources_new
239+
@post_serializer.associations.each do |association|
240+
case association.key
241+
when :comments
242+
assert_instance_of(SerializerNamespace::CommentSerializer, association.serializer.first)
243+
when :author
244+
assert_instance_of(SerializerNamespace::AuthorSerializer, association.serializer)
245+
when :description
246+
assert_instance_of(SerializerNamespace::DescriptionSerializer, association.serializer)
247+
else
248+
flunk "Unknown association: #{key}"
249+
end
250+
end
251+
end
252+
end
210253
end
211254
end
212255
end

test/serializers/serializer_for_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ class SerializerTest < Minitest::Test
3030
module ResourceNamespace
3131
Post = Class.new(::Model)
3232
Comment = Class.new(::Model)
33+
Author = Class.new(::Model)
3334

3435
class PostSerializer < ActiveModel::Serializer
3536
class CommentSerializer < ActiveModel::Serializer
3637
end
3738
end
39+
40+
class AuthorSerializer < ActiveModel::Serializer
41+
end
3842
end
3943

4044
class MyProfile < Profile
@@ -91,6 +95,12 @@ def test_serializer_for_nested_resource
9195
serializer = ResourceNamespace::PostSerializer.serializer_for(comment)
9296
assert_equal(ResourceNamespace::PostSerializer::CommentSerializer, serializer)
9397
end
98+
99+
def test_serializer_for_namespace_nested_resource
100+
author = ResourceNamespace::Author.new
101+
serializer = ResourceNamespace::PostSerializer.serializer_for(author)
102+
assert_equal(ResourceNamespace::AuthorSerializer, serializer)
103+
end
94104
end
95105
end
96106
end

0 commit comments

Comments
 (0)