Skip to content

Commit 3ad2457

Browse files
dariush-yNullVoxPopuli
authored andcommitted
Bugfix/redefine associations on inherited serializers (#1848)
* replace reflection collection type with hash to prevent duplicated associations in some cases * include tests * Fix robucup offenses * Improve test * Remove usless requirement * improve tests * remove custom_options option from Post and InheritedPost serializer * Improve tests * update changelog * update changelog
1 parent 9a206a1 commit 3ad2457

File tree

4 files changed

+64
-8
lines changed

4 files changed

+64
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Misc:
1616

1717
Fixes:
1818
- [#1814](https://github.com/rails-api/active_model_serializers/pull/1814) Ensuring read_multi works with fragment cache
19+
- [#1848](https://github.com/rails-api/active_model_serializers/pull/1848) Redefine associations on inherited serializers. (@EhsanYousefi)
1920

2021
Misc:
2122
- [#1808](https://github.com/rails-api/active_model_serializers/pull/1808) Adds documentation for `fields` option. (@luizkowalski)

lib/active_model/serializer/associations.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module Associations
1313
included do
1414
with_options instance_writer: false, instance_reader: true do |serializer|
1515
serializer.class_attribute :_reflections
16-
self._reflections ||= []
16+
self._reflections ||= {}
1717
end
1818

1919
extend ActiveSupport::Autoload
@@ -74,7 +74,8 @@ def has_one(name, options = {}, &block) # rubocop:disable Style/PredicateName
7474
# @api private
7575
#
7676
def associate(reflection)
77-
self._reflections << reflection
77+
key = reflection.options[:key]
78+
key ? self._reflections[key] = reflection : self._reflections[reflection.name] = reflection
7879
end
7980
end
8081

@@ -86,7 +87,7 @@ def associations(include_directive = ActiveModelSerializers.default_include_dire
8687
return unless object
8788

8889
Enumerator.new do |y|
89-
self.class._reflections.each do |reflection|
90+
self.class._reflections.values.each do |reflection|
9091
next if reflection.excluded?(self)
9192
key = reflection.options.fetch(:key, reflection.name)
9293
next unless include_directive.key?(key)

test/serializers/association_macros_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class AssociationsTestSerializer < Serializer
1111
end
1212

1313
def before_setup
14-
@reflections = AssociationsTestSerializer._reflections
14+
@reflections = AssociationsTestSerializer._reflections.values
1515
end
1616

1717
def test_has_one_defines_reflection

test/serializers/associations_test.rb

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
require 'test_helper'
2-
32
module ActiveModel
43
class Serializer
54
class AssociationsTest < ActiveSupport::TestCase
@@ -104,13 +103,13 @@ def test_associations_inheritance_with_new_association
104103
end
105104

106105
assert(
107-
PostSerializer._reflections.all? do |reflection|
108-
inherited_klass._reflections.include?(reflection)
106+
PostSerializer._reflections.values.all? do |reflection|
107+
inherited_klass._reflections.values.include?(reflection)
109108
end
110109
)
111110

112111
assert(
113-
inherited_klass._reflections.any? do |reflection|
112+
inherited_klass._reflections.values.any? do |reflection|
114113
reflection.name == :top_comments
115114
end
116115
)
@@ -290,6 +289,61 @@ def test_illegal_conditional_associations
290289
assert_match(/:if should be a Symbol, String or Proc/, exception.message)
291290
end
292291
end
292+
293+
class InheritedSerializerTest < ActiveSupport::TestCase
294+
InheritedPostSerializer = Class.new(PostSerializer) do
295+
belongs_to :author, polymorphic: true
296+
has_many :comments, key: :reviews
297+
end
298+
299+
InheritedAuthorSerializer = Class.new(AuthorSerializer) do
300+
has_many :roles, polymorphic: true
301+
has_one :bio, polymorphic: true
302+
end
303+
304+
def setup
305+
@author = Author.new(name: 'Steve K.')
306+
@post = Post.new(title: 'New Post', body: 'Body')
307+
@post_serializer = PostSerializer.new(@post)
308+
@author_serializer = AuthorSerializer.new(@author)
309+
@inherited_post_serializer = InheritedPostSerializer.new(@post)
310+
@inherited_author_serializer = InheritedAuthorSerializer.new(@author)
311+
@author_associations = @author_serializer.associations.to_a
312+
@inherited_author_associations = @inherited_author_serializer.associations.to_a
313+
@post_associations = @post_serializer.associations.to_a
314+
@inherited_post_associations = @inherited_post_serializer.associations.to_a
315+
end
316+
317+
test 'an author serializer must have [posts,roles,bio] associations' do
318+
expected = [:posts, :roles, :bio].sort
319+
result = @author_serializer.associations.map(&:name).sort
320+
assert_equal(result, expected)
321+
end
322+
323+
test 'a post serializer must have [author,comments,blog] associations' do
324+
expected = [:author, :comments, :blog].sort
325+
result = @post_serializer.associations.map(&:name).sort
326+
assert_equal(result, expected)
327+
end
328+
329+
test 'a serializer inheriting from another serializer can redefine has_many and has_one associations' do
330+
expected = [:roles, :bio].sort
331+
result = (@inherited_author_associations - @author_associations).map(&:name).sort
332+
assert_equal(result, expected)
333+
end
334+
335+
test 'a serializer inheriting from another serializer can redefine belongs_to associations' do
336+
expected = [:author, :comments, :blog].sort
337+
result = (@inherited_post_associations - @post_associations).map(&:name).sort
338+
assert_equal(result, expected)
339+
end
340+
341+
test 'a serializer inheriting from another serializer can have an additional association with the same name but with different key' do
342+
expected = [:author, :comments, :blog, :reviews].sort
343+
result = @inherited_post_serializer.associations.map { |a| a.options.fetch(:key, a.name) }.sort
344+
assert_equal(result, expected)
345+
end
346+
end
293347
end
294348
end
295349
end

0 commit comments

Comments
 (0)