active_model_serializers 0.10.2 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -1
  3. data/CHANGELOG.md +45 -3
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +4 -1
  6. data/README.md +5 -2
  7. data/active_model_serializers.gemspec +2 -2
  8. data/docs/ARCHITECTURE.md +6 -7
  9. data/docs/README.md +2 -0
  10. data/docs/general/caching.md +7 -1
  11. data/docs/general/configuration_options.md +64 -0
  12. data/docs/general/rendering.md +35 -1
  13. data/docs/general/serializers.md +35 -5
  14. data/docs/howto/add_pagination_links.md +2 -2
  15. data/docs/howto/add_relationship_links.md +137 -0
  16. data/docs/howto/add_root_key.md +4 -0
  17. data/docs/howto/grape_integration.md +42 -0
  18. data/docs/howto/outside_controller_use.md +9 -2
  19. data/docs/howto/passing_arbitrary_options.md +2 -2
  20. data/docs/howto/test.md +2 -0
  21. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  22. data/docs/integrations/ember-and-json-api.md +41 -24
  23. data/lib/action_controller/serialization.rb +9 -0
  24. data/lib/active_model/serializer.rb +19 -22
  25. data/lib/active_model/serializer/association.rb +19 -4
  26. data/lib/active_model/serializer/collection_serializer.rb +8 -5
  27. data/lib/active_model/serializer/{associations.rb → concerns/associations.rb} +8 -5
  28. data/lib/active_model/serializer/{attributes.rb → concerns/attributes.rb} +0 -0
  29. data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +5 -1
  30. data/lib/active_model/serializer/{configuration.rb → concerns/configuration.rb} +24 -1
  31. data/lib/active_model/serializer/{links.rb → concerns/links.rb} +0 -0
  32. data/lib/active_model/serializer/{meta.rb → concerns/meta.rb} +0 -0
  33. data/lib/active_model/serializer/{type.rb → concerns/type.rb} +0 -0
  34. data/lib/active_model/serializer/reflection.rb +37 -21
  35. data/lib/active_model/serializer/version.rb +1 -1
  36. data/lib/active_model_serializers.rb +1 -0
  37. data/lib/active_model_serializers/adapter/attributes.rb +3 -1
  38. data/lib/active_model_serializers/adapter/json_api.rb +15 -22
  39. data/lib/active_model_serializers/adapter/json_api/relationship.rb +30 -19
  40. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +23 -9
  41. data/lib/active_model_serializers/key_transform.rb +4 -0
  42. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  43. data/lib/active_model_serializers/model.rb +1 -1
  44. data/lib/active_model_serializers/serializable_resource.rb +6 -5
  45. data/lib/generators/rails/serializer_generator.rb +1 -1
  46. data/test/action_controller/json_api/fields_test.rb +57 -0
  47. data/test/action_controller/json_api/transform_test.rb +3 -3
  48. data/test/action_controller/lookup_proc_test.rb +49 -0
  49. data/test/action_controller/namespace_lookup_test.rb +226 -0
  50. data/test/active_model_serializers/key_transform_test.rb +32 -0
  51. data/test/active_model_serializers/model_test.rb +11 -0
  52. data/test/adapter/attributes_test.rb +43 -0
  53. data/test/adapter/json/transform_test.rb +1 -1
  54. data/test/adapter/json_api/fields_test.rb +4 -3
  55. data/test/adapter/json_api/has_many_test.rb +21 -0
  56. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +166 -0
  57. data/test/adapter/json_api/linked_test.rb +24 -6
  58. data/test/adapter/json_api/links_test.rb +1 -1
  59. data/test/adapter/json_api/pagination_links_test.rb +17 -2
  60. data/test/adapter/json_api/relationship_test.rb +309 -73
  61. data/test/adapter/json_api/resource_identifier_test.rb +20 -0
  62. data/test/adapter/json_api/transform_test.rb +4 -3
  63. data/test/benchmark/benchmarking_support.rb +1 -1
  64. data/test/benchmark/bm_active_record.rb +81 -0
  65. data/test/benchmark/bm_adapter.rb +38 -0
  66. data/test/benchmark/bm_caching.rb +1 -1
  67. data/test/benchmark/bm_lookup_chain.rb +83 -0
  68. data/test/cache_test.rb +42 -4
  69. data/test/collection_serializer_test.rb +1 -1
  70. data/test/fixtures/poro.rb +44 -41
  71. data/test/generators/serializer_generator_test.rb +22 -5
  72. data/test/serializers/association_macros_test.rb +3 -2
  73. data/test/serializers/associations_test.rb +97 -22
  74. data/test/serializers/attribute_test.rb +1 -1
  75. data/test/serializers/serializer_for_test.rb +3 -3
  76. data/test/serializers/serializer_for_with_namespace_test.rb +87 -0
  77. data/test/support/serialization_testing.rb +16 -0
  78. data/test/test_helper.rb +1 -0
  79. metadata +35 -14
  80. data/test/adapter/json_api/relationships_test.rb +0 -204
@@ -40,6 +40,27 @@ module ActiveModelSerializers
40
40
  assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments])
41
41
  end
42
42
 
43
+ test 'relationships can be whitelisted via fields' do
44
+ @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, fields: { posts: [:author] })
45
+ result = @adapter.serializable_hash
46
+ expected = {
47
+ data: {
48
+ id: '1',
49
+ type: 'posts',
50
+ relationships: {
51
+ author: {
52
+ data: {
53
+ id: '1',
54
+ type: 'authors'
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ assert_equal expected, result
62
+ end
63
+
43
64
  def test_includes_linked_comments
44
65
  @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments])
45
66
  expected = [{
@@ -0,0 +1,166 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ module Adapter
6
+ class JsonApi
7
+ class IncludeParamTest < ActiveSupport::TestCase
8
+ IncludeParamAuthor = Class.new(::Model)
9
+
10
+ class CustomCommentLoader
11
+ def all
12
+ [{ foo: 'bar' }]
13
+ end
14
+ end
15
+
16
+ class TagSerializer < ActiveModel::Serializer
17
+ attributes :id, :name
18
+ end
19
+
20
+ class IncludeParamAuthorSerializer < ActiveModel::Serializer
21
+ class_attribute :comment_loader
22
+
23
+ has_many :tags, serializer: TagSerializer do
24
+ link :self, '//example.com/link_author/relationships/tags'
25
+ include_data :if_sideloaded
26
+ end
27
+
28
+ has_many :unlinked_tags, serializer: TagSerializer do
29
+ include_data :if_sideloaded
30
+ end
31
+
32
+ has_many :posts, serializer: PostWithTagsSerializer do
33
+ include_data :if_sideloaded
34
+ end
35
+ has_many :locations do
36
+ include_data :if_sideloaded
37
+ end
38
+ has_many :comments do
39
+ include_data :if_sideloaded
40
+ IncludeParamAuthorSerializer.comment_loader.all
41
+ end
42
+ end
43
+
44
+ def setup
45
+ IncludeParamAuthorSerializer.comment_loader = Class.new(CustomCommentLoader).new
46
+ @tag = Tag.new(id: 1337, name: 'mytag')
47
+ @author = IncludeParamAuthor.new(
48
+ id: 1337,
49
+ tags: [@tag]
50
+ )
51
+ end
52
+
53
+ def test_relationship_not_loaded_when_not_included
54
+ expected = {
55
+ links: {
56
+ self: '//example.com/link_author/relationships/tags'
57
+ }
58
+ }
59
+
60
+ @author.define_singleton_method(:read_attribute_for_serialization) do |attr|
61
+ fail 'should not be called' if attr == :tags
62
+ super(attr)
63
+ end
64
+
65
+ assert_relationship(:tags, expected)
66
+ end
67
+
68
+ def test_relationship_included
69
+ expected = {
70
+ data: [
71
+ {
72
+ id: '1337',
73
+ type: 'tags'
74
+ }
75
+ ],
76
+ links: {
77
+ self: '//example.com/link_author/relationships/tags'
78
+ }
79
+ }
80
+
81
+ assert_relationship(:tags, expected, include: :tags)
82
+ end
83
+
84
+ def test_sideloads_included
85
+ expected = [
86
+ {
87
+ id: '1337',
88
+ type: 'tags',
89
+ attributes: { name: 'mytag' }
90
+ }
91
+ ]
92
+ hash = result(include: :tags)
93
+ assert_equal(expected, hash[:included])
94
+ end
95
+
96
+ def test_nested_relationship
97
+ expected = {
98
+ data: [
99
+ {
100
+ id: '1337',
101
+ type: 'tags'
102
+ }
103
+ ],
104
+ links: {
105
+ self: '//example.com/link_author/relationships/tags'
106
+ }
107
+ }
108
+
109
+ expected_no_data = {
110
+ links: {
111
+ self: '//example.com/link_author/relationships/tags'
112
+ }
113
+ }
114
+
115
+ assert_relationship(:tags, expected, include: [:tags, { posts: :tags }])
116
+
117
+ @author.define_singleton_method(:read_attribute_for_serialization) do |attr|
118
+ fail 'should not be called' if attr == :tags
119
+ super(attr)
120
+ end
121
+
122
+ assert_relationship(:tags, expected_no_data, include: { posts: :tags })
123
+ end
124
+
125
+ def test_include_params_with_no_block
126
+ @author.define_singleton_method(:read_attribute_for_serialization) do |attr|
127
+ fail 'should not be called' if attr == :locations
128
+ super(attr)
129
+ end
130
+
131
+ expected = { meta: {} }
132
+
133
+ assert_relationship(:locations, expected)
134
+ end
135
+
136
+ def test_block_relationship
137
+ expected = {
138
+ data: [
139
+ { 'foo' => 'bar' }
140
+ ]
141
+ }
142
+
143
+ assert_relationship(:comments, expected, include: [:comments])
144
+ end
145
+
146
+ def test_node_not_included_when_no_link
147
+ expected = nil
148
+ assert_relationship(:unlinked_tags, expected)
149
+ end
150
+
151
+ private
152
+
153
+ def result(opts)
154
+ opts = { adapter: :json_api }.merge(opts)
155
+ serializable(@author, opts).serializable_hash
156
+ end
157
+
158
+ def assert_relationship(relationship_name, expected, opts = {})
159
+ hash = result(opts)
160
+ assert_equal(expected, hash[:data][:relationships][relationship_name])
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -1,10 +1,9 @@
1
1
  require 'test_helper'
2
2
 
3
- NestedPost = Class.new(Model)
3
+ class NestedPost < ::Model; end
4
4
  class NestedPostSerializer < ActiveModel::Serializer
5
5
  has_many :nested_posts
6
6
  end
7
-
8
7
  module ActiveModelSerializers
9
8
  module Adapter
10
9
  class JsonApi
@@ -224,6 +223,25 @@ module ActiveModelSerializers
224
223
  assert_equal expected, relationships
225
224
  end
226
225
 
226
+ def test_underscore_model_namespace_with_namespace_separator_for_linked_resource_type
227
+ spammy_post = Post.new(id: 123)
228
+ spammy_post.related = [Spam::UnrelatedLink.new(id: 456)]
229
+ serializer = SpammyPostSerializer.new(spammy_post)
230
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
231
+ relationships = with_namespace_separator '--' do
232
+ adapter.serializable_hash[:data][:relationships]
233
+ end
234
+ expected = {
235
+ related: {
236
+ data: [{
237
+ type: 'spam--unrelated-links',
238
+ id: '456'
239
+ }]
240
+ }
241
+ }
242
+ assert_equal expected, relationships
243
+ end
244
+
227
245
  def test_multiple_references_to_same_resource
228
246
  serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_comment, @second_comment])
229
247
  adapter = ActiveModelSerializers::Adapter::JsonApi.new(
@@ -283,8 +301,8 @@ module ActiveModelSerializers
283
301
  end
284
302
 
285
303
  class NoDuplicatesTest < ActiveSupport::TestCase
286
- Post = Class.new(::Model)
287
- Author = Class.new(::Model)
304
+ class Post < ::Model; end
305
+ class Author < ::Model; end
288
306
 
289
307
  class PostSerializer < ActiveModel::Serializer
290
308
  type 'posts'
@@ -303,8 +321,8 @@ module ActiveModelSerializers
303
321
  @author.posts << @post1
304
322
  @author.posts << @post2
305
323
 
306
- @nestedpost1 = ::NestedPost.new(id: 1, nested_posts: [])
307
- @nestedpost2 = ::NestedPost.new(id: 2, nested_posts: [])
324
+ @nestedpost1 = NestedPost.new(id: 1, nested_posts: [])
325
+ @nestedpost2 = NestedPost.new(id: 2, nested_posts: [])
308
326
  @nestedpost1.nested_posts << @nestedpost1
309
327
  @nestedpost1.nested_posts << @nestedpost2
310
328
  @nestedpost2.nested_posts << @nestedpost1
@@ -4,7 +4,7 @@ module ActiveModelSerializers
4
4
  module Adapter
5
5
  class JsonApi
6
6
  class LinksTest < ActiveSupport::TestCase
7
- LinkAuthor = Class.new(::Model)
7
+ class LinkAuthor < ::Model; end
8
8
  class LinkAuthorSerializer < ActiveModel::Serializer
9
9
  link :self do
10
10
  href "http://example.com/link_author/#{object.id}"
@@ -76,7 +76,7 @@ module ActiveModelSerializers
76
76
  }
77
77
  end
78
78
 
79
- def expected_response_without_pagination_links
79
+ def expected_response_when_unpaginatable
80
80
  data
81
81
  end
82
82
 
@@ -87,6 +87,12 @@ module ActiveModelSerializers
87
87
  end
88
88
  end
89
89
 
90
+ def expected_response_without_pagination_links
91
+ {}.tap do |hash|
92
+ hash[:data] = data.values.flatten[2..3]
93
+ end
94
+ end
95
+
90
96
  def expected_response_with_pagination_links_and_additional_params
91
97
  new_links = links[:links].each_with_object({}) { |(key, value), hash| hash[key] = "#{value}&test=test" }
92
98
  {}.tap do |hash|
@@ -159,7 +165,7 @@ module ActiveModelSerializers
159
165
  def test_not_showing_pagination_links
160
166
  adapter = load_adapter(@array, mock_request)
161
167
 
162
- assert_equal expected_response_without_pagination_links, adapter.serializable_hash
168
+ assert_equal expected_response_when_unpaginatable, adapter.serializable_hash
163
169
  end
164
170
 
165
171
  def test_raises_descriptive_error_when_serialization_context_unset
@@ -172,6 +178,15 @@ module ActiveModelSerializers
172
178
  assert_equal exception_class, exception.class
173
179
  assert_match(/CollectionSerializer#paginated\?/, exception.message)
174
180
  end
181
+
182
+ def test_pagination_links_not_present_when_disabled
183
+ ActiveModel::Serializer.config.jsonapi_pagination_links_enabled = false
184
+ adapter = load_adapter(using_kaminari, mock_request)
185
+
186
+ assert_equal expected_response_without_pagination_links, adapter.serializable_hash
187
+ ensure
188
+ ActiveModel::Serializer.config.jsonapi_pagination_links_enabled = true
189
+ end
175
190
  end
176
191
  end
177
192
  end
@@ -4,13 +4,6 @@ module ActiveModelSerializers
4
4
  module Adapter
5
5
  class JsonApi
6
6
  class RelationshipTest < ActiveSupport::TestCase
7
- setup do
8
- @blog = Blog.new(id: 1)
9
- @author = Author.new(id: 1, name: 'Steve K.', blog: @blog)
10
- @serializer = BlogSerializer.new(@blog)
11
- ActionController::Base.cache_store.clear
12
- end
13
-
14
7
  def test_relationship_with_data
15
8
  expected = {
16
9
  data: {
@@ -18,26 +11,29 @@ module ActiveModelSerializers
18
11
  type: 'blogs'
19
12
  }
20
13
  }
21
- test_relationship(expected, options: { include_data: true })
14
+
15
+ model_attributes = { blog: Blog.new(id: 1) }
16
+ relationship_name = :blog
17
+ model = new_model(model_attributes)
18
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
19
+ has_one :blog
20
+ end
21
+ assert_equal(expected, actual)
22
22
  end
23
23
 
24
24
  def test_relationship_with_nil_model
25
- @serializer = BlogSerializer.new(nil)
26
25
  expected = { data: nil }
27
- test_relationship(expected, options: { include_data: true })
28
- end
29
26
 
30
- def test_relationship_with_nil_serializer
31
- @serializer = nil
32
- expected = { data: nil }
33
- test_relationship(expected, options: { include_data: true })
27
+ model_attributes = { blog: nil }
28
+ relationship_name = :blog
29
+ model = new_model(model_attributes)
30
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
31
+ has_one :blog
32
+ end
33
+ assert_equal(expected, actual)
34
34
  end
35
35
 
36
36
  def test_relationship_with_data_array
37
- posts = [Post.new(id: 1), Post.new(id: 2)]
38
- @serializer = ActiveModel::Serializer::CollectionSerializer.new(posts)
39
- @author.posts = posts
40
- @author.blog = nil
41
37
  expected = {
42
38
  data: [
43
39
  {
@@ -50,110 +46,350 @@ module ActiveModelSerializers
50
46
  }
51
47
  ]
52
48
  }
53
- test_relationship(expected, options: { include_data: true })
49
+
50
+ model_attributes = { posts: [Post.new(id: 1), Post.new(id: 2)] }
51
+ relationship_name = :posts
52
+ model = new_model(model_attributes)
53
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
54
+ has_many :posts
55
+ end
56
+ assert_equal(expected, actual)
54
57
  end
55
58
 
56
59
  def test_relationship_data_not_included
57
- test_relationship({}, options: { include_data: false })
58
- end
60
+ expected = { meta: {} }
59
61
 
60
- def test_relationship_simple_link
61
- links = { self: 'a link' }
62
- test_relationship({ links: { self: 'a link' } }, links: links)
62
+ model_attributes = { blog: :does_not_matter }
63
+ relationship_name = :blog
64
+ model = new_model(model_attributes)
65
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
66
+ has_one :blog do
67
+ include_data false
68
+ end
69
+ end
70
+ assert_equal(expected, actual)
63
71
  end
64
72
 
65
73
  def test_relationship_many_links
66
- links = {
67
- self: 'a link',
68
- related: 'another link'
69
- }
70
74
  expected = {
71
75
  links: {
72
76
  self: 'a link',
73
77
  related: 'another link'
74
78
  }
75
79
  }
76
- test_relationship(expected, links: links)
77
- end
78
80
 
79
- def test_relationship_block_link
80
- links = { self: proc { object.id.to_s } }
81
- expected = { links: { self: @blog.id.to_s } }
82
- test_relationship(expected, links: links)
81
+ model_attributes = { blog: :does_not_matter }
82
+ relationship_name = :blog
83
+ model = new_model(model_attributes)
84
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
85
+ has_one :blog do
86
+ include_data false
87
+ link :self, 'a link'
88
+ link :related, 'another link'
89
+ end
90
+ end
91
+ assert_equal(expected, actual)
83
92
  end
84
93
 
85
94
  def test_relationship_block_link_with_meta
86
- links = {
87
- self: proc do
88
- href object.id.to_s
89
- meta(id: object.id)
90
- end
91
- }
92
95
  expected = {
93
96
  links: {
94
97
  self: {
95
- href: @blog.id.to_s,
96
- meta: { id: @blog.id }
98
+ href: '1',
99
+ meta: { id: 1 }
97
100
  }
98
101
  }
99
102
  }
100
- test_relationship(expected, links: links)
103
+
104
+ model_attributes = { blog: Blog.new(id: 1) }
105
+ relationship_name = :blog
106
+ model = new_model(model_attributes)
107
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
108
+ has_one :blog do
109
+ include_data false
110
+ link :self do
111
+ href object.blog.id.to_s
112
+ meta(id: object.blog.id)
113
+ end
114
+ end
115
+ end
116
+ assert_equal(expected, actual)
101
117
  end
102
118
 
103
119
  def test_relationship_simple_meta
104
- meta = { id: '1' }
105
- expected = { meta: meta }
106
- test_relationship(expected, meta: meta)
120
+ expected = { meta: { id: '1' } }
121
+
122
+ model_attributes = { blog: Blog.new(id: 1) }
123
+ relationship_name = :blog
124
+ model = new_model(model_attributes)
125
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
126
+ has_one :blog do
127
+ include_data false
128
+ meta(id: object.blog.id.to_s)
129
+ end
130
+ end
131
+ assert_equal(expected, actual)
107
132
  end
108
133
 
109
134
  def test_relationship_block_meta
110
- meta = proc do
111
- { id: object.id }
112
- end
113
135
  expected = {
114
136
  meta: {
115
- id: @blog.id
137
+ id: 1
116
138
  }
117
139
  }
118
- test_relationship(expected, meta: meta)
140
+
141
+ model_attributes = { blog: Blog.new(id: 1) }
142
+ relationship_name = :blog
143
+ model = new_model(model_attributes)
144
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
145
+ has_one :blog do
146
+ include_data false
147
+ meta(id: object.blog.id)
148
+ end
149
+ end
150
+ assert_equal(expected, actual)
119
151
  end
120
152
 
121
- def test_relationship_with_everything
122
- links = {
123
- self: 'a link',
124
- related: proc do
125
- href object.id.to_s
126
- meta object.id
153
+ def test_relationship_simple_link
154
+ expected = {
155
+ data: {
156
+ id: '1337',
157
+ type: 'bios'
158
+ },
159
+ links: {
160
+ self: '//example.com/link_author/relationships/bio'
161
+ }
162
+ }
163
+
164
+ model_attributes = { bio: Bio.new(id: 1337) }
165
+ relationship_name = :bio
166
+ model = new_model(model_attributes)
167
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
168
+ has_one :bio do
169
+ link :self, '//example.com/link_author/relationships/bio'
127
170
  end
171
+ end
172
+ assert_equal(expected, actual)
173
+ end
128
174
 
175
+ def test_relationship_block_link
176
+ expected = {
177
+ data: { id: '1337', type: 'profiles' },
178
+ links: { related: '//example.com/profiles/1337' }
129
179
  }
130
- meta = proc do
131
- { id: object.id }
180
+
181
+ model_attributes = { profile: Profile.new(id: 1337) }
182
+ relationship_name = :profile
183
+ model = new_model(model_attributes)
184
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
185
+ has_one :profile do
186
+ id = object.profile.id
187
+ link :related do
188
+ "//example.com/profiles/#{id}" if id != 123
189
+ end
190
+ end
132
191
  end
192
+ assert_equal(expected, actual)
193
+ end
194
+
195
+ def test_relationship_with_everything
133
196
  expected = {
134
- data: {
135
- id: '1',
136
- type: 'blogs'
137
- },
197
+ data: [{ id: '1337', type: 'likes' }],
138
198
  links: {
139
- self: 'a link',
140
199
  related: {
141
- href: '1', meta: 1
200
+ href: '//example.com/likes/1337',
201
+ meta: { ids: '1337' }
142
202
  }
143
203
  },
144
- meta: {
145
- id: @blog.id
204
+ meta: { liked: true }
205
+ }
206
+
207
+ model_attributes = { likes: [Like.new(id: 1337)] }
208
+ relationship_name = :likes
209
+ model = new_model(model_attributes)
210
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
211
+ has_many :likes do
212
+ link :related do
213
+ ids = object.likes.map(&:id).join(',')
214
+ href "//example.com/likes/#{ids}"
215
+ meta ids: ids
216
+ end
217
+ meta liked: object.likes.any?
218
+ end
219
+ end
220
+ assert_equal(expected, actual)
221
+ end
222
+
223
+ def test_relationship_nil_link
224
+ expected = {
225
+ data: { id: '123', type: 'profiles' }
226
+ }
227
+
228
+ model_attributes = { profile: Profile.new(id: 123) }
229
+ relationship_name = :profile
230
+ model = new_model(model_attributes)
231
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
232
+ has_one :profile do
233
+ id = object.profile.id
234
+ link :related do
235
+ "//example.com/profiles/#{id}" if id != 123
236
+ end
237
+ end
238
+ end
239
+ assert_equal(expected, actual)
240
+ end
241
+
242
+ def test_relationship_block_link_href
243
+ expected = {
244
+ data: [{ id: '1337', type: 'locations' }],
245
+ links: {
246
+ related: { href: '//example.com/locations/1337' }
247
+ }
248
+ }
249
+
250
+ model_attributes = { locations: [Location.new(id: 1337)] }
251
+ relationship_name = :locations
252
+ model = new_model(model_attributes)
253
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
254
+ has_many :locations do
255
+ link :related do
256
+ ids = object.locations.map(&:id).join(',')
257
+ href "//example.com/locations/#{ids}"
258
+ end
259
+ end
260
+ end
261
+ assert_equal(expected, actual)
262
+ end
263
+
264
+ def test_relationship_block_link_href_and_meta
265
+ expected = {
266
+ data: [{ id: '1337', type: 'posts' }],
267
+ links: {
268
+ related: {
269
+ href: '//example.com/posts/1337',
270
+ meta: { ids: '1337' }
271
+ }
272
+ }
273
+ }
274
+
275
+ model_attributes = { posts: [Post.new(id: 1337, comments: [], author: nil)] }
276
+ relationship_name = :posts
277
+ model = new_model(model_attributes)
278
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
279
+ has_many :posts do
280
+ link :related do
281
+ ids = object.posts.map(&:id).join(',')
282
+ href "//example.com/posts/#{ids}"
283
+ meta ids: ids
284
+ end
285
+ end
286
+ end
287
+ assert_equal(expected, actual)
288
+ end
289
+
290
+ def test_relationship_block_link_meta
291
+ expected = {
292
+ data: [{ id: '1337', type: 'comments' }],
293
+ links: {
294
+ self: {
295
+ meta: { ids: [1] }
296
+ }
146
297
  }
147
298
  }
148
- test_relationship(expected, meta: meta, options: { include_data: true }, links: links)
299
+
300
+ model_attributes = { comments: [Comment.new(id: 1337)] }
301
+ relationship_name = :comments
302
+ model = new_model(model_attributes)
303
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
304
+ has_many :comments do
305
+ link :self do
306
+ meta ids: [1]
307
+ end
308
+ end
309
+ end
310
+ assert_equal(expected, actual)
311
+ end
312
+
313
+ def test_relationship_meta
314
+ expected = {
315
+ data: [{ id: 'from-serializer-method', type: 'roles' }],
316
+ meta: { count: 1 }
317
+ }
318
+
319
+ model_attributes = { roles: [Role.new(id: 'from-record')] }
320
+ relationship_name = :roles
321
+ model = new_model(model_attributes)
322
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
323
+ has_many :roles do |serializer|
324
+ meta count: object.roles.count
325
+ serializer.cached_roles
326
+ end
327
+ def cached_roles
328
+ [
329
+ Role.new(id: 'from-serializer-method')
330
+ ]
331
+ end
332
+ end
333
+ assert_equal(expected, actual)
334
+ end
335
+
336
+ def test_relationship_not_including_data
337
+ expected = {
338
+ links: { self: '//example.com/link_author/relationships/blog' }
339
+ }
340
+
341
+ model_attributes = { blog: Object }
342
+ relationship_name = :blog
343
+ model = new_model(model_attributes)
344
+ model.define_singleton_method(:read_attribute_for_serialization) do |attr|
345
+ fail 'should not be called' if attr == :blog
346
+ super(attr)
347
+ end
348
+ assert_nothing_raised do
349
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
350
+ has_one :blog do
351
+ link :self, '//example.com/link_author/relationships/blog'
352
+ include_data false
353
+ end
354
+ end
355
+ assert_equal(expected, actual)
356
+ end
357
+ end
358
+
359
+ def test_relationship_including_data_explicit
360
+ expected = {
361
+ data: { id: '1337', type: 'authors' },
362
+ meta: { name: 'Dan Brown' }
363
+ }
364
+
365
+ model_attributes = { reviewer: Author.new(id: 1337) }
366
+ relationship_name = :reviewer
367
+ model = new_model(model_attributes)
368
+ actual = build_serializer_and_serialize_relationship(model, relationship_name) do
369
+ belongs_to :reviewer do
370
+ meta name: 'Dan Brown'
371
+ include_data true
372
+ end
373
+ end
374
+ assert_equal(expected, actual)
149
375
  end
150
376
 
151
377
  private
152
378
 
153
- def test_relationship(expected, params = {})
154
- parent_serializer = AuthorSerializer.new(@author)
155
- relationship = Relationship.new(parent_serializer, @serializer, nil, params)
156
- assert_equal(expected, relationship.as_json)
379
+ def build_serializer_and_serialize_relationship(model, relationship_name, &block)
380
+ serializer_class = Class.new(ActiveModel::Serializer, &block)
381
+ hash = serializable(model, serializer: serializer_class, adapter: :json_api).serializable_hash
382
+ hash[:data][:relationships][relationship_name]
383
+ end
384
+
385
+ def new_model(model_attributes)
386
+ Class.new(ActiveModelSerializers::Model) do
387
+ attr_accessor(*model_attributes.keys)
388
+
389
+ def self.name
390
+ 'TestModel'
391
+ end
392
+ end.new(model_attributes)
157
393
  end
158
394
  end
159
395
  end