Skip to content

Add supports for :links and :data options to has_many associations for jsonapi adapter #1282

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

Closed
wants to merge 1 commit into from
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
52 changes: 44 additions & 8 deletions lib/active_model/serializer/adapter/json_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def primary_data_for(serializer)
end
end

def relationship_value_for(serializer, options = {})
def relationship_data_for(serializer, options = {})
if serializer.respond_to?(:each)
serializer.map { |s| resource_identifier_for(s) }
else
Expand All @@ -173,18 +173,52 @@ def relationship_value_for(serializer, options = {})
end
end

def relationship_links_for(name, owner, options = {})
options[:links].each_with_object({}) do |(key, link), hash|
if link.respond_to?(:call)
hash[key] = link.call(owner, name)
Copy link
Member

Choose a reason for hiding this comment

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

should be respond_to?(:call)

Copy link
Member

Choose a reason for hiding this comment

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

here and elsewhere

else
hash[key] = link
end

hash
end
end

def relationship_meta_for(name, owner, options = {})
if options[:meta].respond_to?(:call)
options[:meta].call(owner, name)
else
options[:meta]
end
end

def relationships_for(serializer)
serializer.associations.each_with_object({}) do |association, hash|
hash[association.key] = { data: relationship_value_for(association.serializer, association.options) }
hash[association.key] = {}

if association.data?
hash[association.key][:data] = relationship_data_for(association.serializer, association.options)
Copy link
Member

Choose a reason for hiding this comment

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

oh, interesting, you're re-using the :data key

end

if association.options.fetch(:links, false)
hash[association.key][:links] = relationship_links_for(association.name, serializer.object, association.options)
end

if association.options.fetch(:meta, false)
hash[association.key][:meta] = relationship_meta_for(association.name, serializer.object, association.options)
end
end
end

def included_resources(include_tree, primary_data)
included = []

serializer.associations(include_tree).each do |association|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
end
serializer.associations(include_tree)
.select(&:data?)
.each do |association|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
end

included
end
Expand All @@ -202,9 +236,11 @@ def add_included_resources_for(serializer, include_tree, primary_data, included)
return if included.include?(resource_object) || primary_data.include?(resource_object)
included.push(resource_object)

serializer.associations(include_tree).each do |association|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
end
serializer.associations(include_tree)
.select(&:data?)
.each do |association|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
end
end
end

Expand Down
6 changes: 6 additions & 0 deletions lib/active_model/serializer/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ class Serializer
def key
options.fetch(:key, name)
end

# @return [Boolean]
#
def data?
@data ||= options.fetch(:data, true)
end
end
end
end
250 changes: 250 additions & 0 deletions test/adapter/json_api/has_many_links_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
require 'test_helper'

module ActionController
module Serialization
class JsonApiHasManyLinksTest < ActionController::TestCase
class LinkTagSerializer < ActiveModel::Serializer
has_many :posts, links: { related: proc { |object, name| "http://test.host/tags/#{object.id}/#{name}" } }, data: false
end
Copy link
Member

Choose a reason for hiding this comment

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

plz just class LinkTagSerializesr < AM::S. we're moving away from that weird Class.new(AM::S)


class MyController < ActionController::Base
def render_resource_with_has_many_association
@tag = Tag.new(id: 1)
@tag.posts = []
render json: @tag, adapter: :json_api, serializer: LinkTagSerializer
end
end

tests MyController

def test_render_resource_with_has_many_association
get :render_resource_with_has_many_association
expected = {
data: {
id: '1',
type: 'tags',
relationships: {
posts: {
links: {
related: 'http://test.host/tags/1/posts'
}
}
}
}
}
assert_equal expected.to_json, response.body
end
end

class JsonApiHasManyLinksWithIncludedTest < ActionController::TestCase
class CommentSerializer < ActiveModel::Serializer
attribute :text
end

class LinkPostSerializer < ActiveModel::Serializer
has_many :comments, links: { related: 'comments-link' }, data: true, serializer: CommentSerializer
end

class LinkTagSerializer < ActiveModel::Serializer
has_many :posts, links: { related: 'posts-link' }, data: true, serializer: LinkPostSerializer
end

class MyController < ActionController::Base
def render_resource_with_has_many_association
@tag = Tag.new(id: 1)
@post = Post.new(id: 2)
@comment = Comment.new(id: 3, text: nil)
@post.comments = [@comment]
@tag.posts = [@post]
render json: @tag,
adapter: :json_api,
serializer: LinkTagSerializer,
include: 'posts,posts.comments'
end
end

tests MyController

def test_render_resource_with_has_many_association
get :render_resource_with_has_many_association
expected = {
data: {
id: '1',
type: 'tags',
relationships: {
posts: {
data: [{ id: '2', type: 'posts' }],
links: {
related: 'posts-link'
}
}
}
},
included: [
{
id: '2',
type: 'posts',
relationships: {
comments: {
data: [{ id: '3', type: 'comments' }],
links: {
related: 'comments-link'
}
}
}
},
{
id: '3',
type: 'comments',
attributes: {
text: nil
}
}
]
}

assert_equal expected.to_json, response.body
end
end

class JsonApiHasManyLinksWithIncludedAndNoDataTest < ActionController::TestCase
class LinkPostSerializer < ActiveModel::Serializer
has_many :comments, links: { related: 'comments-link' }, data: false
end

class LinkTagSerializer < ActiveModel::Serializer
has_many :posts, links: { related: 'posts-link' }, data: false, serializer: LinkPostSerializer
end

class MyController < ActionController::Base
def render_resource_with_has_many_association
@tag = Tag.new(id: 1)
@post = Post.new(id: 2)
@comment = Comment.new(id: 3, text: nil)
@post.comments = [@comment]
@tag.posts = [@post]
render json: @tag,
adapter: :json_api,
serializer: LinkTagSerializer,
include: 'posts,posts.comments'
end
end

tests MyController

def test_render_resource_with_has_many_association
get :render_resource_with_has_many_association
expected = {
data: {
id: '1',
type: 'tags',
relationships: {
posts: {
links: {
related: 'posts-link'
}
}
}
}
}

assert_equal expected.to_json, response.body
end
end

class JsonApiHasManyLinksWithNestedIncludedAndNoDataTest < ActionController::TestCase
class LinkPostSerializer < ActiveModel::Serializer
has_many :comments, links: { related: 'comments-link' }, data: false
end

class LinkTagSerializer < ActiveModel::Serializer
has_many :posts, links: { related: 'posts-link' }, data: true, serializer: LinkPostSerializer
end

class MyController < ActionController::Base
def render_resource_with_has_many_association
@tag = Tag.new(id: 1)
@post = Post.new(id: 2)
@comment = Comment.new(id: 3, text: nil)
@post.comments = [@comment]
@tag.posts = [@post]
render json: @tag,
adapter: :json_api,
serializer: LinkTagSerializer,
include: 'posts,posts.comments'
end
end

tests MyController

def test_render_resource_with_has_many_association
get :render_resource_with_has_many_association
expected = {
data: {
id: '1',
type: 'tags',
relationships: {
posts: {
data: [{ id: '2', type: 'posts' }],
links: {
related: 'posts-link'
}
}
}
},
included: [
{
id: '2',
type: 'posts',
relationships: {
comments: {
links: {
related: 'comments-link'
}
}
}
}
]
}

assert_equal expected.to_json, response.body
end
end

class JsonApiHasManyLinksWithDataTest < ActionController::TestCase
class LinkTagSerializer < ActiveModel::Serializer
has_many :posts, links: { related: proc { |object, name| "http://test.host/tags/#{object.id}/#{name}" } }
end

class MyController < ActionController::Base
def render_resource_with_has_many_association
@tag = Tag.new(id: 1)
@post = Post.new(id: 2)
@tag.posts = [@post]
render json: @tag, adapter: :json_api, serializer: LinkTagSerializer
end
end

tests MyController

def test_render_resource_with_has_many_association
get :render_resource_with_has_many_association
expected = {
data: {
id: '1',
type: 'tags',
relationships: {
posts: {
data: [{ id: '2', type: 'posts' }],
links: {
related: 'http://test.host/tags/1/posts'
}
}
}
}
}
assert_equal expected.to_json, response.body
end
end
end
end
40 changes: 40 additions & 0 deletions test/adapter/json_api/has_many_meta_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require 'test_helper'

module ActionController
module Serialization
class JsonApiHasManyMetaTest < ActionController::TestCase
class MetaTagSerializer < ActiveModel::Serializer
has_many :posts, meta: proc { { page: '1' } }
end

class MyController < ActionController::Base
def render_resource_with_has_many_association
@tag = Tag.new(id: 1)
@tag.posts = []
render json: @tag, adapter: :json_api, serializer: MetaTagSerializer
end
end

tests MyController

def test_render_resource_with_has_many_association
get :render_resource_with_has_many_association
expected = {
data: {
id: '1',
type: 'tags',
relationships: {
posts: {
data: [],
meta: {
page: '1'
}
}
}
}
}
assert_equal expected.to_json, response.body
end
end
end
end
Loading