From b1fd43303cb10cc636e06ccddf1a76a18981c70b Mon Sep 17 00:00:00 2001 From: Yohan Robert Date: Wed, 10 Feb 2016 21:00:53 +0100 Subject: [PATCH] Fix relationship behavior when using block When using the relationships DSL with a block e.g. has_one :bio do link :self, "some_link" end the "data" field would be rendered with a nil value even though the bio is not nil. This happened because the block return value was set to nil but used as a value for the "data" field. --- lib/active_model/serializer/reflection.rb | 13 +- test/adapter/json_api/links_test.rb | 29 ---- test/adapter/json_api/relationship_test.rb | 173 +++++++++++++++++++++ 3 files changed, 182 insertions(+), 33 deletions(-) create mode 100644 test/adapter/json_api/relationship_test.rb diff --git a/lib/active_model/serializer/reflection.rb b/lib/active_model/serializer/reflection.rb index 89fa4074..d7378e60 100644 --- a/lib/active_model/serializer/reflection.rb +++ b/lib/active_model/serializer/reflection.rb @@ -42,17 +42,17 @@ module ActiveModel def link(name, value = nil, &block) @_links[name] = block || value - nil + :nil end def meta(value = nil, &block) @_meta = block || value - nil + :nil end def include_data(value = true) @_include_data = value - nil + :nil end def value(serializer) @@ -60,7 +60,12 @@ module ActiveModel @scope = serializer.scope if block - instance_eval(&block) + block_value = instance_eval(&block) + if block_value == :nil + serializer.read_attribute_for_serialization(name) + else + block_value + end else serializer.read_attribute_for_serialization(name) end diff --git a/test/adapter/json_api/links_test.rb b/test/adapter/json_api/links_test.rb index 81dde4a3..43e37dd7 100644 --- a/test/adapter/json_api/links_test.rb +++ b/test/adapter/json_api/links_test.rb @@ -17,18 +17,6 @@ module ActiveModel link :yet_another do "//example.com/resource/#{object.id}" end - - has_many :posts do - link :self do - href '//example.com/link_author/relationships/posts' - meta stuff: 'value' - end - link :related do - href '//example.com/link_author/posts' - meta count: object.posts.count - end - include_data false - end end def setup @@ -91,23 +79,6 @@ module ActiveModel } assert_equal(expected, hash[:data][:links]) end - - def test_relationship_links - hash = serializable(@author, adapter: :json_api).serializable_hash - expected = { - links: { - self: { - href: '//example.com/link_author/relationships/posts', - meta: { stuff: 'value' } - }, - related: { - href: '//example.com/link_author/posts', - meta: { count: 1 } - } - } - } - assert_equal(expected, hash[:data][:relationships][:posts]) - end end end end diff --git a/test/adapter/json_api/relationship_test.rb b/test/adapter/json_api/relationship_test.rb new file mode 100644 index 00000000..110fbec4 --- /dev/null +++ b/test/adapter/json_api/relationship_test.rb @@ -0,0 +1,173 @@ +require 'test_helper' + +module ActiveModel + class Serializer + module Adapter + class JsonApi + class RelationshipTest < ActiveSupport::TestCase + RelationshipAuthor = Class.new(::Model) + class RelationshipAuthorSerializer < ActiveModel::Serializer + has_one :bio do + link :self, '//example.com/link_author/relationships/bio' + end + + has_one :profile do + link :related do + "//example.com/profiles/#{object.profile.id}" + end + end + + has_many :locations do + link :related do + ids = object.locations.map!(&:id).join(',') + href "//example.com/locations/#{ids}" + end + end + + has_many :posts do + link :related do + ids = object.posts.map!(&:id).join(',') + href "//example.com/posts/#{ids}" + meta ids: ids + end + end + + has_many :roles do + meta count: object.posts.count + end + + has_one :blog do + link :self, '//example.com/link_author/relationships/blog' + include_data false + end + + belongs_to :reviewer do + meta name: 'Dan Brown' + include_data true + end + + has_many :likes do + link :related do + ids = object.likes.map!(&:id).join(',') + href "//example.com/likes/#{ids}" + meta ids: ids + end + meta liked: object.likes.any? + end + end + + def setup + @post = Post.new(id: 1337, comments: [], author: nil) + @blog = Blog.new(id: 1337, name: 'extra') + @bio = Bio.new(id: 1337) + @like = Like.new(id: 1337) + @role = Role.new(id: 1337) + @profile = Profile.new(id: 1337) + @location = Location.new(id: 1337) + @reviewer = Author.new(id: 1337) + @author = RelationshipAuthor.new( + id: 1337, + posts: [@post], + blog: @blog, + reviewer: @reviewer, + bio: @bio, + likes: [@like], + roles: [@role], + locations: [@location], + profile: @profile + ) + end + + def test_relationship_simple_link + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + data: { + id: '1337', + type: 'bios' + }, + links: { + self: '//example.com/link_author/relationships/bio' + } + } + assert_equal(expected, hash[:data][:relationships][:bio]) + end + + def test_relationship_block_link + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + data: { id: '1337', type: 'profiles' }, + links: { related: '//example.com/profiles/1337' } + } + assert_equal(expected, hash[:data][:relationships][:profile]) + end + + def test_relationship_block_link_href + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + data: [{ id: '1337', type: 'locations' }], + links: { + related: { href: '//example.com/locations/1337' } + } + } + assert_equal(expected, hash[:data][:relationships][:locations]) + end + + def test_relationship_block_link_meta + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + data: [{ id: '1337', type: 'posts' }], + links: { + related: { + href: '//example.com/posts/1337', + meta: { ids: '1337' } + } + } + } + assert_equal(expected, hash[:data][:relationships][:posts]) + end + + def test_relationship_meta + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + data: [{ id: '1337', type: 'roles' }], + meta: { count: 1 } + } + assert_equal(expected, hash[:data][:relationships][:roles]) + end + + def test_relationship_not_including_data + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + links: { self: '//example.com/link_author/relationships/blog' } + } + assert_equal(expected, hash[:data][:relationships][:blog]) + end + + def test_relationship_including_data_explicit + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + data: { id: '1337', type: 'authors' }, + meta: { name: 'Dan Brown' } + } + assert_equal(expected, hash[:data][:relationships][:reviewer]) + end + + def test_relationship_with_everything + hash = serializable(@author, adapter: :json_api).serializable_hash + expected = { + data: [{ id: '1337', type: 'likes' }], + links: { + related: { + href: '//example.com/likes/1337', + meta: { ids: '1337' } + } + }, + meta: { liked: true } + } + assert_equal(expected, hash[:data][:relationships][:likes]) + end + end + end + end + end +end