mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-23 06:16:50 +00:00
Merge branch 'fix_thread_safety_bug' into 0-10-stable
This commit is contained in:
commit
09264da273
@ -347,7 +347,7 @@ module ActiveModel
|
||||
return Enumerator.new {} unless object
|
||||
|
||||
Enumerator.new do |y|
|
||||
self.class._reflections.each do |key, reflection|
|
||||
(self.instance_reflections ||= self.class._reflections.deep_dup).each do |key, reflection|
|
||||
next if reflection.excluded?(self)
|
||||
next unless include_directive.key?(key)
|
||||
|
||||
@ -411,6 +411,6 @@ module ActiveModel
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :instance_options
|
||||
attr_accessor :instance_options, :instance_reflections
|
||||
end
|
||||
end
|
||||
|
||||
@ -151,6 +151,9 @@ module ActiveModel
|
||||
# @yield [ActiveModel::Serializer]
|
||||
# @return [:nil, associated resource or resource collection]
|
||||
def value(serializer, include_slice)
|
||||
# NOTE(BF): This method isn't thread-safe because the _reflections class attribute is not thread-safe
|
||||
# Therefore, when we build associations from reflections, we dup the entire reflection instance.
|
||||
# Better solutions much appreciated!
|
||||
@object = serializer.object
|
||||
@scope = serializer.scope
|
||||
|
||||
|
||||
@ -423,5 +423,57 @@ module ActiveModel
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
end
|
||||
class ThreadedReflectionTest < ActiveSupport::TestCase
|
||||
class Post < ::Model
|
||||
attributes :id, :title, :body
|
||||
associations :comments
|
||||
end
|
||||
class Comment < ::Model
|
||||
attributes :id, :body
|
||||
associations :post
|
||||
end
|
||||
class CommentSerializer < ActiveModel::Serializer
|
||||
type 'comment'
|
||||
attributes :id, :body
|
||||
has_one :post
|
||||
end
|
||||
class PostSerializer < ActiveModel::Serializer
|
||||
type 'post'
|
||||
attributes :id, :title, :body
|
||||
has_many :comments, serializer: CommentSerializer do
|
||||
sleep 0.1
|
||||
object.comments
|
||||
end
|
||||
end
|
||||
|
||||
# per https://github.com/rails-api/active_model_serializers/issues/2270
|
||||
def test_concurrent_serialization
|
||||
post1 = Post.new(id: 1, title: 'Post 1 Title', body: 'Post 1 Body')
|
||||
post1.comments = [Comment.new(id: 1, body: 'Comment on Post 1', post: post1)]
|
||||
post2 = Post.new(id: 2, title: 'Post 2 Title', body: 'Post 2 Body')
|
||||
post2.comments = [Comment.new(id: 2, body: 'Comment on Post 2', post: post2)]
|
||||
serialized_posts = {
|
||||
first: Set.new,
|
||||
second: Set.new
|
||||
}
|
||||
t1 = Thread.new do
|
||||
10.times do
|
||||
serialized_posts[:first] << PostSerializer.new(post1, {}).to_json
|
||||
end
|
||||
end
|
||||
t2 = Thread.new do
|
||||
10.times do
|
||||
serialized_posts[:second] << PostSerializer.new(post2, {}).to_json
|
||||
end
|
||||
end
|
||||
t1.join
|
||||
t2.join
|
||||
expected_first_post_serialization = '{"id":1,"title":"Post 1 Title","body":"Post 1 Body","comments":[{"id":1,"body":"Comment on Post 1"}]}'
|
||||
expected_second_post_serialization = '{"id":2,"title":"Post 2 Title","body":"Post 2 Body","comments":[{"id":2,"body":"Comment on Post 2"}]}'
|
||||
|
||||
assert_equal [expected_second_post_serialization], serialized_posts[:second].to_a
|
||||
assert_equal [expected_first_post_serialization], serialized_posts[:first].to_a
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user