Skip to content
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

Includes links inside of linked resources #727

Merged
merged 1 commit into from
Nov 12, 2014
Merged
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
102 changes: 59 additions & 43 deletions lib/active_model/serializer/adapter/json_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,57 @@ def serializable_hash(options = {})
else
@hash[@root] = attributes_for_serializer(serializer, @options)

serializer.each_association do |name, association, opts|
@hash[@root][:links] ||= {}

if association.respond_to?(:each)
add_links(name, association, opts)
else
add_link(name, association, opts)
end
end
add_resource_links(@hash[@root], serializer)
end

@hash
end

def add_links(name, serializers, options)
if serializers.first
type = serializers.first.object.class.to_s.underscore.pluralize
end
private
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved add_links and add_link to below private.

Copy link
Member

Choose a reason for hiding this comment

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

+1


def add_links(resource, name, serializers)
type = serialized_object_type(serializers)
resource[:links] ||= {}

if name.to_s == type || !type
@hash[@root][:links][name] ||= []
@hash[@root][:links][name] += serializers.map{|serializer| serializer.id.to_s }
resource[:links][name] ||= []
resource[:links][name] += serializers.map{|serializer| serializer.id.to_s }
else
@hash[@root][:links][name] ||= {}
@hash[@root][:links][name][:type] = type
@hash[@root][:links][name][:ids] ||= []
@hash[@root][:links][name][:ids] += serializers.map{|serializer| serializer.id.to_s }
end

unless serializers.none? || @options[:embed] == :ids
serializers.each do |serializer|
add_linked(name, serializer)
end
resource[:links][name] ||= {}
resource[:links][name][:type] = type
resource[:links][name][:ids] ||= []
resource[:links][name][:ids] += serializers.map{|serializer| serializer.id.to_s }
end
end

def add_link(name, serializer, options)
def add_link(resource, name, serializer)
resource[:links] ||= {}
resource[:links][name] = nil

if serializer
type = serializer.object.class.to_s.underscore
type = serialized_object_type(serializer)
if name.to_s == type || !type
@hash[@root][:links][name] = serializer.id.to_s
resource[:links][name] = serializer.id.to_s
else
@hash[@root][:links][name] ||= {}
@hash[@root][:links][name][:type] = type
@hash[@root][:links][name][:id] = serializer.id.to_s
end

unless @options[:embed] == :ids
add_linked(name, serializer)
resource[:links][name] ||= {}
resource[:links][name][:type] = type
resource[:links][name][:id] = serializer.id.to_s
end
else
@hash[@root][:links][name] = nil
end
end

def add_linked(resource, serializer, parent = nil)
resource_path = [parent, resource].compact.join('.')
if include_assoc? resource_path
plural_name = resource.to_s.pluralize.to_sym
def add_linked(resource_name, serializer, parent = nil)
resource_path = [parent, resource_name].compact.join('.')

if include_assoc?(resource_path)
plural_name = resource_name.to_s.pluralize.to_sym
attrs = [attributes_for_serializer(serializer, @options)].flatten
@top[:linked] ||= {}
@top[:linked][plural_name] ||= []

attrs.each do |attrs|
add_resource_links(attrs, serializer, add_linked: false)

@top[:linked][plural_name].push(attrs) unless @top[:linked][plural_name].include?(attrs)
end
end
Expand All @@ -91,8 +79,6 @@ def add_linked(resource, serializer, parent = nil)
end if include_nested_assoc? resource_path
end

private

def attributes_for_serializer(serializer, options)
if serializer.respond_to?(:each)
result = []
Expand Down Expand Up @@ -124,6 +110,36 @@ def check_assoc(assoc)
s.match(/^#{assoc.gsub('.', '\.')}/)
end
end

def serialized_object_type(serializer)
return false unless Array(serializer).first
type_name = Array(serializer).first.object.class.to_s.underscore
if serializer.respond_to?(:first)
type_name.pluralize
else
type_name
end
end

def add_resource_links(attrs, serializer, options = {})
options[:add_linked] = options.fetch(:add_linked, true)

Array(serializer).first.each_association do |name, association, opts|
attrs[:links] ||= {}

if association.respond_to?(:each)
add_links(attrs, name, association)
else
add_link(attrs, name, association)
end

if @options[:embed] != :ids && options[:add_linked]
Array(association).each do |association|
add_linked(name, association)
end
end
end
end
end
end
end
Expand Down
23 changes: 20 additions & 3 deletions test/action_controller/json_api_linked_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ class JsonApiLinkedTest < ActionController::TestCase
class MyController < ActionController::Base
def setup_post
@role1 = Role.new(id: 1, name: 'admin')
@role2 = Role.new(id: 2, name: 'colab')
@author = Author.new(id: 1, name: 'Steve K.')
@author.posts = []
@author.bio = nil
@author.roles = [@role1]
@author.roles = [@role1, @role2]
@role1.author = @author
@role2.author = @author
@author2 = Author.new(id: 2, name: 'Anonymous')
@author2.posts = []
@author2.bio = nil
Expand Down Expand Up @@ -98,11 +101,25 @@ def test_render_resource_with_nested_has_many_include
expected_linked = {
"authors" => [{
"id" => "1",
"name" => "Steve K."
"name" => "Steve K.",
"links" => {
"posts" => [],
"roles" => ["1", "2"],
"bio" => nil
}
}],
"roles"=>[{
"id" => "1",
"name" => "admin"
"name" => "admin",
"links" => {
"author" => "1"
}
}, {
"id" => "2",
"name" => "colab",
"links" => {
"author" => "1"
}
}]
}
assert_equal expected_linked, response['linked']
Expand Down
11 changes: 10 additions & 1 deletion test/adapter/json_api/belongs_to_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,16 @@ def test_includes_post_id

def test_includes_linked_post
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'post')
assert_equal([{id: "42", title: 'New Post', body: 'Body'}], @adapter.serializable_hash[:linked][:posts])
expected = [{
id: "42",
title: 'New Post',
body: 'Body',
links: {
comments: ["1"],
author: "1"
}
}]
assert_equal expected, @adapter.serializable_hash[:linked][:posts]
end

def test_include_nil_author
Expand Down
20 changes: 16 additions & 4 deletions test/adapter/json_api/has_many_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,22 @@ def test_includes_comment_ids

def test_includes_linked_comments
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments')
assert_equal([
{id: "1", body: 'ZOMG A COMMENT'},
{id: "2", body: 'ZOMG ANOTHER COMMENT'}
], @adapter.serializable_hash[:linked][:comments])
expected = [{
id: "1",
body: 'ZOMG A COMMENT',
links: {
post: "1",
author: nil
}
}, {
id: "2",
body: 'ZOMG ANOTHER COMMENT',
links: {
post: "1",
author: nil
}
}]
assert_equal expected, @adapter.serializable_hash[:linked][:comments]
end

def test_no_include_linked_if_comments_is_empty
Expand Down
40 changes: 36 additions & 4 deletions test/adapter/json_api/linked_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,42 @@ def test_include_multiple_posts_and_linked
{ title: "Hello!!", body: "Hello, world!!", id: "1", links: { comments: ['1', '2'], author: "1" } },
{ title: "New Post", body: "Body", id: "2", links: { comments: [], :author => "1" } }
], @adapter.serializable_hash[:posts])
assert_equal({ :comments => [{ :id => "1", :body => "ZOMG A COMMENT" },
{ :id => "2", :body => "ZOMG ANOTHER COMMENT" }],
:authors => [{ :id => "1", :name => "Steve K." }],
:bios=>[{:id=>"1", :content=>"AMS Contributor"}] }, @adapter.serializable_hash[:linked])


expected = {
comments: [{
id: "1",
body: "ZOMG A COMMENT",
links: {
post: "1",
author: nil
}
}, {
id: "2",
body: "ZOMG ANOTHER COMMENT",
links: {
post: "1",
author: nil
}
}],
authors: [{
id: "1",
name: "Steve K.",
links: {
posts: ["1", "2"],
roles: [],
bio: "1"
}
}],
bios: [{
id: "1",
content: "AMS Contributor",
links: {
author: "1"
}
}]
}
assert_equal expected, @adapter.serializable_hash[:linked]
end
end
end
Expand Down