Skip to content
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
10 changes: 9 additions & 1 deletion lib/active_model/serializable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ module Serializable
def as_json(options={})
if root = options.fetch(:root, json_key)
hash = { root => serializable_object }
hash.merge!(serializable_data)

serializable_data.each_pair do |key, value|
if hash[key].is_a?(Array) && value.is_a?(Array)
hash[key].concat(value)
else
hash[key] = value
end
end

hash
else
serializable_object
Expand Down
70 changes: 44 additions & 26 deletions lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,11 @@ def attributes
end

def associations
associations = self.class._associations
included_associations = filter(associations.keys)
associations.each_with_object({}) do |(name, association), hash|
if included_associations.include? name
if association.embed_ids?
hash[association.key] = serialize_ids association
elsif association.embed_objects?
hash[association.embedded_key] = serialize association
end
allowed_associations.each_with_object({}) do |association, hash|
if association.embed_ids?
hash[association.key] = serialize_ids association
elsif association.embed_objects?
hash[association.embedded_key] = serialize association
end
end
end
Expand All @@ -152,23 +148,8 @@ def filter(keys)
end

def embedded_in_root_associations
associations = self.class._associations
included_associations = filter(associations.keys)
associations.each_with_object({}) do |(name, association), hash|
if included_associations.include? name
if association.embed_in_root?
association_serializer = build_serializer(association)
hash.merge! association_serializer.embedded_in_root_associations

serialized_data = association_serializer.serializable_object
key = association.root_key
if hash.has_key?(key)
hash[key].concat(serialized_data).uniq!
else
hash[key] = serialized_data
end
end
end
allowed_associations.each_with_object({}) do |association, hash|
embed_serialized_assocation(association, hash) if association.embed_in_root?
end
end

Expand Down Expand Up @@ -197,5 +178,42 @@ def serializable_object(options={})
@wrap_in_array ? [hash] : hash
end
alias_method :serializable_hash, :serializable_object

protected

def embed_serialized_assocation(association, hash)
association_serializer = build_serializer(association)
embed_association_root_assocations(association_serializer, hash)
embed_data(hash, association.root_key, association_serializer.serializable_object)
end

def embed_association_root_assocations(association_serializer, hash)
return if association_serializer.send(:object).nil?

association_serializer.embedded_in_root_associations.each do |key, value|
embed_data(hash, key, value)
end
end

def embed_data(hash, key, data)
if hash[key].is_a?(Array) && data.is_a?(Array)
hash[key].concat(data).uniq!
else
hash[key] = data
end
hash
end

def allowed_associations
included_associations.values_at(*allowed_association_keys)
end

def allowed_association_keys
filter(included_associations.keys)
end

def included_associations
self.class._associations
end
end
end
11 changes: 11 additions & 0 deletions test/fixtures/poro.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,24 @@ class Profile < Model
end

class Post < Model
def user
@user
end

def comments
@comments ||= [Comment.new(content: 'C1'),
Comment.new(content: 'C2')]
end
end

class Comment < Model
def user
@user
end

def comments
@comments ||= []
end
end

###
Expand Down
27 changes: 27 additions & 0 deletions test/unit/active_model/array_serializer/serialization_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,33 @@ class << @post2
PostSerializer._associations[:comments] = @old_association
end

def test_associated_objects_of_recursive_instances_embedded_in_root
CommentSerializer.has_many :comments
@association = CommentSerializer._associations[:comments]

@association.embed = :ids
@association.embed_in_root = true

@comment1 = Comment.new(content: 'C1')
@comment2 = Comment.new(content: 'C2')

class << @comment1
attr_writer :comments
end
@comment1.comments = [Comment.new(content: 'C1-1')]

@serializer = ArraySerializer.new([@comment1, @comment2], root: :comments)
assert_equal({
comments: [
{ content: 'C1', 'comment_ids' => @comment1.comments.map(&:object_id) },
{ content: 'C2', 'comment_ids' => [] },
{ content: 'C1-1', 'comment_ids' => []}
]
}, @serializer.as_json)
ensure
CommentSerializer._associations = {}
end

def test_embed_object_for_has_one_association_with_nil_value
@association = UserSerializer._associations[:profile]
@old_association = @association.dup
Expand Down
56 changes: 56 additions & 0 deletions test/unit/active_model/serializer/embed_in_root_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require 'test_helper'

module ActiveModel
class Serializer
class EmbedInRootTest < Minitest::Test
def setup
@old_post_associations = PostSerializer._associations.dup
@old_comment_associations = CommentSerializer._associations.dup
@old_user_associations = UserSerializer._associations.dup

# reset associations
PostSerializer._associations = {}
UserSerializer._associations = {}

# set associations
PostSerializer.has_one :user, embed: :ids, embed_in_root: true
PostSerializer.has_many :comments, embed: :ids, embed_in_root: true
CommentSerializer.has_one :user, embed: :ids, embed_in_root: true

@post = Post.new({ title: 'Title 1', body: 'Body 1', date: '1/1/2000' })
@post_serializer = PostSerializer.new(@post)
end

def teardown
PostSerializer._associations = @old_post_associations
CommentSerializer._associations = @old_comment_associations
UserSerializer._associations = @old_user_associations
end

def test_associations_embedding_ids_including_objects_with_same_key_serialization_using_as_json
@user1 = User.new(name: 'post author')
@user2 = User.new(name: 'comment author')

@post.instance_variable_set(:@user, @user1)
@post.comments.first.instance_variable_set(:@user, @user2)

assert_equal({
'post' => {
title: 'Title 1',
body: 'Body 1',
'user_id' => @user1.object_id,
'comment_ids' => @post.comments.map(&:object_id)
},
'users' => [
{ name: 'post author', email: nil },
{ name: 'comment author', email: nil }
],
comments: [
{ content: 'C1', 'user_id' => @user2.object_id },
{ content: 'C2', 'user_id' => nil }
]
}, @post_serializer.as_json)
end
end
end
end