active_model_serializers 0.8.3 → 0.10.8

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 (235) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +29 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. data/.gitignore +17 -0
  5. data/.rubocop.yml +105 -0
  6. data/.simplecov +110 -0
  7. data/.travis.yml +50 -24
  8. data/CHANGELOG.md +650 -6
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +69 -1
  12. data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
  13. data/README.md +195 -545
  14. data/Rakefile +64 -8
  15. data/active_model_serializers.gemspec +62 -23
  16. data/appveyor.yml +28 -0
  17. data/bin/bench +171 -0
  18. data/bin/bench_regression +316 -0
  19. data/bin/rubocop +38 -0
  20. data/bin/serve_benchmark +39 -0
  21. data/docs/README.md +41 -0
  22. data/docs/STYLE.md +58 -0
  23. data/docs/general/adapters.md +269 -0
  24. data/docs/general/caching.md +58 -0
  25. data/docs/general/configuration_options.md +185 -0
  26. data/docs/general/deserialization.md +100 -0
  27. data/docs/general/fields.md +31 -0
  28. data/docs/general/getting_started.md +133 -0
  29. data/docs/general/instrumentation.md +40 -0
  30. data/docs/general/key_transforms.md +40 -0
  31. data/docs/general/logging.md +21 -0
  32. data/docs/general/rendering.md +293 -0
  33. data/docs/general/serializers.md +495 -0
  34. data/docs/how-open-source-maintained.jpg +0 -0
  35. data/docs/howto/add_pagination_links.md +138 -0
  36. data/docs/howto/add_relationship_links.md +140 -0
  37. data/docs/howto/add_root_key.md +62 -0
  38. data/docs/howto/grape_integration.md +42 -0
  39. data/docs/howto/outside_controller_use.md +66 -0
  40. data/docs/howto/passing_arbitrary_options.md +27 -0
  41. data/docs/howto/serialize_poro.md +73 -0
  42. data/docs/howto/test.md +154 -0
  43. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  44. data/docs/integrations/ember-and-json-api.md +147 -0
  45. data/docs/integrations/grape.md +19 -0
  46. data/docs/jsonapi/errors.md +56 -0
  47. data/docs/jsonapi/schema/schema.json +366 -0
  48. data/docs/jsonapi/schema.md +151 -0
  49. data/docs/rfcs/0000-namespace.md +106 -0
  50. data/docs/rfcs/template.md +15 -0
  51. data/lib/action_controller/serialization.rb +43 -38
  52. data/lib/active_model/serializable_resource.rb +11 -0
  53. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  54. data/lib/active_model/serializer/adapter/base.rb +18 -0
  55. data/lib/active_model/serializer/adapter/json.rb +15 -0
  56. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  57. data/lib/active_model/serializer/adapter/null.rb +15 -0
  58. data/lib/active_model/serializer/adapter.rb +24 -0
  59. data/lib/active_model/serializer/array_serializer.rb +12 -0
  60. data/lib/active_model/serializer/association.rb +71 -0
  61. data/lib/active_model/serializer/attribute.rb +25 -0
  62. data/lib/active_model/serializer/belongs_to_reflection.rb +11 -0
  63. data/lib/active_model/serializer/collection_serializer.rb +88 -0
  64. data/lib/active_model/serializer/concerns/caching.rb +300 -0
  65. data/lib/active_model/serializer/error_serializer.rb +14 -0
  66. data/lib/active_model/serializer/errors_serializer.rb +32 -0
  67. data/lib/active_model/serializer/field.rb +90 -0
  68. data/lib/active_model/serializer/fieldset.rb +31 -0
  69. data/lib/active_model/serializer/has_many_reflection.rb +10 -0
  70. data/lib/active_model/serializer/has_one_reflection.rb +7 -0
  71. data/lib/active_model/serializer/lazy_association.rb +96 -0
  72. data/lib/active_model/serializer/link.rb +21 -0
  73. data/lib/active_model/serializer/lint.rb +150 -0
  74. data/lib/active_model/serializer/null.rb +17 -0
  75. data/lib/active_model/serializer/reflection.rb +210 -0
  76. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  77. data/lib/active_model/serializer.rb +343 -442
  78. data/lib/active_model_serializers/adapter/attributes.rb +13 -0
  79. data/lib/active_model_serializers/adapter/base.rb +83 -0
  80. data/lib/active_model_serializers/adapter/json.rb +21 -0
  81. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  82. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  83. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  84. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  85. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  86. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +88 -0
  87. data/lib/active_model_serializers/adapter/json_api/relationship.rb +104 -0
  88. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +66 -0
  89. data/lib/active_model_serializers/adapter/json_api.rb +533 -0
  90. data/lib/active_model_serializers/adapter/null.rb +9 -0
  91. data/lib/active_model_serializers/adapter.rb +98 -0
  92. data/lib/active_model_serializers/callbacks.rb +55 -0
  93. data/lib/active_model_serializers/deprecate.rb +54 -0
  94. data/lib/active_model_serializers/deserialization.rb +15 -0
  95. data/lib/active_model_serializers/json_pointer.rb +14 -0
  96. data/lib/active_model_serializers/logging.rb +122 -0
  97. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  98. data/lib/active_model_serializers/model.rb +130 -0
  99. data/lib/active_model_serializers/railtie.rb +50 -0
  100. data/lib/active_model_serializers/register_jsonapi_renderer.rb +78 -0
  101. data/lib/active_model_serializers/serializable_resource.rb +82 -0
  102. data/lib/active_model_serializers/serialization_context.rb +39 -0
  103. data/lib/active_model_serializers/test/schema.rb +138 -0
  104. data/lib/active_model_serializers/test/serializer.rb +125 -0
  105. data/lib/active_model_serializers/test.rb +7 -0
  106. data/lib/active_model_serializers.rb +47 -81
  107. data/lib/generators/rails/USAGE +6 -0
  108. data/lib/generators/rails/resource_override.rb +10 -0
  109. data/lib/generators/rails/serializer_generator.rb +36 -0
  110. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  111. data/lib/grape/active_model_serializers.rb +16 -0
  112. data/lib/grape/formatters/active_model_serializers.rb +32 -0
  113. data/lib/grape/helpers/active_model_serializers.rb +17 -0
  114. data/lib/tasks/rubocop.rake +53 -0
  115. data/test/action_controller/adapter_selector_test.rb +62 -0
  116. data/test/action_controller/explicit_serializer_test.rb +135 -0
  117. data/test/action_controller/json/include_test.rb +246 -0
  118. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  119. data/test/action_controller/json_api/errors_test.rb +40 -0
  120. data/test/action_controller/json_api/fields_test.rb +66 -0
  121. data/test/action_controller/json_api/linked_test.rb +202 -0
  122. data/test/action_controller/json_api/pagination_test.rb +124 -0
  123. data/test/action_controller/json_api/transform_test.rb +189 -0
  124. data/test/action_controller/lookup_proc_test.rb +49 -0
  125. data/test/action_controller/namespace_lookup_test.rb +232 -0
  126. data/test/action_controller/serialization_scope_name_test.rb +235 -0
  127. data/test/action_controller/serialization_test.rb +478 -0
  128. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  129. data/test/active_model_serializers/json_pointer_test.rb +22 -0
  130. data/test/active_model_serializers/logging_test.rb +77 -0
  131. data/test/active_model_serializers/model_test.rb +142 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +68 -0
  133. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
  134. data/test/active_model_serializers/serialization_context_test_isolated.rb +71 -0
  135. data/test/active_model_serializers/test/schema_test.rb +131 -0
  136. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  137. data/test/active_record_test.rb +9 -0
  138. data/test/adapter/attributes_test.rb +40 -0
  139. data/test/adapter/deprecation_test.rb +100 -0
  140. data/test/adapter/json/belongs_to_test.rb +45 -0
  141. data/test/adapter/json/collection_test.rb +104 -0
  142. data/test/adapter/json/has_many_test.rb +53 -0
  143. data/test/adapter/json/transform_test.rb +93 -0
  144. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  145. data/test/adapter/json_api/collection_test.rb +96 -0
  146. data/test/adapter/json_api/errors_test.rb +76 -0
  147. data/test/adapter/json_api/fields_test.rb +96 -0
  148. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  149. data/test/adapter/json_api/has_many_test.rb +173 -0
  150. data/test/adapter/json_api/has_one_test.rb +80 -0
  151. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +213 -0
  152. data/test/adapter/json_api/json_api_test.rb +33 -0
  153. data/test/adapter/json_api/linked_test.rb +413 -0
  154. data/test/adapter/json_api/links_test.rb +110 -0
  155. data/test/adapter/json_api/pagination_links_test.rb +206 -0
  156. data/test/adapter/json_api/parse_test.rb +137 -0
  157. data/test/adapter/json_api/relationship_test.rb +397 -0
  158. data/test/adapter/json_api/resource_meta_test.rb +100 -0
  159. data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
  160. data/test/adapter/json_api/transform_test.rb +512 -0
  161. data/test/adapter/json_api/type_test.rb +193 -0
  162. data/test/adapter/json_test.rb +46 -0
  163. data/test/adapter/null_test.rb +22 -0
  164. data/test/adapter/polymorphic_test.rb +218 -0
  165. data/test/adapter_test.rb +67 -0
  166. data/test/array_serializer_test.rb +20 -73
  167. data/test/benchmark/app.rb +65 -0
  168. data/test/benchmark/benchmarking_support.rb +67 -0
  169. data/test/benchmark/bm_active_record.rb +81 -0
  170. data/test/benchmark/bm_adapter.rb +38 -0
  171. data/test/benchmark/bm_caching.rb +119 -0
  172. data/test/benchmark/bm_lookup_chain.rb +83 -0
  173. data/test/benchmark/bm_transform.rb +45 -0
  174. data/test/benchmark/config.ru +3 -0
  175. data/test/benchmark/controllers.rb +83 -0
  176. data/test/benchmark/fixtures.rb +219 -0
  177. data/test/cache_test.rb +651 -0
  178. data/test/collection_serializer_test.rb +127 -0
  179. data/test/fixtures/active_record.rb +113 -0
  180. data/test/fixtures/poro.rb +225 -0
  181. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  182. data/test/generators/serializer_generator_test.rb +75 -0
  183. data/test/grape_test.rb +196 -0
  184. data/test/lint_test.rb +49 -0
  185. data/test/logger_test.rb +20 -0
  186. data/test/poro_test.rb +9 -0
  187. data/test/serializable_resource_test.rb +79 -0
  188. data/test/serializers/association_macros_test.rb +37 -0
  189. data/test/serializers/associations_test.rb +518 -0
  190. data/test/serializers/attribute_test.rb +153 -0
  191. data/test/serializers/attributes_test.rb +52 -0
  192. data/test/serializers/caching_configuration_test_isolated.rb +170 -0
  193. data/test/serializers/configuration_test.rb +32 -0
  194. data/test/serializers/fieldset_test.rb +14 -0
  195. data/test/serializers/meta_test.rb +202 -0
  196. data/test/serializers/options_test.rb +32 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  198. data/test/serializers/reflection_test.rb +479 -0
  199. data/test/serializers/root_test.rb +21 -0
  200. data/test/serializers/serialization_test.rb +55 -0
  201. data/test/serializers/serializer_for_test.rb +136 -0
  202. data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
  203. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  204. data/test/support/isolated_unit.rb +84 -0
  205. data/test/support/rails5_shims.rb +53 -0
  206. data/test/support/rails_app.rb +38 -0
  207. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  208. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  209. data/test/support/schemas/custom/show.json +7 -0
  210. data/test/support/schemas/hyper_schema.json +93 -0
  211. data/test/support/schemas/render_using_json_api.json +43 -0
  212. data/test/support/schemas/simple_json_pointers.json +10 -0
  213. data/test/support/serialization_testing.rb +79 -0
  214. data/test/test_helper.rb +59 -21
  215. metadata +529 -43
  216. data/DESIGN.textile +0 -586
  217. data/Gemfile.edge +0 -9
  218. data/bench/perf.rb +0 -43
  219. data/cruft.md +0 -19
  220. data/lib/active_model/array_serializer.rb +0 -104
  221. data/lib/active_model/serializer/associations.rb +0 -233
  222. data/lib/active_record/serializer_override.rb +0 -16
  223. data/lib/generators/resource_override.rb +0 -13
  224. data/lib/generators/serializer/USAGE +0 -9
  225. data/lib/generators/serializer/serializer_generator.rb +0 -42
  226. data/lib/generators/serializer/templates/serializer.rb +0 -19
  227. data/test/association_test.rb +0 -592
  228. data/test/caching_test.rb +0 -96
  229. data/test/generators_test.rb +0 -85
  230. data/test/no_serialization_scope_test.rb +0 -34
  231. data/test/serialization_scope_name_test.rb +0 -67
  232. data/test/serialization_test.rb +0 -392
  233. data/test/serializer_support_test.rb +0 -51
  234. data/test/serializer_test.rb +0 -1465
  235. data/test/test_fakes.rb +0 -217
@@ -0,0 +1,213 @@
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) do
9
+ associations :tags, :posts, :roles
10
+ end
11
+
12
+ class CustomCommentLoader
13
+ def all
14
+ [{ foo: 'bar' }]
15
+ end
16
+ end
17
+ class Tag < ::Model
18
+ attributes :id, :name
19
+ end
20
+
21
+ class TagSerializer < ActiveModel::Serializer
22
+ type 'tags'
23
+ attributes :id, :name
24
+ end
25
+
26
+ class PostWithTagsSerializer < ActiveModel::Serializer
27
+ type 'posts'
28
+ attributes :id
29
+ has_many :tags
30
+ end
31
+
32
+ class IncludeParamAuthorSerializer < ActiveModel::Serializer
33
+ class_attribute :comment_loader
34
+
35
+ has_many :tags, serializer: TagSerializer do
36
+ link :self, '//example.com/link_author/relationships/tags'
37
+ include_data :if_sideloaded
38
+ end
39
+
40
+ has_many :unlinked_tags, serializer: TagSerializer do
41
+ include_data :if_sideloaded
42
+ end
43
+
44
+ has_many :posts, serializer: PostWithTagsSerializer do
45
+ include_data :if_sideloaded
46
+ end
47
+ has_many :locations do
48
+ include_data :if_sideloaded
49
+ end
50
+ has_many :comments do
51
+ include_data :if_sideloaded
52
+ IncludeParamAuthorSerializer.comment_loader.all
53
+ end
54
+ has_many :roles, key: :granted_roles do
55
+ include_data :if_sideloaded
56
+ end
57
+ end
58
+
59
+ def setup
60
+ IncludeParamAuthorSerializer.comment_loader = Class.new(CustomCommentLoader).new
61
+ @tag = Tag.new(id: 1337, name: 'mytag')
62
+ @role = Role.new(id: 1337, name: 'myrole')
63
+ @author = IncludeParamAuthor.new(
64
+ id: 1337,
65
+ tags: [@tag],
66
+ roles: [@role]
67
+ )
68
+ end
69
+
70
+ def test_relationship_not_loaded_when_not_included
71
+ expected = {
72
+ links: {
73
+ self: '//example.com/link_author/relationships/tags'
74
+ }
75
+ }
76
+
77
+ @author.define_singleton_method(:read_attribute_for_serialization) do |attr|
78
+ fail 'should not be called' if attr == :tags
79
+ super(attr)
80
+ end
81
+
82
+ assert_relationship(:tags, expected)
83
+ end
84
+
85
+ def test_relationship_included
86
+ expected = {
87
+ data: [
88
+ {
89
+ id: '1337',
90
+ type: 'tags'
91
+ }
92
+ ],
93
+ links: {
94
+ self: '//example.com/link_author/relationships/tags'
95
+ }
96
+ }
97
+
98
+ assert_relationship(:tags, expected, include: :tags)
99
+ end
100
+
101
+ def test_sideloads_included
102
+ expected = [
103
+ {
104
+ id: '1337',
105
+ type: 'tags',
106
+ attributes: { name: 'mytag' }
107
+ }
108
+ ]
109
+ hash = result(include: :tags)
110
+ assert_equal(expected, hash[:included])
111
+ end
112
+
113
+ def test_sideloads_included_when_using_key
114
+ expected = [
115
+ {
116
+ id: '1337',
117
+ type: 'roles',
118
+ attributes: {
119
+ name: 'myrole',
120
+ description: nil,
121
+ slug: 'myrole-1337'
122
+ },
123
+ relationships: {
124
+ author: { data: nil }
125
+ }
126
+ }
127
+ ]
128
+
129
+ hash = result(include: :granted_roles)
130
+ assert_equal(expected, hash[:included])
131
+ end
132
+
133
+ def test_sideloads_not_included_when_using_name_when_key_defined
134
+ hash = result(include: :roles)
135
+ assert_nil(hash[:included])
136
+ end
137
+
138
+ def test_nested_relationship
139
+ expected = {
140
+ data: [
141
+ {
142
+ id: '1337',
143
+ type: 'tags'
144
+ }
145
+ ],
146
+ links: {
147
+ self: '//example.com/link_author/relationships/tags'
148
+ }
149
+ }
150
+
151
+ expected_no_data = {
152
+ links: {
153
+ self: '//example.com/link_author/relationships/tags'
154
+ }
155
+ }
156
+
157
+ assert_relationship(:tags, expected, include: [:tags, { posts: :tags }])
158
+
159
+ @author.define_singleton_method(:read_attribute_for_serialization) do |attr|
160
+ fail 'should not be called' if attr == :tags
161
+ super(attr)
162
+ end
163
+
164
+ assert_relationship(:tags, expected_no_data, include: { posts: :tags })
165
+ end
166
+
167
+ def test_include_params_with_no_block
168
+ @author.define_singleton_method(:read_attribute_for_serialization) do |attr|
169
+ fail 'should not be called' if attr == :locations
170
+ super(attr)
171
+ end
172
+
173
+ expected = { meta: {} }
174
+
175
+ assert_relationship(:locations, expected)
176
+ end
177
+
178
+ def test_block_relationship
179
+ expected = {
180
+ data: [
181
+ { 'foo' => 'bar' }
182
+ ]
183
+ }
184
+
185
+ assert_relationship(:comments, expected, include: [:comments])
186
+ end
187
+
188
+ def test_node_not_included_when_no_link
189
+ expected = { meta: {} }
190
+ assert_relationship(:unlinked_tags, expected, key_transform: :unaltered)
191
+ end
192
+
193
+ private
194
+
195
+ def assert_relationship(relationship_name, expected, opts = {})
196
+ actual = relationship_data(relationship_name, opts)
197
+ assert_equal(expected, actual)
198
+ end
199
+
200
+ def result(opts)
201
+ opts = { adapter: :json_api }.merge(opts)
202
+ serializable(@author, opts).serializable_hash
203
+ end
204
+
205
+ def relationship_data(relationship_name, opts = {})
206
+ hash = result(opts)
207
+ hash[:data][:relationships][relationship_name]
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApiTest < ActiveSupport::TestCase
6
+ def setup
7
+ ActionController::Base.cache_store.clear
8
+ @author = Author.new(id: 1, name: 'Steve K.')
9
+ @post = Post.new(id: 1, title: 'New Post', body: 'Body')
10
+ @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
11
+ @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
12
+ @post.comments = [@first_comment, @second_comment]
13
+ @first_comment.post = @post
14
+ @second_comment.post = @post
15
+ @post.author = @author
16
+ @blog = Blog.new(id: 1, name: 'My Blog!!')
17
+ @post.blog = @blog
18
+ end
19
+
20
+ def test_custom_keys
21
+ serializer = PostWithCustomKeysSerializer.new(@post)
22
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
23
+
24
+ assert_equal({
25
+ reviews: { data: [{ type: 'comments', id: '1' },
26
+ { type: 'comments', id: '2' }] },
27
+ writer: { data: { type: 'authors', id: '1' } },
28
+ site: { data: { type: 'blogs', id: '1' } }
29
+ }, adapter.serializable_hash[:data][:relationships])
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,413 @@
1
+ require 'test_helper'
2
+
3
+ class NestedPost < ::Model; associations :nested_posts end
4
+ class NestedPostSerializer < ActiveModel::Serializer
5
+ has_many :nested_posts
6
+ end
7
+ module ActiveModelSerializers
8
+ module Adapter
9
+ class JsonApi
10
+ class LinkedTest < ActiveSupport::TestCase
11
+ def setup
12
+ @author1 = Author.new(id: 1, name: 'Steve K.')
13
+ @author2 = Author.new(id: 2, name: 'Tenderlove')
14
+ @bio1 = Bio.new(id: 1, content: 'AMS Contributor')
15
+ @bio2 = Bio.new(id: 2, content: 'Rails Contributor')
16
+ @first_post = Post.new(id: 10, title: 'Hello!!', body: 'Hello, world!!')
17
+ @second_post = Post.new(id: 20, title: 'New Post', body: 'Body')
18
+ @third_post = Post.new(id: 30, title: 'Yet Another Post', body: 'Body')
19
+ @blog = Blog.new(name: 'AMS Blog')
20
+ @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
21
+ @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
22
+ @first_post.blog = @blog
23
+ @second_post.blog = @blog
24
+ @third_post.blog = nil
25
+ @first_post.comments = [@first_comment, @second_comment]
26
+ @second_post.comments = []
27
+ @third_post.comments = []
28
+ @first_post.author = @author1
29
+ @second_post.author = @author2
30
+ @third_post.author = @author1
31
+ @first_comment.post = @first_post
32
+ @first_comment.author = nil
33
+ @second_comment.post = @first_post
34
+ @second_comment.author = nil
35
+ @author1.posts = [@first_post, @third_post]
36
+ @author1.bio = @bio1
37
+ @author1.roles = []
38
+ @author2.posts = [@second_post]
39
+ @author2.bio = @bio2
40
+ @author2.roles = []
41
+ @bio1.author = @author1
42
+ @bio2.author = @author2
43
+ end
44
+
45
+ def test_include_multiple_posts_and_linked_array
46
+ serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post])
47
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(
48
+ serializer,
49
+ include: [:comments, author: [:bio]]
50
+ )
51
+ alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new(
52
+ serializer,
53
+ include: [:comments, author: [:bio]]
54
+ )
55
+
56
+ expected = {
57
+ data: [
58
+ {
59
+ id: '10',
60
+ type: 'posts',
61
+ attributes: {
62
+ title: 'Hello!!',
63
+ body: 'Hello, world!!'
64
+ },
65
+ relationships: {
66
+ comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
67
+ blog: { data: { type: 'blogs', id: '999' } },
68
+ author: { data: { type: 'authors', id: '1' } }
69
+ }
70
+ },
71
+ {
72
+ id: '20',
73
+ type: 'posts',
74
+ attributes: {
75
+ title: 'New Post',
76
+ body: 'Body'
77
+ },
78
+ relationships: {
79
+ comments: { data: [] },
80
+ blog: { data: { type: 'blogs', id: '999' } },
81
+ author: { data: { type: 'authors', id: '2' } }
82
+ }
83
+ }
84
+ ],
85
+ included: [
86
+ {
87
+ id: '1',
88
+ type: 'comments',
89
+ attributes: {
90
+ body: 'ZOMG A COMMENT'
91
+ },
92
+ relationships: {
93
+ post: { data: { type: 'posts', id: '10' } },
94
+ author: { data: nil }
95
+ }
96
+ }, {
97
+ id: '2',
98
+ type: 'comments',
99
+ attributes: {
100
+ body: 'ZOMG ANOTHER COMMENT'
101
+ },
102
+ relationships: {
103
+ post: { data: { type: 'posts', id: '10' } },
104
+ author: { data: nil }
105
+ }
106
+ }, {
107
+ id: '1',
108
+ type: 'authors',
109
+ attributes: {
110
+ name: 'Steve K.'
111
+ },
112
+ relationships: {
113
+ posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] },
114
+ roles: { data: [] },
115
+ bio: { data: { type: 'bios', id: '1' } }
116
+ }
117
+ }, {
118
+ id: '1',
119
+ type: 'bios',
120
+ attributes: {
121
+ content: 'AMS Contributor',
122
+ rating: nil
123
+ },
124
+ relationships: {
125
+ author: { data: { type: 'authors', id: '1' } }
126
+ }
127
+ }, {
128
+ id: '2',
129
+ type: 'authors',
130
+ attributes: {
131
+ name: 'Tenderlove'
132
+ },
133
+ relationships: {
134
+ posts: { data: [{ type: 'posts', id: '20' }] },
135
+ roles: { data: [] },
136
+ bio: { data: { type: 'bios', id: '2' } }
137
+ }
138
+ }, {
139
+ id: '2',
140
+ type: 'bios',
141
+ attributes: {
142
+ rating: nil,
143
+ content: 'Rails Contributor'
144
+ },
145
+ relationships: {
146
+ author: { data: { type: 'authors', id: '2' } }
147
+ }
148
+ }
149
+ ]
150
+ }
151
+ assert_equal expected, adapter.serializable_hash
152
+ assert_equal expected, alt_adapter.serializable_hash
153
+ end
154
+
155
+ def test_include_multiple_posts_and_linked
156
+ serializer = BioSerializer.new @bio1
157
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(
158
+ serializer,
159
+ include: [author: [:posts]]
160
+ )
161
+ alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new(
162
+ serializer,
163
+ include: [author: [:posts]]
164
+ )
165
+
166
+ expected = [
167
+ {
168
+ id: '1',
169
+ type: 'authors',
170
+ attributes: {
171
+ name: 'Steve K.'
172
+ },
173
+ relationships: {
174
+ posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] },
175
+ roles: { data: [] },
176
+ bio: { data: { type: 'bios', id: '1' } }
177
+ }
178
+ }, {
179
+ id: '10',
180
+ type: 'posts',
181
+ attributes: {
182
+ title: 'Hello!!',
183
+ body: 'Hello, world!!'
184
+ },
185
+ relationships: {
186
+ comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
187
+ blog: { data: { type: 'blogs', id: '999' } },
188
+ author: { data: { type: 'authors', id: '1' } }
189
+ }
190
+ }, {
191
+ id: '30',
192
+ type: 'posts',
193
+ attributes: {
194
+ title: 'Yet Another Post',
195
+ body: 'Body'
196
+ },
197
+ relationships: {
198
+ comments: { data: [] },
199
+ blog: { data: { type: 'blogs', id: '999' } },
200
+ author: { data: { type: 'authors', id: '1' } }
201
+ }
202
+ }
203
+ ]
204
+
205
+ assert_equal expected, adapter.serializable_hash[:included]
206
+ assert_equal expected, alt_adapter.serializable_hash[:included]
207
+ end
208
+
209
+ def test_underscore_model_namespace_for_linked_resource_type
210
+ spammy_post = Post.new(id: 123)
211
+ spammy_post.related = [Spam::UnrelatedLink.new(id: 456)]
212
+ serializer = SpammyPostSerializer.new(spammy_post)
213
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
214
+ relationships = adapter.serializable_hash[:data][:relationships]
215
+ expected = {
216
+ related: {
217
+ data: [{
218
+ type: 'spam-unrelated-links',
219
+ id: '456'
220
+ }]
221
+ }
222
+ }
223
+ assert_equal expected, relationships
224
+ end
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
+
245
+ def test_multiple_references_to_same_resource
246
+ serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_comment, @second_comment])
247
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(
248
+ serializer,
249
+ include: [:post]
250
+ )
251
+
252
+ expected = [
253
+ {
254
+ id: '10',
255
+ type: 'posts',
256
+ attributes: {
257
+ title: 'Hello!!',
258
+ body: 'Hello, world!!'
259
+ },
260
+ relationships: {
261
+ comments: {
262
+ data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }]
263
+ },
264
+ blog: {
265
+ data: { type: 'blogs', id: '999' }
266
+ },
267
+ author: {
268
+ data: { type: 'authors', id: '1' }
269
+ }
270
+ }
271
+ }
272
+ ]
273
+
274
+ assert_equal expected, adapter.serializable_hash[:included]
275
+ end
276
+
277
+ def test_nil_link_with_specified_serializer
278
+ @first_post.author = nil
279
+ serializer = PostPreviewSerializer.new(@first_post)
280
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(
281
+ serializer,
282
+ include: [:author]
283
+ )
284
+
285
+ expected = {
286
+ data: {
287
+ id: '10',
288
+ type: 'posts',
289
+ attributes: {
290
+ title: 'Hello!!',
291
+ body: 'Hello, world!!'
292
+ },
293
+ relationships: {
294
+ comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] },
295
+ author: { data: nil }
296
+ }
297
+ }
298
+ }
299
+ assert_equal expected, adapter.serializable_hash
300
+ end
301
+ end
302
+
303
+ class NoDuplicatesTest < ActiveSupport::TestCase
304
+ class Post < ::Model; associations :author end
305
+ class Author < ::Model; associations :posts, :roles, :bio end
306
+
307
+ class PostSerializer < ActiveModel::Serializer
308
+ type 'posts'
309
+ belongs_to :author
310
+ end
311
+
312
+ class AuthorSerializer < ActiveModel::Serializer
313
+ type 'authors'
314
+ has_many :posts
315
+ end
316
+
317
+ def setup
318
+ @author = Author.new(id: 1, posts: [], roles: [], bio: nil)
319
+ @post1 = Post.new(id: 1, author: @author)
320
+ @post2 = Post.new(id: 2, author: @author)
321
+ @author.posts << @post1
322
+ @author.posts << @post2
323
+
324
+ @nestedpost1 = NestedPost.new(id: 1, nested_posts: [])
325
+ @nestedpost2 = NestedPost.new(id: 2, nested_posts: [])
326
+ @nestedpost1.nested_posts << @nestedpost1
327
+ @nestedpost1.nested_posts << @nestedpost2
328
+ @nestedpost2.nested_posts << @nestedpost1
329
+ @nestedpost2.nested_posts << @nestedpost2
330
+ end
331
+
332
+ def test_no_duplicates
333
+ hash = ActiveModelSerializers::SerializableResource.new(@post1, adapter: :json_api,
334
+ include: '*.*')
335
+ .serializable_hash
336
+ expected = [
337
+ {
338
+ type: 'authors', id: '1',
339
+ relationships: {
340
+ posts: {
341
+ data: [
342
+ { type: 'posts', id: '1' },
343
+ { type: 'posts', id: '2' }
344
+ ]
345
+ }
346
+ }
347
+ },
348
+ {
349
+ type: 'posts', id: '2',
350
+ relationships: {
351
+ author: {
352
+ data: { type: 'authors', id: '1' }
353
+ }
354
+ }
355
+ }
356
+ ]
357
+ assert_equal(expected, hash[:included])
358
+ end
359
+
360
+ def test_no_duplicates_collection
361
+ hash = ActiveModelSerializers::SerializableResource.new(
362
+ [@post1, @post2],
363
+ adapter: :json_api,
364
+ include: '*.*'
365
+ ).serializable_hash
366
+ expected = [
367
+ {
368
+ type: 'authors', id: '1',
369
+ relationships: {
370
+ posts: {
371
+ data: [
372
+ { type: 'posts', id: '1' },
373
+ { type: 'posts', id: '2' }
374
+ ]
375
+ }
376
+ }
377
+ }
378
+ ]
379
+ assert_equal(expected, hash[:included])
380
+ end
381
+
382
+ def test_no_duplicates_global
383
+ hash = ActiveModelSerializers::SerializableResource.new(
384
+ @nestedpost1,
385
+ adapter: :json_api,
386
+ include: '*'
387
+ ).serializable_hash
388
+ expected = [
389
+ type: 'nested-posts', id: '2',
390
+ relationships: {
391
+ :"nested-posts" => {
392
+ data: [
393
+ { type: 'nested-posts', id: '1' },
394
+ { type: 'nested-posts', id: '2' }
395
+ ]
396
+ }
397
+ }
398
+ ]
399
+ assert_equal(expected, hash[:included])
400
+ end
401
+
402
+ def test_no_duplicates_collection_global
403
+ hash = ActiveModelSerializers::SerializableResource.new(
404
+ [@nestedpost1, @nestedpost2],
405
+ adapter: :json_api,
406
+ include: '*'
407
+ ).serializable_hash
408
+ assert_nil(hash[:included])
409
+ end
410
+ end
411
+ end
412
+ end
413
+ end