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,518 @@
1
+ require 'test_helper'
2
+ module ActiveModel
3
+ class Serializer
4
+ class AssociationsTest < ActiveSupport::TestCase
5
+ class ModelWithoutSerializer < ::Model
6
+ attributes :id, :name
7
+ end
8
+
9
+ def setup
10
+ @author = Author.new(name: 'Steve K.')
11
+ @author.bio = nil
12
+ @author.roles = []
13
+ @blog = Blog.new(name: 'AMS Blog')
14
+ @post = Post.new(title: 'New Post', body: 'Body')
15
+ @tag = ModelWithoutSerializer.new(id: 'tagid', name: '#hashtagged')
16
+ @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
17
+ @post.comments = [@comment]
18
+ @post.tags = [@tag]
19
+ @post.blog = @blog
20
+ @comment.post = @post
21
+ @comment.author = nil
22
+ @post.author = @author
23
+ @author.posts = [@post]
24
+
25
+ @post_serializer = PostSerializer.new(@post, custom_options: true)
26
+ @author_serializer = AuthorSerializer.new(@author)
27
+ @comment_serializer = CommentSerializer.new(@comment)
28
+ end
29
+
30
+ def test_has_many_and_has_one
31
+ @author_serializer.associations.each do |association|
32
+ key = association.key
33
+ serializer = association.lazy_association.serializer
34
+
35
+ case key
36
+ when :posts
37
+ assert_equal true, association.include_data?
38
+ assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
39
+ when :bio
40
+ assert_equal true, association.include_data?
41
+ assert_nil serializer
42
+ when :roles
43
+ assert_equal true, association.include_data?
44
+ assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
45
+ else
46
+ flunk "Unknown association: #{key}"
47
+ end
48
+ end
49
+ end
50
+
51
+ def test_has_many_with_no_serializer
52
+ post_serializer_class = Class.new(ActiveModel::Serializer) do
53
+ attributes :id
54
+ has_many :tags
55
+ end
56
+ post_serializer_class.new(@post).associations.each do |association|
57
+ key = association.key
58
+ serializer = association.lazy_association.serializer
59
+
60
+ assert_equal :tags, key
61
+ assert_nil serializer
62
+ assert_equal [{ id: 'tagid', name: '#hashtagged' }].to_json, association.virtual_value.to_json
63
+ end
64
+ end
65
+
66
+ def test_serializer_options_are_passed_into_associations_serializers
67
+ association = @post_serializer
68
+ .associations
69
+ .detect { |assoc| assoc.key == :comments }
70
+
71
+ comment_serializer = association.lazy_association.serializer.first
72
+ class << comment_serializer
73
+ def custom_options
74
+ instance_options
75
+ end
76
+ end
77
+ assert comment_serializer.custom_options.fetch(:custom_options)
78
+ end
79
+
80
+ def test_belongs_to
81
+ @comment_serializer.associations.each do |association|
82
+ key = association.key
83
+ serializer = association.lazy_association.serializer
84
+
85
+ case key
86
+ when :post
87
+ assert_kind_of(PostSerializer, serializer)
88
+ when :author
89
+ assert_nil serializer
90
+ else
91
+ flunk "Unknown association: #{key}"
92
+ end
93
+
94
+ assert_equal true, association.include_data?
95
+ end
96
+ end
97
+
98
+ def test_belongs_to_with_custom_method
99
+ assert(
100
+ @post_serializer.associations.any? do |association|
101
+ association.key == :blog
102
+ end
103
+ )
104
+ end
105
+
106
+ def test_associations_inheritance
107
+ inherited_klass = Class.new(PostSerializer)
108
+
109
+ assert_equal(PostSerializer._reflections, inherited_klass._reflections)
110
+ end
111
+
112
+ def test_associations_inheritance_with_new_association
113
+ inherited_klass = Class.new(PostSerializer) do
114
+ has_many :top_comments, serializer: CommentSerializer
115
+ end
116
+
117
+ assert(
118
+ PostSerializer._reflections.values.all? do |reflection|
119
+ inherited_klass._reflections.values.include?(reflection)
120
+ end
121
+ )
122
+
123
+ assert(
124
+ inherited_klass._reflections.values.any? do |reflection|
125
+ reflection.name == :top_comments
126
+ end
127
+ )
128
+ end
129
+
130
+ def test_associations_custom_keys
131
+ serializer = PostWithCustomKeysSerializer.new(@post)
132
+
133
+ expected_association_keys = serializer.associations.map(&:key)
134
+
135
+ assert expected_association_keys.include? :reviews
136
+ assert expected_association_keys.include? :writer
137
+ assert expected_association_keys.include? :site
138
+ end
139
+
140
+ class BelongsToBlogModel < ::Model
141
+ attributes :id, :title
142
+ associations :blog
143
+ end
144
+ class BelongsToBlogModelSerializer < ActiveModel::Serializer
145
+ type :posts
146
+ belongs_to :blog
147
+ end
148
+
149
+ def test_belongs_to_doesnt_load_record
150
+ attributes = { id: 1, title: 'Belongs to Blog', blog: Blog.new(id: 5) }
151
+ post = BelongsToBlogModel.new(attributes)
152
+ class << post
153
+ def blog
154
+ fail 'should use blog_id'
155
+ end
156
+
157
+ def blog_id
158
+ 5
159
+ end
160
+ end
161
+
162
+ actual =
163
+ begin
164
+ original_option = BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship
165
+ BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true
166
+ serializable(post, adapter: :json_api, serializer: BelongsToBlogModelSerializer).as_json
167
+ ensure
168
+ BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = original_option
169
+ end
170
+ expected = { data: { id: '1', type: 'posts', relationships: { blog: { data: { id: '5', type: 'blogs' } } } } }
171
+
172
+ assert_equal expected, actual
173
+ end
174
+
175
+ class ExternalBlog < Blog
176
+ attributes :external_id
177
+ end
178
+ class BelongsToExternalBlogModel < ::Model
179
+ attributes :id, :title, :external_blog_id
180
+ associations :external_blog
181
+ end
182
+ class BelongsToExternalBlogModelSerializer < ActiveModel::Serializer
183
+ type :posts
184
+ belongs_to :external_blog
185
+
186
+ def external_blog_id
187
+ object.external_blog.external_id
188
+ end
189
+ end
190
+
191
+ def test_belongs_to_allows_id_overwriting
192
+ attributes = {
193
+ id: 1,
194
+ title: 'Title',
195
+ external_blog: ExternalBlog.new(id: 5, external_id: 6)
196
+ }
197
+ post = BelongsToExternalBlogModel.new(attributes)
198
+
199
+ actual =
200
+ begin
201
+ original_option = BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship
202
+ BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true
203
+ serializable(post, adapter: :json_api, serializer: BelongsToExternalBlogModelSerializer).as_json
204
+ ensure
205
+ BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = original_option
206
+ end
207
+ expected = { data: { id: '1', type: 'posts', relationships: { :'external-blog' => { data: { id: '6', type: 'external-blogs' } } } } }
208
+
209
+ assert_equal expected, actual
210
+ end
211
+
212
+ class InlineAssociationTestPostSerializer < ActiveModel::Serializer
213
+ has_many :comments
214
+ has_many :comments, key: :last_comments do
215
+ object.comments.last(1)
216
+ end
217
+ end
218
+
219
+ def test_virtual_attribute_block
220
+ comment1 = ::ARModels::Comment.create!(contents: 'first comment')
221
+ comment2 = ::ARModels::Comment.create!(contents: 'last comment')
222
+ post = ::ARModels::Post.create!(
223
+ title: 'inline association test',
224
+ body: 'etc',
225
+ comments: [comment1, comment2]
226
+ )
227
+ actual = serializable(post, adapter: :attributes, serializer: InlineAssociationTestPostSerializer).as_json
228
+ expected = {
229
+ comments: [
230
+ { id: 1, contents: 'first comment' },
231
+ { id: 2, contents: 'last comment' }
232
+ ],
233
+ last_comments: [
234
+ { id: 2, contents: 'last comment' }
235
+ ]
236
+ }
237
+
238
+ assert_equal expected, actual
239
+ ensure
240
+ ::ARModels::Post.delete_all
241
+ ::ARModels::Comment.delete_all
242
+ end
243
+
244
+ class NamespacedResourcesTest < ActiveSupport::TestCase
245
+ class ResourceNamespace
246
+ class Post < ::Model
247
+ associations :comments, :author, :description
248
+ end
249
+ class Comment < ::Model; end
250
+ class Author < ::Model; end
251
+ class Description < ::Model; end
252
+ class PostSerializer < ActiveModel::Serializer
253
+ has_many :comments
254
+ belongs_to :author
255
+ has_one :description
256
+ end
257
+ class CommentSerializer < ActiveModel::Serializer; end
258
+ class AuthorSerializer < ActiveModel::Serializer; end
259
+ class DescriptionSerializer < ActiveModel::Serializer; end
260
+ end
261
+
262
+ def setup
263
+ @comment = ResourceNamespace::Comment.new
264
+ @author = ResourceNamespace::Author.new
265
+ @description = ResourceNamespace::Description.new
266
+ @post = ResourceNamespace::Post.new(comments: [@comment],
267
+ author: @author,
268
+ description: @description)
269
+ @post_serializer = ResourceNamespace::PostSerializer.new(@post)
270
+ end
271
+
272
+ def test_associations_namespaced_resources
273
+ @post_serializer.associations.each do |association|
274
+ case association.key
275
+ when :comments
276
+ assert_instance_of(ResourceNamespace::CommentSerializer, association.lazy_association.serializer.first)
277
+ when :author
278
+ assert_instance_of(ResourceNamespace::AuthorSerializer, association.lazy_association.serializer)
279
+ when :description
280
+ assert_instance_of(ResourceNamespace::DescriptionSerializer, association.lazy_association.serializer)
281
+ else
282
+ flunk "Unknown association: #{key}"
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ class AssociationsNamespacedSerializersTest < ActiveSupport::TestCase
289
+ class Post < ::Model
290
+ associations :comments, :author, :description
291
+
292
+ def latest_comments
293
+ comments[0..3]
294
+ end
295
+ end
296
+ class Comment < ::Model; end
297
+ class Author < ::Model; end
298
+ class Description < ::Model; end
299
+
300
+ class ResourceNamespace
301
+ class PostSerializer < ActiveModel::Serializer
302
+ has_many :comments, namespace: ResourceNamespace
303
+ has_many :latest_comments, namespace: ResourceNamespace
304
+ belongs_to :author, namespace: ResourceNamespace
305
+ has_one :description, namespace: ResourceNamespace
306
+ end
307
+ class CommentSerializer < ActiveModel::Serializer; end
308
+ class AuthorSerializer < ActiveModel::Serializer; end
309
+ class DescriptionSerializer < ActiveModel::Serializer; end
310
+ end
311
+
312
+ def setup
313
+ @comment = Comment.new
314
+ @author = Author.new
315
+ @description = Description.new
316
+ @post = Post.new(comments: [@comment],
317
+ author: @author,
318
+ description: @description)
319
+ @post_serializer = ResourceNamespace::PostSerializer.new(@post)
320
+ end
321
+
322
+ def test_associations_namespaced_serializers
323
+ @post_serializer.associations.each do |association|
324
+ case association.key
325
+ when :comments, :latest_comments
326
+ assert_instance_of(ResourceNamespace::CommentSerializer, association.lazy_association.serializer.first)
327
+ when :author
328
+ assert_instance_of(ResourceNamespace::AuthorSerializer, association.lazy_association.serializer)
329
+ when :description
330
+ assert_instance_of(ResourceNamespace::DescriptionSerializer, association.lazy_association.serializer)
331
+ else
332
+ flunk "Unknown association: #{key}"
333
+ end
334
+ end
335
+ end
336
+ end
337
+
338
+ class NestedSerializersTest < ActiveSupport::TestCase
339
+ class Post < ::Model
340
+ associations :comments, :author, :description
341
+ end
342
+ class Comment < ::Model; end
343
+ class Author < ::Model; end
344
+ class Description < ::Model; end
345
+ class PostSerializer < ActiveModel::Serializer
346
+ has_many :comments
347
+ class CommentSerializer < ActiveModel::Serializer; end
348
+ belongs_to :author
349
+ class AuthorSerializer < ActiveModel::Serializer; end
350
+ has_one :description
351
+ class DescriptionSerializer < ActiveModel::Serializer; end
352
+ end
353
+
354
+ def setup
355
+ @comment = Comment.new
356
+ @author = Author.new
357
+ @description = Description.new
358
+ @post = Post.new(comments: [@comment],
359
+ author: @author,
360
+ description: @description)
361
+ @post_serializer = PostSerializer.new(@post)
362
+ end
363
+
364
+ def test_associations_namespaced_resources
365
+ @post_serializer.associations.each do |association|
366
+ case association.key
367
+ when :comments
368
+ assert_instance_of(PostSerializer::CommentSerializer, association.lazy_association.serializer.first)
369
+ when :author
370
+ assert_instance_of(PostSerializer::AuthorSerializer, association.lazy_association.serializer)
371
+ when :description
372
+ assert_instance_of(PostSerializer::DescriptionSerializer, association.lazy_association.serializer)
373
+ else
374
+ flunk "Unknown association: #{key}"
375
+ end
376
+ end
377
+ end
378
+
379
+ # rubocop:disable Metrics/AbcSize
380
+ def test_conditional_associations
381
+ model = Class.new(::Model) do
382
+ attributes :true, :false
383
+ associations :something
384
+ end.new(true: true, false: false)
385
+
386
+ scenarios = [
387
+ { options: { if: :true }, included: true },
388
+ { options: { if: :false }, included: false },
389
+ { options: { unless: :false }, included: true },
390
+ { options: { unless: :true }, included: false },
391
+ { options: { if: 'object.true' }, included: true },
392
+ { options: { if: 'object.false' }, included: false },
393
+ { options: { unless: 'object.false' }, included: true },
394
+ { options: { unless: 'object.true' }, included: false },
395
+ { options: { if: -> { object.true } }, included: true },
396
+ { options: { if: -> { object.false } }, included: false },
397
+ { options: { unless: -> { object.false } }, included: true },
398
+ { options: { unless: -> { object.true } }, included: false },
399
+ { options: { if: -> (s) { s.object.true } }, included: true },
400
+ { options: { if: -> (s) { s.object.false } }, included: false },
401
+ { options: { unless: -> (s) { s.object.false } }, included: true },
402
+ { options: { unless: -> (s) { s.object.true } }, included: false }
403
+ ]
404
+
405
+ scenarios.each do |s|
406
+ serializer = Class.new(ActiveModel::Serializer) do
407
+ belongs_to :something, s[:options]
408
+
409
+ def true
410
+ true
411
+ end
412
+
413
+ def false
414
+ false
415
+ end
416
+ end
417
+
418
+ hash = serializable(model, serializer: serializer).serializable_hash
419
+ assert_equal(s[:included], hash.key?(:something), "Error with #{s[:options]}")
420
+ end
421
+ end
422
+
423
+ def test_illegal_conditional_associations
424
+ exception = assert_raises(TypeError) do
425
+ Class.new(ActiveModel::Serializer) do
426
+ belongs_to :x, if: nil
427
+ end
428
+ end
429
+
430
+ assert_match(/:if should be a Symbol, String or Proc/, exception.message)
431
+ end
432
+ end
433
+
434
+ class InheritedSerializerTest < ActiveSupport::TestCase
435
+ class PostSerializer < ActiveModel::Serializer
436
+ belongs_to :author
437
+ has_many :comments
438
+ belongs_to :blog
439
+ end
440
+
441
+ class InheritedPostSerializer < PostSerializer
442
+ belongs_to :author, polymorphic: true
443
+ has_many :comments, key: :reviews
444
+ end
445
+
446
+ class AuthorSerializer < ActiveModel::Serializer
447
+ has_many :posts
448
+ has_many :roles
449
+ has_one :bio
450
+ end
451
+
452
+ class InheritedAuthorSerializer < AuthorSerializer
453
+ has_many :roles, polymorphic: true
454
+ has_one :bio, polymorphic: true
455
+ end
456
+
457
+ def setup
458
+ @author = Author.new(name: 'Steve K.')
459
+ @post = Post.new(title: 'New Post', body: 'Body')
460
+ @post_serializer = PostSerializer.new(@post)
461
+ @author_serializer = AuthorSerializer.new(@author)
462
+ @inherited_post_serializer = InheritedPostSerializer.new(@post)
463
+ @inherited_author_serializer = InheritedAuthorSerializer.new(@author)
464
+ @author_associations = @author_serializer.associations.to_a.sort_by(&:name)
465
+ @inherited_author_associations = @inherited_author_serializer.associations.to_a.sort_by(&:name)
466
+ @post_associations = @post_serializer.associations.to_a
467
+ @inherited_post_associations = @inherited_post_serializer.associations.to_a
468
+ end
469
+
470
+ test 'an author serializer must have [posts,roles,bio] associations' do
471
+ expected = [:posts, :roles, :bio].sort
472
+ result = @author_serializer.associations.map(&:name).sort
473
+ assert_equal(result, expected)
474
+ end
475
+
476
+ test 'a post serializer must have [author,comments,blog] associations' do
477
+ expected = [:author, :comments, :blog].sort
478
+ result = @post_serializer.associations.map(&:name).sort
479
+ assert_equal(result, expected)
480
+ end
481
+
482
+ test 'a serializer inheriting from another serializer can redefine has_many and has_one associations' do
483
+ expected = [:roles, :bio].sort
484
+ result = (@inherited_author_associations.map(&:reflection) - @author_associations.map(&:reflection)).map(&:name)
485
+ assert_equal(result, expected)
486
+ assert_equal [true, false, true], @inherited_author_associations.map(&:polymorphic?)
487
+ assert_equal [false, false, false], @author_associations.map(&:polymorphic?)
488
+ end
489
+
490
+ test 'a serializer inheriting from another serializer can redefine belongs_to associations' do
491
+ assert_equal [:author, :comments, :blog], @post_associations.map(&:name)
492
+ assert_equal [:author, :comments, :blog, :comments], @inherited_post_associations.map(&:name)
493
+
494
+ refute @post_associations.detect { |assoc| assoc.name == :author }.polymorphic?
495
+ assert @inherited_post_associations.detect { |assoc| assoc.name == :author }.polymorphic?
496
+
497
+ refute @post_associations.detect { |assoc| assoc.name == :comments }.key?
498
+ original_comment_assoc, new_comments_assoc = @inherited_post_associations.select { |assoc| assoc.name == :comments }
499
+ refute original_comment_assoc.key?
500
+ assert_equal :reviews, new_comments_assoc.key
501
+
502
+ original_blog = @post_associations.detect { |assoc| assoc.name == :blog }
503
+ inherited_blog = @inherited_post_associations.detect { |assoc| assoc.name == :blog }
504
+ original_parent_serializer = original_blog.lazy_association.association_options.delete(:parent_serializer)
505
+ inherited_parent_serializer = inherited_blog.lazy_association.association_options.delete(:parent_serializer)
506
+ assert_equal PostSerializer, original_parent_serializer.class
507
+ assert_equal InheritedPostSerializer, inherited_parent_serializer.class
508
+ end
509
+
510
+ test 'a serializer inheriting from another serializer can have an additional association with the same name but with different key' do
511
+ expected = [:author, :comments, :blog, :reviews].sort
512
+ result = @inherited_post_serializer.associations.map(&:key).sort
513
+ assert_equal(result, expected)
514
+ end
515
+ end
516
+ end
517
+ end
518
+ 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