agi_active_model_serializers 0.10.7

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 (220) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +29 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. data/.gitignore +35 -0
  5. data/.rubocop.yml +102 -0
  6. data/.simplecov +110 -0
  7. data/.travis.yml +51 -0
  8. data/CHANGELOG.md +612 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +56 -0
  12. data/MIT-LICENSE +22 -0
  13. data/README.md +307 -0
  14. data/Rakefile +103 -0
  15. data/active_model_serializers.gemspec +63 -0
  16. data/appveyor.yml +24 -0
  17. data/bin/bench +171 -0
  18. data/bin/bench_regression +316 -0
  19. data/bin/serve_benchmark +39 -0
  20. data/docs/README.md +41 -0
  21. data/docs/STYLE.md +58 -0
  22. data/docs/general/adapters.md +247 -0
  23. data/docs/general/caching.md +58 -0
  24. data/docs/general/configuration_options.md +169 -0
  25. data/docs/general/deserialization.md +100 -0
  26. data/docs/general/fields.md +31 -0
  27. data/docs/general/getting_started.md +133 -0
  28. data/docs/general/instrumentation.md +40 -0
  29. data/docs/general/key_transforms.md +40 -0
  30. data/docs/general/logging.md +14 -0
  31. data/docs/general/rendering.md +279 -0
  32. data/docs/general/serializers.md +461 -0
  33. data/docs/how-open-source-maintained.jpg +0 -0
  34. data/docs/howto/add_pagination_links.md +138 -0
  35. data/docs/howto/add_relationship_links.md +137 -0
  36. data/docs/howto/add_root_key.md +55 -0
  37. data/docs/howto/grape_integration.md +42 -0
  38. data/docs/howto/outside_controller_use.md +65 -0
  39. data/docs/howto/passing_arbitrary_options.md +27 -0
  40. data/docs/howto/serialize_poro.md +32 -0
  41. data/docs/howto/test.md +154 -0
  42. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  43. data/docs/integrations/ember-and-json-api.md +144 -0
  44. data/docs/integrations/grape.md +19 -0
  45. data/docs/jsonapi/errors.md +56 -0
  46. data/docs/jsonapi/schema.md +151 -0
  47. data/docs/jsonapi/schema/schema.json +366 -0
  48. data/docs/rfcs/0000-namespace.md +106 -0
  49. data/docs/rfcs/template.md +15 -0
  50. data/lib/action_controller/serialization.rb +66 -0
  51. data/lib/active_model/serializable_resource.rb +11 -0
  52. data/lib/active_model/serializer.rb +231 -0
  53. data/lib/active_model/serializer/adapter.rb +24 -0
  54. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  55. data/lib/active_model/serializer/adapter/base.rb +18 -0
  56. data/lib/active_model/serializer/adapter/json.rb +15 -0
  57. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  58. data/lib/active_model/serializer/adapter/null.rb +15 -0
  59. data/lib/active_model/serializer/array_serializer.rb +12 -0
  60. data/lib/active_model/serializer/association.rb +34 -0
  61. data/lib/active_model/serializer/attribute.rb +25 -0
  62. data/lib/active_model/serializer/belongs_to_reflection.rb +7 -0
  63. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  64. data/lib/active_model/serializer/collection_serializer.rb +87 -0
  65. data/lib/active_model/serializer/concerns/associations.rb +102 -0
  66. data/lib/active_model/serializer/concerns/attributes.rb +82 -0
  67. data/lib/active_model/serializer/concerns/caching.rb +292 -0
  68. data/lib/active_model/serializer/concerns/configuration.rb +59 -0
  69. data/lib/active_model/serializer/concerns/links.rb +35 -0
  70. data/lib/active_model/serializer/concerns/meta.rb +29 -0
  71. data/lib/active_model/serializer/concerns/type.rb +25 -0
  72. data/lib/active_model/serializer/error_serializer.rb +14 -0
  73. data/lib/active_model/serializer/errors_serializer.rb +32 -0
  74. data/lib/active_model/serializer/field.rb +90 -0
  75. data/lib/active_model/serializer/fieldset.rb +31 -0
  76. data/lib/active_model/serializer/has_many_reflection.rb +7 -0
  77. data/lib/active_model/serializer/has_one_reflection.rb +7 -0
  78. data/lib/active_model/serializer/lint.rb +150 -0
  79. data/lib/active_model/serializer/null.rb +17 -0
  80. data/lib/active_model/serializer/reflection.rb +163 -0
  81. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  82. data/lib/active_model/serializer/version.rb +5 -0
  83. data/lib/active_model_serializers.rb +53 -0
  84. data/lib/active_model_serializers/adapter.rb +98 -0
  85. data/lib/active_model_serializers/adapter/attributes.rb +13 -0
  86. data/lib/active_model_serializers/adapter/base.rb +83 -0
  87. data/lib/active_model_serializers/adapter/json.rb +21 -0
  88. data/lib/active_model_serializers/adapter/json_api.rb +517 -0
  89. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  90. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  91. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  92. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  93. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  94. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +69 -0
  95. data/lib/active_model_serializers/adapter/json_api/relationship.rb +63 -0
  96. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +51 -0
  97. data/lib/active_model_serializers/adapter/null.rb +9 -0
  98. data/lib/active_model_serializers/callbacks.rb +55 -0
  99. data/lib/active_model_serializers/deprecate.rb +54 -0
  100. data/lib/active_model_serializers/deserialization.rb +15 -0
  101. data/lib/active_model_serializers/json_pointer.rb +14 -0
  102. data/lib/active_model_serializers/logging.rb +122 -0
  103. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  104. data/lib/active_model_serializers/model.rb +71 -0
  105. data/lib/active_model_serializers/railtie.rb +48 -0
  106. data/lib/active_model_serializers/register_jsonapi_renderer.rb +78 -0
  107. data/lib/active_model_serializers/serializable_resource.rb +82 -0
  108. data/lib/active_model_serializers/serialization_context.rb +39 -0
  109. data/lib/active_model_serializers/test.rb +7 -0
  110. data/lib/active_model_serializers/test/schema.rb +138 -0
  111. data/lib/active_model_serializers/test/serializer.rb +125 -0
  112. data/lib/generators/rails/USAGE +6 -0
  113. data/lib/generators/rails/resource_override.rb +10 -0
  114. data/lib/generators/rails/serializer_generator.rb +36 -0
  115. data/lib/generators/rails/templates/serializer.rb.erb +15 -0
  116. data/lib/grape/active_model_serializers.rb +16 -0
  117. data/lib/grape/formatters/active_model_serializers.rb +32 -0
  118. data/lib/grape/helpers/active_model_serializers.rb +17 -0
  119. data/test/action_controller/adapter_selector_test.rb +53 -0
  120. data/test/action_controller/explicit_serializer_test.rb +135 -0
  121. data/test/action_controller/json/include_test.rb +246 -0
  122. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  123. data/test/action_controller/json_api/errors_test.rb +40 -0
  124. data/test/action_controller/json_api/fields_test.rb +66 -0
  125. data/test/action_controller/json_api/linked_test.rb +202 -0
  126. data/test/action_controller/json_api/pagination_test.rb +116 -0
  127. data/test/action_controller/json_api/transform_test.rb +189 -0
  128. data/test/action_controller/lookup_proc_test.rb +49 -0
  129. data/test/action_controller/namespace_lookup_test.rb +232 -0
  130. data/test/action_controller/serialization_scope_name_test.rb +229 -0
  131. data/test/action_controller/serialization_test.rb +472 -0
  132. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  133. data/test/active_model_serializers/json_pointer_test.rb +22 -0
  134. data/test/active_model_serializers/logging_test.rb +77 -0
  135. data/test/active_model_serializers/model_test.rb +69 -0
  136. data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
  137. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
  138. data/test/active_model_serializers/serialization_context_test_isolated.rb +71 -0
  139. data/test/active_model_serializers/test/schema_test.rb +131 -0
  140. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  141. data/test/active_record_test.rb +9 -0
  142. data/test/adapter/attributes_test.rb +43 -0
  143. data/test/adapter/deprecation_test.rb +100 -0
  144. data/test/adapter/json/belongs_to_test.rb +45 -0
  145. data/test/adapter/json/collection_test.rb +104 -0
  146. data/test/adapter/json/has_many_test.rb +45 -0
  147. data/test/adapter/json/transform_test.rb +93 -0
  148. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  149. data/test/adapter/json_api/collection_test.rb +96 -0
  150. data/test/adapter/json_api/errors_test.rb +76 -0
  151. data/test/adapter/json_api/fields_test.rb +96 -0
  152. data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
  153. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  154. data/test/adapter/json_api/has_many_test.rb +165 -0
  155. data/test/adapter/json_api/has_one_test.rb +80 -0
  156. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +168 -0
  157. data/test/adapter/json_api/json_api_test.rb +33 -0
  158. data/test/adapter/json_api/linked_test.rb +413 -0
  159. data/test/adapter/json_api/links_test.rb +95 -0
  160. data/test/adapter/json_api/pagination_links_test.rb +193 -0
  161. data/test/adapter/json_api/parse_test.rb +137 -0
  162. data/test/adapter/json_api/relationship_test.rb +397 -0
  163. data/test/adapter/json_api/resource_identifier_test.rb +110 -0
  164. data/test/adapter/json_api/resource_meta_test.rb +100 -0
  165. data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
  166. data/test/adapter/json_api/transform_test.rb +512 -0
  167. data/test/adapter/json_api/type_test.rb +61 -0
  168. data/test/adapter/json_test.rb +46 -0
  169. data/test/adapter/null_test.rb +22 -0
  170. data/test/adapter/polymorphic_test.rb +171 -0
  171. data/test/adapter_test.rb +67 -0
  172. data/test/array_serializer_test.rb +22 -0
  173. data/test/benchmark/app.rb +65 -0
  174. data/test/benchmark/benchmarking_support.rb +67 -0
  175. data/test/benchmark/bm_active_record.rb +81 -0
  176. data/test/benchmark/bm_adapter.rb +38 -0
  177. data/test/benchmark/bm_caching.rb +119 -0
  178. data/test/benchmark/bm_lookup_chain.rb +83 -0
  179. data/test/benchmark/bm_transform.rb +45 -0
  180. data/test/benchmark/config.ru +3 -0
  181. data/test/benchmark/controllers.rb +83 -0
  182. data/test/benchmark/fixtures.rb +219 -0
  183. data/test/cache_test.rb +595 -0
  184. data/test/collection_serializer_test.rb +123 -0
  185. data/test/fixtures/active_record.rb +113 -0
  186. data/test/fixtures/poro.rb +232 -0
  187. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  188. data/test/generators/serializer_generator_test.rb +74 -0
  189. data/test/grape_test.rb +178 -0
  190. data/test/lint_test.rb +49 -0
  191. data/test/logger_test.rb +20 -0
  192. data/test/poro_test.rb +9 -0
  193. data/test/serializable_resource_test.rb +79 -0
  194. data/test/serializers/association_macros_test.rb +37 -0
  195. data/test/serializers/associations_test.rb +383 -0
  196. data/test/serializers/attribute_test.rb +153 -0
  197. data/test/serializers/attributes_test.rb +52 -0
  198. data/test/serializers/caching_configuration_test_isolated.rb +170 -0
  199. data/test/serializers/configuration_test.rb +32 -0
  200. data/test/serializers/fieldset_test.rb +14 -0
  201. data/test/serializers/meta_test.rb +202 -0
  202. data/test/serializers/options_test.rb +32 -0
  203. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  204. data/test/serializers/root_test.rb +21 -0
  205. data/test/serializers/serialization_test.rb +55 -0
  206. data/test/serializers/serializer_for_test.rb +136 -0
  207. data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
  208. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  209. data/test/support/isolated_unit.rb +82 -0
  210. data/test/support/rails5_shims.rb +53 -0
  211. data/test/support/rails_app.rb +36 -0
  212. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  213. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  214. data/test/support/schemas/custom/show.json +7 -0
  215. data/test/support/schemas/hyper_schema.json +93 -0
  216. data/test/support/schemas/render_using_json_api.json +43 -0
  217. data/test/support/schemas/simple_json_pointers.json +10 -0
  218. data/test/support/serialization_testing.rb +71 -0
  219. data/test/test_helper.rb +58 -0
  220. metadata +602 -0
@@ -0,0 +1,383 @@
1
+ require 'test_helper'
2
+ module ActiveModel
3
+ class Serializer
4
+ class AssociationsTest < ActiveSupport::TestCase
5
+ def setup
6
+ @author = Author.new(name: 'Steve K.')
7
+ @author.bio = nil
8
+ @author.roles = []
9
+ @blog = Blog.new(name: 'AMS Blog')
10
+ @post = Post.new(title: 'New Post', body: 'Body')
11
+ @tag = Tag.new(id: 'tagid', name: '#hashtagged')
12
+ @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
13
+ @post.comments = [@comment]
14
+ @post.tags = [@tag]
15
+ @post.blog = @blog
16
+ @comment.post = @post
17
+ @comment.author = nil
18
+ @post.author = @author
19
+ @author.posts = [@post]
20
+
21
+ @post_serializer = PostSerializer.new(@post, custom_options: true)
22
+ @author_serializer = AuthorSerializer.new(@author)
23
+ @comment_serializer = CommentSerializer.new(@comment)
24
+ end
25
+
26
+ def test_has_many_and_has_one
27
+ @author_serializer.associations.each do |association|
28
+ key = association.key
29
+ serializer = association.serializer
30
+ options = association.options
31
+
32
+ case key
33
+ when :posts
34
+ assert_equal true, options.fetch(:include_data)
35
+ assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
36
+ when :bio
37
+ assert_equal true, options.fetch(:include_data)
38
+ assert_nil serializer
39
+ when :roles
40
+ assert_equal true, options.fetch(:include_data)
41
+ assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
42
+ else
43
+ flunk "Unknown association: #{key}"
44
+ end
45
+ end
46
+ end
47
+
48
+ def test_has_many_with_no_serializer
49
+ PostWithTagsSerializer.new(@post).associations.each do |association|
50
+ key = association.key
51
+ serializer = association.serializer
52
+ options = association.options
53
+
54
+ assert_equal :tags, key
55
+ assert_nil serializer
56
+ assert_equal [{ id: 'tagid', name: '#hashtagged' }].to_json, options[:virtual_value].to_json
57
+ end
58
+ end
59
+
60
+ def test_serializer_options_are_passed_into_associations_serializers
61
+ association = @post_serializer
62
+ .associations
63
+ .detect { |assoc| assoc.key == :comments }
64
+
65
+ comment_serializer = association.serializer.first
66
+ class << comment_serializer
67
+ def custom_options
68
+ instance_options
69
+ end
70
+ end
71
+ assert comment_serializer.custom_options.fetch(:custom_options)
72
+ end
73
+
74
+ def test_belongs_to
75
+ @comment_serializer.associations.each do |association|
76
+ key = association.key
77
+ serializer = association.serializer
78
+
79
+ case key
80
+ when :post
81
+ assert_kind_of(PostSerializer, serializer)
82
+ when :author
83
+ assert_nil serializer
84
+ else
85
+ flunk "Unknown association: #{key}"
86
+ end
87
+
88
+ assert_equal true, association.options.fetch(:include_data)
89
+ end
90
+ end
91
+
92
+ def test_belongs_to_with_custom_method
93
+ assert(
94
+ @post_serializer.associations.any? do |association|
95
+ association.key == :blog
96
+ end
97
+ )
98
+ end
99
+
100
+ def test_associations_inheritance
101
+ inherited_klass = Class.new(PostSerializer)
102
+
103
+ assert_equal(PostSerializer._reflections, inherited_klass._reflections)
104
+ end
105
+
106
+ def test_associations_inheritance_with_new_association
107
+ inherited_klass = Class.new(PostSerializer) do
108
+ has_many :top_comments, serializer: CommentSerializer
109
+ end
110
+
111
+ assert(
112
+ PostSerializer._reflections.values.all? do |reflection|
113
+ inherited_klass._reflections.values.include?(reflection)
114
+ end
115
+ )
116
+
117
+ assert(
118
+ inherited_klass._reflections.values.any? do |reflection|
119
+ reflection.name == :top_comments
120
+ end
121
+ )
122
+ end
123
+
124
+ def test_associations_custom_keys
125
+ serializer = PostWithCustomKeysSerializer.new(@post)
126
+
127
+ expected_association_keys = serializer.associations.map(&:key)
128
+
129
+ assert expected_association_keys.include? :reviews
130
+ assert expected_association_keys.include? :writer
131
+ assert expected_association_keys.include? :site
132
+ end
133
+
134
+ class InlineAssociationTestPostSerializer < ActiveModel::Serializer
135
+ has_many :comments
136
+ has_many :comments, key: :last_comments do
137
+ object.comments.last(1)
138
+ end
139
+ end
140
+
141
+ def test_virtual_attribute_block
142
+ comment1 = ::ARModels::Comment.create!(contents: 'first comment')
143
+ comment2 = ::ARModels::Comment.create!(contents: 'last comment')
144
+ post = ::ARModels::Post.create!(
145
+ title: 'inline association test',
146
+ body: 'etc',
147
+ comments: [comment1, comment2]
148
+ )
149
+ actual = serializable(post, adapter: :attributes, serializer: InlineAssociationTestPostSerializer).as_json
150
+ expected = {
151
+ comments: [
152
+ { id: 1, contents: 'first comment' },
153
+ { id: 2, contents: 'last comment' }
154
+ ],
155
+ last_comments: [
156
+ { id: 2, contents: 'last comment' }
157
+ ]
158
+ }
159
+
160
+ assert_equal expected, actual
161
+ ensure
162
+ ::ARModels::Post.delete_all
163
+ ::ARModels::Comment.delete_all
164
+ end
165
+
166
+ class NamespacedResourcesTest < ActiveSupport::TestCase
167
+ class ResourceNamespace
168
+ class Post < ::Model
169
+ associations :comments, :author, :description
170
+ end
171
+ class Comment < ::Model; end
172
+ class Author < ::Model; end
173
+ class Description < ::Model; end
174
+ class PostSerializer < ActiveModel::Serializer
175
+ has_many :comments
176
+ belongs_to :author
177
+ has_one :description
178
+ end
179
+ class CommentSerializer < ActiveModel::Serializer; end
180
+ class AuthorSerializer < ActiveModel::Serializer; end
181
+ class DescriptionSerializer < ActiveModel::Serializer; end
182
+ end
183
+
184
+ def setup
185
+ @comment = ResourceNamespace::Comment.new
186
+ @author = ResourceNamespace::Author.new
187
+ @description = ResourceNamespace::Description.new
188
+ @post = ResourceNamespace::Post.new(comments: [@comment],
189
+ author: @author,
190
+ description: @description)
191
+ @post_serializer = ResourceNamespace::PostSerializer.new(@post)
192
+ end
193
+
194
+ def test_associations_namespaced_resources
195
+ @post_serializer.associations.each do |association|
196
+ case association.key
197
+ when :comments
198
+ assert_instance_of(ResourceNamespace::CommentSerializer, association.serializer.first)
199
+ when :author
200
+ assert_instance_of(ResourceNamespace::AuthorSerializer, association.serializer)
201
+ when :description
202
+ assert_instance_of(ResourceNamespace::DescriptionSerializer, association.serializer)
203
+ else
204
+ flunk "Unknown association: #{key}"
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ class NestedSerializersTest < ActiveSupport::TestCase
211
+ class Post < ::Model
212
+ associations :comments, :author, :description
213
+ end
214
+ class Comment < ::Model; end
215
+ class Author < ::Model; end
216
+ class Description < ::Model; end
217
+ class PostSerializer < ActiveModel::Serializer
218
+ has_many :comments
219
+ class CommentSerializer < ActiveModel::Serializer; end
220
+ belongs_to :author
221
+ class AuthorSerializer < ActiveModel::Serializer; end
222
+ has_one :description
223
+ class DescriptionSerializer < ActiveModel::Serializer; end
224
+ end
225
+
226
+ def setup
227
+ @comment = Comment.new
228
+ @author = Author.new
229
+ @description = Description.new
230
+ @post = Post.new(comments: [@comment],
231
+ author: @author,
232
+ description: @description)
233
+ @post_serializer = PostSerializer.new(@post)
234
+ end
235
+
236
+ def test_associations_namespaced_resources
237
+ @post_serializer.associations.each do |association|
238
+ case association.key
239
+ when :comments
240
+ assert_instance_of(PostSerializer::CommentSerializer, association.serializer.first)
241
+ when :author
242
+ assert_instance_of(PostSerializer::AuthorSerializer, association.serializer)
243
+ when :description
244
+ assert_instance_of(PostSerializer::DescriptionSerializer, association.serializer)
245
+ else
246
+ flunk "Unknown association: #{key}"
247
+ end
248
+ end
249
+ end
250
+
251
+ # rubocop:disable Metrics/AbcSize
252
+ def test_conditional_associations
253
+ model = Class.new(::Model) do
254
+ attributes :true, :false
255
+ associations :association
256
+ end.new(true: true, false: false)
257
+
258
+ scenarios = [
259
+ { options: { if: :true }, included: true },
260
+ { options: { if: :false }, included: false },
261
+ { options: { unless: :false }, included: true },
262
+ { options: { unless: :true }, included: false },
263
+ { options: { if: 'object.true' }, included: true },
264
+ { options: { if: 'object.false' }, included: false },
265
+ { options: { unless: 'object.false' }, included: true },
266
+ { options: { unless: 'object.true' }, included: false },
267
+ { options: { if: -> { object.true } }, included: true },
268
+ { options: { if: -> { object.false } }, included: false },
269
+ { options: { unless: -> { object.false } }, included: true },
270
+ { options: { unless: -> { object.true } }, included: false },
271
+ { options: { if: -> (s) { s.object.true } }, included: true },
272
+ { options: { if: -> (s) { s.object.false } }, included: false },
273
+ { options: { unless: -> (s) { s.object.false } }, included: true },
274
+ { options: { unless: -> (s) { s.object.true } }, included: false }
275
+ ]
276
+
277
+ scenarios.each do |s|
278
+ serializer = Class.new(ActiveModel::Serializer) do
279
+ belongs_to :association, s[:options]
280
+
281
+ def true
282
+ true
283
+ end
284
+
285
+ def false
286
+ false
287
+ end
288
+ end
289
+
290
+ hash = serializable(model, serializer: serializer).serializable_hash
291
+ assert_equal(s[:included], hash.key?(:association), "Error with #{s[:options]}")
292
+ end
293
+ end
294
+
295
+ def test_illegal_conditional_associations
296
+ exception = assert_raises(TypeError) do
297
+ Class.new(ActiveModel::Serializer) do
298
+ belongs_to :x, if: nil
299
+ end
300
+ end
301
+
302
+ assert_match(/:if should be a Symbol, String or Proc/, exception.message)
303
+ end
304
+ end
305
+
306
+ class InheritedSerializerTest < ActiveSupport::TestCase
307
+ class PostSerializer < ActiveModel::Serializer
308
+ belongs_to :author
309
+ has_many :comments
310
+ belongs_to :blog
311
+ end
312
+
313
+ class InheritedPostSerializer < PostSerializer
314
+ belongs_to :author, polymorphic: true
315
+ has_many :comments, key: :reviews
316
+ end
317
+
318
+ class AuthorSerializer < ActiveModel::Serializer
319
+ has_many :posts
320
+ has_many :roles
321
+ has_one :bio
322
+ end
323
+
324
+ class InheritedAuthorSerializer < AuthorSerializer
325
+ has_many :roles, polymorphic: true
326
+ has_one :bio, polymorphic: true
327
+ end
328
+
329
+ def setup
330
+ @author = Author.new(name: 'Steve K.')
331
+ @post = Post.new(title: 'New Post', body: 'Body')
332
+ @post_serializer = PostSerializer.new(@post)
333
+ @author_serializer = AuthorSerializer.new(@author)
334
+ @inherited_post_serializer = InheritedPostSerializer.new(@post)
335
+ @inherited_author_serializer = InheritedAuthorSerializer.new(@author)
336
+ @author_associations = @author_serializer.associations.to_a
337
+ @inherited_author_associations = @inherited_author_serializer.associations.to_a
338
+ @post_associations = @post_serializer.associations.to_a
339
+ @inherited_post_associations = @inherited_post_serializer.associations.to_a
340
+ end
341
+
342
+ test 'an author serializer must have [posts,roles,bio] associations' do
343
+ expected = [:posts, :roles, :bio].sort
344
+ result = @author_serializer.associations.map(&:name).sort
345
+ assert_equal(result, expected)
346
+ end
347
+
348
+ test 'a post serializer must have [author,comments,blog] associations' do
349
+ expected = [:author, :comments, :blog].sort
350
+ result = @post_serializer.associations.map(&:name).sort
351
+ assert_equal(result, expected)
352
+ end
353
+
354
+ test 'a serializer inheriting from another serializer can redefine has_many and has_one associations' do
355
+ expected = [:roles, :bio].sort
356
+ result = (@inherited_author_associations - @author_associations).map(&:name).sort
357
+ assert_equal(result, expected)
358
+ end
359
+
360
+ test 'a serializer inheriting from another serializer can redefine belongs_to associations' do
361
+ assert_equal [:author, :comments, :blog], @post_associations.map(&:name)
362
+ assert_equal [:author, :comments, :blog, :comments], @inherited_post_associations.map(&:name)
363
+
364
+ refute @post_associations.detect { |assoc| assoc.name == :author }.options.key?(:polymorphic)
365
+ assert_equal true, @inherited_post_associations.detect { |assoc| assoc.name == :author }.options.fetch(:polymorphic)
366
+
367
+ refute @post_associations.detect { |assoc| assoc.name == :comments }.options.key?(:key)
368
+ original_comment_assoc, new_comments_assoc = @inherited_post_associations.select { |assoc| assoc.name == :comments }
369
+ refute original_comment_assoc.options.key?(:key)
370
+ assert_equal :reviews, new_comments_assoc.options.fetch(:key)
371
+
372
+ assert_equal @post_associations.detect { |assoc| assoc.name == :blog }, @inherited_post_associations.detect { |assoc| assoc.name == :blog }
373
+ end
374
+
375
+ test 'a serializer inheriting from another serializer can have an additional association with the same name but with different key' do
376
+ expected = [:author, :comments, :blog, :reviews].sort
377
+ result = @inherited_post_serializer.associations.map { |a| a.options.fetch(:key, a.name) }.sort
378
+ assert_equal(result, expected)
379
+ end
380
+ end
381
+ end
382
+ end
383
+ end
@@ -0,0 +1,153 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class AttributeTest < ActiveSupport::TestCase
6
+ def setup
7
+ @blog = Blog.new(id: 1, name: 'AMS Hints', type: 'stuff')
8
+ @blog_serializer = AlternateBlogSerializer.new(@blog)
9
+ end
10
+
11
+ def test_attributes_definition
12
+ assert_equal([:id, :title],
13
+ @blog_serializer.class._attributes)
14
+ end
15
+
16
+ def test_json_serializable_hash
17
+ adapter = ActiveModelSerializers::Adapter::Json.new(@blog_serializer)
18
+ assert_equal({ blog: { id: 1, title: 'AMS Hints' } }, adapter.serializable_hash)
19
+ end
20
+
21
+ def test_attribute_inheritance_with_key
22
+ inherited_klass = Class.new(AlternateBlogSerializer)
23
+ blog_serializer = inherited_klass.new(@blog)
24
+ adapter = ActiveModelSerializers::Adapter::Attributes.new(blog_serializer)
25
+ assert_equal({ id: 1, title: 'AMS Hints' }, adapter.serializable_hash)
26
+ end
27
+
28
+ def test_multiple_calls_with_the_same_attribute
29
+ serializer_class = Class.new(ActiveModel::Serializer) do
30
+ attribute :title
31
+ attribute :title
32
+ end
33
+
34
+ assert_equal([:title], serializer_class._attributes)
35
+ end
36
+
37
+ def test_id_attribute_override
38
+ serializer = Class.new(ActiveModel::Serializer) do
39
+ attribute :name, key: :id
40
+ end
41
+
42
+ adapter = ActiveModelSerializers::Adapter::Json.new(serializer.new(@blog))
43
+ assert_equal({ blog: { id: 'AMS Hints' } }, adapter.serializable_hash)
44
+ end
45
+
46
+ def test_object_attribute_override
47
+ serializer = Class.new(ActiveModel::Serializer) do
48
+ attribute :name, key: :object
49
+ end
50
+
51
+ adapter = ActiveModelSerializers::Adapter::Json.new(serializer.new(@blog))
52
+ assert_equal({ blog: { object: 'AMS Hints' } }, adapter.serializable_hash)
53
+ end
54
+
55
+ def test_type_attribute
56
+ attribute_serializer = Class.new(ActiveModel::Serializer) do
57
+ attribute :id, key: :type
58
+ end
59
+ attributes_serializer = Class.new(ActiveModel::Serializer) do
60
+ attributes :type
61
+ end
62
+
63
+ adapter = ActiveModelSerializers::Adapter::Json.new(attribute_serializer.new(@blog))
64
+ assert_equal({ blog: { type: 1 } }, adapter.serializable_hash)
65
+
66
+ adapter = ActiveModelSerializers::Adapter::Json.new(attributes_serializer.new(@blog))
67
+ assert_equal({ blog: { type: 'stuff' } }, adapter.serializable_hash)
68
+ end
69
+
70
+ def test_id_attribute_override_before
71
+ serializer = Class.new(ActiveModel::Serializer) do
72
+ def id
73
+ 'custom'
74
+ end
75
+
76
+ attribute :id
77
+ end
78
+
79
+ hash = ActiveModelSerializers::SerializableResource.new(@blog, adapter: :json, serializer: serializer).serializable_hash
80
+
81
+ assert_equal('custom', hash[:blog][:id])
82
+ end
83
+
84
+ class PostWithVirtualAttribute < ::Model; attributes :first_name, :last_name end
85
+ class PostWithVirtualAttributeSerializer < ActiveModel::Serializer
86
+ attribute :name do
87
+ "#{object.first_name} #{object.last_name}"
88
+ end
89
+ end
90
+
91
+ def test_virtual_attribute_block
92
+ post = PostWithVirtualAttribute.new(first_name: 'Lucas', last_name: 'Hosseini')
93
+ hash = serializable(post).serializable_hash
94
+ expected = { name: 'Lucas Hosseini' }
95
+
96
+ assert_equal(expected, hash)
97
+ end
98
+
99
+ # rubocop:disable Metrics/AbcSize
100
+ def test_conditional_associations
101
+ model = Class.new(::Model) do
102
+ attributes :true, :false, :attribute
103
+ end.new(true: true, false: false)
104
+
105
+ scenarios = [
106
+ { options: { if: :true }, included: true },
107
+ { options: { if: :false }, included: false },
108
+ { options: { unless: :false }, included: true },
109
+ { options: { unless: :true }, included: false },
110
+ { options: { if: 'object.true' }, included: true },
111
+ { options: { if: 'object.false' }, included: false },
112
+ { options: { unless: 'object.false' }, included: true },
113
+ { options: { unless: 'object.true' }, included: false },
114
+ { options: { if: -> { object.true } }, included: true },
115
+ { options: { if: -> { object.false } }, included: false },
116
+ { options: { unless: -> { object.false } }, included: true },
117
+ { options: { unless: -> { object.true } }, included: false },
118
+ { options: { if: -> (s) { s.object.true } }, included: true },
119
+ { options: { if: -> (s) { s.object.false } }, included: false },
120
+ { options: { unless: -> (s) { s.object.false } }, included: true },
121
+ { options: { unless: -> (s) { s.object.true } }, included: false }
122
+ ]
123
+
124
+ scenarios.each do |s|
125
+ serializer = Class.new(ActiveModel::Serializer) do
126
+ attribute :attribute, s[:options]
127
+
128
+ def true
129
+ true
130
+ end
131
+
132
+ def false
133
+ false
134
+ end
135
+ end
136
+
137
+ hash = serializable(model, serializer: serializer).serializable_hash
138
+ assert_equal(s[:included], hash.key?(:attribute), "Error with #{s[:options]}")
139
+ end
140
+ end
141
+
142
+ def test_illegal_conditional_attributes
143
+ exception = assert_raises(TypeError) do
144
+ Class.new(ActiveModel::Serializer) do
145
+ attribute :x, if: nil
146
+ end
147
+ end
148
+
149
+ assert_match(/:if should be a Symbol, String or Proc/, exception.message)
150
+ end
151
+ end
152
+ end
153
+ end