active_model_serializers 0.8.3 → 0.10.0

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 (232) hide show
  1. checksums.yaml +4 -4
  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 +104 -0
  6. data/.rubocop_todo.yml +167 -0
  7. data/.simplecov +110 -0
  8. data/.travis.yml +39 -24
  9. data/CHANGELOG.md +465 -6
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +50 -1
  12. data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
  13. data/README.md +102 -590
  14. data/Rakefile +93 -8
  15. data/active_model_serializers.gemspec +65 -23
  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/ARCHITECTURE.md +126 -0
  21. data/docs/README.md +40 -0
  22. data/docs/STYLE.md +58 -0
  23. data/docs/general/adapters.md +245 -0
  24. data/docs/general/caching.md +52 -0
  25. data/docs/general/configuration_options.md +100 -0
  26. data/docs/general/deserialization.md +100 -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 +255 -0
  32. data/docs/general/serializers.md +372 -0
  33. data/docs/how-open-source-maintained.jpg +0 -0
  34. data/docs/howto/add_pagination_links.md +139 -0
  35. data/docs/howto/add_root_key.md +51 -0
  36. data/docs/howto/outside_controller_use.md +58 -0
  37. data/docs/howto/passing_arbitrary_options.md +27 -0
  38. data/docs/howto/serialize_poro.md +32 -0
  39. data/docs/howto/test.md +152 -0
  40. data/docs/integrations/ember-and-json-api.md +112 -0
  41. data/docs/integrations/grape.md +19 -0
  42. data/docs/jsonapi/errors.md +56 -0
  43. data/docs/jsonapi/schema/schema.json +366 -0
  44. data/docs/jsonapi/schema.md +151 -0
  45. data/docs/rfcs/0000-namespace.md +106 -0
  46. data/docs/rfcs/template.md +15 -0
  47. data/lib/action_controller/serialization.rb +31 -36
  48. data/lib/active_model/serializable_resource.rb +11 -0
  49. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  50. data/lib/active_model/serializer/adapter/base.rb +16 -0
  51. data/lib/active_model/serializer/adapter/json.rb +15 -0
  52. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  53. data/lib/active_model/serializer/adapter/null.rb +15 -0
  54. data/lib/active_model/serializer/adapter.rb +24 -0
  55. data/lib/active_model/serializer/array_serializer.rb +9 -0
  56. data/lib/active_model/serializer/association.rb +19 -0
  57. data/lib/active_model/serializer/associations.rb +87 -220
  58. data/lib/active_model/serializer/attribute.rb +25 -0
  59. data/lib/active_model/serializer/attributes.rb +82 -0
  60. data/lib/active_model/serializer/belongs_to_reflection.rb +10 -0
  61. data/lib/active_model/serializer/caching.rb +333 -0
  62. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  63. data/lib/active_model/serializer/collection_serializer.rb +64 -0
  64. data/lib/active_model/serializer/configuration.rb +35 -0
  65. data/lib/active_model/serializer/error_serializer.rb +10 -0
  66. data/lib/active_model/serializer/errors_serializer.rb +27 -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 +10 -0
  71. data/lib/active_model/serializer/include_tree.rb +111 -0
  72. data/lib/active_model/serializer/links.rb +35 -0
  73. data/lib/active_model/serializer/lint.rb +146 -0
  74. data/lib/active_model/serializer/meta.rb +29 -0
  75. data/lib/active_model/serializer/null.rb +17 -0
  76. data/lib/active_model/serializer/reflection.rb +147 -0
  77. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  78. data/lib/active_model/serializer/type.rb +25 -0
  79. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  80. data/lib/active_model/serializer.rb +158 -481
  81. data/lib/active_model_serializers/adapter/attributes.rb +76 -0
  82. data/lib/active_model_serializers/adapter/base.rb +83 -0
  83. data/lib/active_model_serializers/adapter/json.rb +21 -0
  84. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  85. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  86. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  87. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  88. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  89. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +62 -0
  90. data/lib/active_model_serializers/adapter/json_api/relationship.rb +52 -0
  91. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +37 -0
  92. data/lib/active_model_serializers/adapter/json_api.rb +516 -0
  93. data/lib/active_model_serializers/adapter/null.rb +9 -0
  94. data/lib/active_model_serializers/adapter.rb +92 -0
  95. data/lib/active_model_serializers/callbacks.rb +55 -0
  96. data/lib/active_model_serializers/deprecate.rb +55 -0
  97. data/lib/active_model_serializers/deserialization.rb +13 -0
  98. data/lib/active_model_serializers/json_pointer.rb +14 -0
  99. data/lib/active_model_serializers/key_transform.rb +70 -0
  100. data/lib/active_model_serializers/logging.rb +122 -0
  101. data/lib/active_model_serializers/model.rb +49 -0
  102. data/lib/active_model_serializers/railtie.rb +46 -0
  103. data/lib/active_model_serializers/register_jsonapi_renderer.rb +65 -0
  104. data/lib/active_model_serializers/serializable_resource.rb +81 -0
  105. data/lib/active_model_serializers/serialization_context.rb +32 -0
  106. data/lib/active_model_serializers/test/schema.rb +138 -0
  107. data/lib/active_model_serializers/test/serializer.rb +125 -0
  108. data/lib/active_model_serializers/test.rb +7 -0
  109. data/lib/active_model_serializers.rb +32 -89
  110. data/lib/generators/rails/USAGE +6 -0
  111. data/lib/generators/rails/resource_override.rb +10 -0
  112. data/lib/generators/rails/serializer_generator.rb +36 -0
  113. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  114. data/lib/grape/active_model_serializers.rb +14 -0
  115. data/lib/grape/formatters/active_model_serializers.rb +15 -0
  116. data/lib/grape/helpers/active_model_serializers.rb +16 -0
  117. data/test/action_controller/adapter_selector_test.rb +53 -0
  118. data/test/action_controller/explicit_serializer_test.rb +134 -0
  119. data/test/action_controller/json/include_test.rb +167 -0
  120. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  121. data/test/action_controller/json_api/errors_test.rb +41 -0
  122. data/test/action_controller/json_api/linked_test.rb +197 -0
  123. data/test/action_controller/json_api/pagination_test.rb +116 -0
  124. data/test/action_controller/json_api/transform_test.rb +181 -0
  125. data/test/action_controller/serialization_scope_name_test.rb +229 -0
  126. data/test/action_controller/serialization_test.rb +469 -0
  127. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  128. data/test/active_model_serializers/json_pointer_test.rb +20 -0
  129. data/test/active_model_serializers/key_transform_test.rb +263 -0
  130. data/test/active_model_serializers/logging_test.rb +77 -0
  131. data/test/active_model_serializers/model_test.rb +9 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
  133. data/test/active_model_serializers/serialization_context_test_isolated.rb +58 -0
  134. data/test/active_model_serializers/test/schema_test.rb +130 -0
  135. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  136. data/test/active_record_test.rb +9 -0
  137. data/test/adapter/deprecation_test.rb +100 -0
  138. data/test/adapter/json/belongs_to_test.rb +45 -0
  139. data/test/adapter/json/collection_test.rb +90 -0
  140. data/test/adapter/json/has_many_test.rb +45 -0
  141. data/test/adapter/json/transform_test.rb +93 -0
  142. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  143. data/test/adapter/json_api/collection_test.rb +95 -0
  144. data/test/adapter/json_api/errors_test.rb +78 -0
  145. data/test/adapter/json_api/fields_test.rb +87 -0
  146. data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
  147. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  148. data/test/adapter/json_api/has_many_test.rb +144 -0
  149. data/test/adapter/json_api/has_one_test.rb +80 -0
  150. data/test/adapter/json_api/json_api_test.rb +35 -0
  151. data/test/adapter/json_api/linked_test.rb +392 -0
  152. data/test/adapter/json_api/links_test.rb +93 -0
  153. data/test/adapter/json_api/pagination_links_test.rb +166 -0
  154. data/test/adapter/json_api/parse_test.rb +137 -0
  155. data/test/adapter/json_api/relationship_test.rb +161 -0
  156. data/test/adapter/json_api/relationships_test.rb +199 -0
  157. data/test/adapter/json_api/resource_identifier_test.rb +85 -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 +502 -0
  161. data/test/adapter/json_api/type_test.rb +61 -0
  162. data/test/adapter/json_test.rb +45 -0
  163. data/test/adapter/null_test.rb +23 -0
  164. data/test/adapter/polymorphic_test.rb +171 -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_caching.rb +119 -0
  170. data/test/benchmark/bm_transform.rb +34 -0
  171. data/test/benchmark/config.ru +3 -0
  172. data/test/benchmark/controllers.rb +84 -0
  173. data/test/benchmark/fixtures.rb +219 -0
  174. data/test/cache_test.rb +485 -0
  175. data/test/collection_serializer_test.rb +110 -0
  176. data/test/fixtures/active_record.rb +78 -0
  177. data/test/fixtures/poro.rb +282 -0
  178. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  179. data/test/generators/serializer_generator_test.rb +57 -0
  180. data/test/grape_test.rb +82 -0
  181. data/test/include_tree/from_include_args_test.rb +26 -0
  182. data/test/include_tree/from_string_test.rb +94 -0
  183. data/test/include_tree/include_args_to_hash_test.rb +64 -0
  184. data/test/lint_test.rb +49 -0
  185. data/test/logger_test.rb +18 -0
  186. data/test/poro_test.rb +9 -0
  187. data/test/serializable_resource_test.rb +83 -0
  188. data/test/serializers/association_macros_test.rb +36 -0
  189. data/test/serializers/associations_test.rb +295 -0
  190. data/test/serializers/attribute_test.rb +151 -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 +196 -0
  196. data/test/serializers/options_test.rb +21 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  198. data/test/serializers/root_test.rb +21 -0
  199. data/test/serializers/serialization_test.rb +55 -0
  200. data/test/serializers/serializer_for_test.rb +134 -0
  201. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  202. data/test/support/isolated_unit.rb +79 -0
  203. data/test/support/rails5_shims.rb +47 -0
  204. data/test/support/rails_app.rb +45 -0
  205. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  206. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  207. data/test/support/schemas/custom/show.json +7 -0
  208. data/test/support/schemas/hyper_schema.json +93 -0
  209. data/test/support/schemas/render_using_json_api.json +43 -0
  210. data/test/support/schemas/simple_json_pointers.json +10 -0
  211. data/test/support/serialization_testing.rb +53 -0
  212. data/test/test_helper.rb +48 -23
  213. metadata +449 -43
  214. data/DESIGN.textile +0 -586
  215. data/Gemfile.edge +0 -9
  216. data/bench/perf.rb +0 -43
  217. data/cruft.md +0 -19
  218. data/lib/active_model/array_serializer.rb +0 -104
  219. data/lib/active_record/serializer_override.rb +0 -16
  220. data/lib/generators/resource_override.rb +0 -13
  221. data/lib/generators/serializer/USAGE +0 -9
  222. data/lib/generators/serializer/serializer_generator.rb +0 -42
  223. data/lib/generators/serializer/templates/serializer.rb +0 -19
  224. data/test/association_test.rb +0 -592
  225. data/test/caching_test.rb +0 -96
  226. data/test/generators_test.rb +0 -85
  227. data/test/no_serialization_scope_test.rb +0 -34
  228. data/test/serialization_scope_name_test.rb +0 -67
  229. data/test/serialization_test.rb +0 -392
  230. data/test/serializer_support_test.rb +0 -51
  231. data/test/serializer_test.rb +0 -1465
  232. data/test/test_fakes.rb +0 -217
@@ -0,0 +1,83 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModelSerializers
4
+ class SerializableResourceTest < ActiveSupport::TestCase
5
+ def setup
6
+ @resource = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
7
+ @serializer = ProfileSerializer.new(@resource)
8
+ @adapter = ActiveModelSerializers::Adapter.create(@serializer)
9
+ @serializable_resource = SerializableResource.new(@resource)
10
+ end
11
+
12
+ def test_deprecation
13
+ assert_output(nil, /deprecated/) do
14
+ deprecated_serializable_resource = ActiveModel::SerializableResource.new(@resource)
15
+ assert_equal(@serializable_resource.as_json, deprecated_serializable_resource.as_json)
16
+ end
17
+ end
18
+
19
+ def test_serializable_resource_delegates_serializable_hash_to_the_adapter
20
+ options = nil
21
+ assert_equal @adapter.serializable_hash(options), @serializable_resource.serializable_hash(options)
22
+ end
23
+
24
+ def test_serializable_resource_delegates_to_json_to_the_adapter
25
+ options = nil
26
+ assert_equal @adapter.to_json(options), @serializable_resource.to_json(options)
27
+ end
28
+
29
+ def test_serializable_resource_delegates_as_json_to_the_adapter
30
+ options = nil
31
+ assert_equal @adapter.as_json(options), @serializable_resource.as_json(options)
32
+ end
33
+
34
+ def test_use_adapter_with_adapter_option
35
+ assert SerializableResource.new(@resource, { adapter: 'json' }).use_adapter?
36
+ end
37
+
38
+ def test_use_adapter_with_adapter_option_as_false
39
+ refute SerializableResource.new(@resource, { adapter: false }).use_adapter?
40
+ end
41
+
42
+ class SerializableResourceErrorsTest < Minitest::Test
43
+ def test_serializable_resource_with_errors
44
+ options = nil
45
+ resource = ModelWithErrors.new
46
+ resource.errors.add(:name, 'must be awesome')
47
+ serializable_resource = ActiveModelSerializers::SerializableResource.new(
48
+ resource, {
49
+ serializer: ActiveModel::Serializer::ErrorSerializer,
50
+ adapter: :json_api
51
+ })
52
+ expected_response_document =
53
+ { :errors =>
54
+ [
55
+ { :source => { :pointer => '/data/attributes/name' }, :detail => 'must be awesome' }
56
+ ]
57
+ }
58
+ assert_equal serializable_resource.as_json(options), expected_response_document
59
+ end
60
+
61
+ def test_serializable_resource_with_collection_containing_errors
62
+ options = nil
63
+ resources = []
64
+ resources << resource = ModelWithErrors.new
65
+ resource.errors.add(:title, 'must be amazing')
66
+ resources << ModelWithErrors.new
67
+ serializable_resource = SerializableResource.new(
68
+ resources, {
69
+ serializer: ActiveModel::Serializer::ErrorsSerializer,
70
+ each_serializer: ActiveModel::Serializer::ErrorSerializer,
71
+ adapter: :json_api
72
+ })
73
+ expected_response_document =
74
+ { :errors =>
75
+ [
76
+ { :source => { :pointer => '/data/attributes/title' }, :detail => 'must be amazing' }
77
+ ]
78
+ }
79
+ assert_equal serializable_resource.as_json(options), expected_response_document
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class AssociationMacrosTest < ActiveSupport::TestCase
6
+ AuthorSummarySerializer = Class.new
7
+ class AssociationsTestSerializer < Serializer
8
+ belongs_to :author, serializer: AuthorSummarySerializer
9
+ has_many :comments
10
+ has_one :category
11
+ end
12
+
13
+ def before_setup
14
+ @reflections = AssociationsTestSerializer._reflections
15
+ end
16
+
17
+ def test_has_one_defines_reflection
18
+ has_one_reflection = HasOneReflection.new(:category, {})
19
+
20
+ assert_includes(@reflections, has_one_reflection)
21
+ end
22
+
23
+ def test_has_many_defines_reflection
24
+ has_many_reflection = HasManyReflection.new(:comments, {})
25
+
26
+ assert_includes(@reflections, has_many_reflection)
27
+ end
28
+
29
+ def test_belongs_to_defines_reflection
30
+ belongs_to_reflection = BelongsToReflection.new(:author, serializer: AuthorSummarySerializer)
31
+
32
+ assert_includes(@reflections, belongs_to_reflection)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,295 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class AssociationsTest < ActiveSupport::TestCase
6
+ def setup
7
+ @author = Author.new(name: 'Steve K.')
8
+ @author.bio = nil
9
+ @author.roles = []
10
+ @blog = Blog.new({ name: 'AMS Blog' })
11
+ @post = Post.new({ title: 'New Post', body: 'Body' })
12
+ @tag = Tag.new({ name: '#hashtagged' })
13
+ @comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
14
+ @post.comments = [@comment]
15
+ @post.tags = [@tag]
16
+ @post.blog = @blog
17
+ @comment.post = @post
18
+ @comment.author = nil
19
+ @post.author = @author
20
+ @author.posts = [@post]
21
+
22
+ @post_serializer = PostSerializer.new(@post, { custom_options: true })
23
+ @author_serializer = AuthorSerializer.new(@author)
24
+ @comment_serializer = CommentSerializer.new(@comment)
25
+ end
26
+
27
+ def test_has_many_and_has_one
28
+ @author_serializer.associations.each do |association|
29
+ key = association.key
30
+ serializer = association.serializer
31
+ options = association.options
32
+
33
+ case key
34
+ when :posts
35
+ assert_equal({ include_data: true }, options)
36
+ assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
37
+ when :bio
38
+ assert_equal({ include_data: true }, options)
39
+ assert_nil serializer
40
+ when :roles
41
+ assert_equal({ include_data: true }, options)
42
+ assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
43
+ else
44
+ flunk "Unknown association: #{key}"
45
+ end
46
+ end
47
+ end
48
+
49
+ def test_has_many_with_no_serializer
50
+ PostWithTagsSerializer.new(@post).associations.each do |association|
51
+ key = association.key
52
+ serializer = association.serializer
53
+ options = association.options
54
+
55
+ assert_equal :tags, key
56
+ assert_nil serializer
57
+ assert_equal [{ name: '#hashtagged' }].to_json, options[:virtual_value].to_json
58
+ end
59
+ end
60
+
61
+ def test_serializer_options_are_passed_into_associations_serializers
62
+ association = @post_serializer
63
+ .associations
64
+ .detect { |assoc| assoc.key == :comments }
65
+
66
+ assert association.serializer.first.custom_options[:custom_options]
67
+ end
68
+
69
+ def test_belongs_to
70
+ @comment_serializer.associations.each do |association|
71
+ key = association.key
72
+ serializer = association.serializer
73
+
74
+ case key
75
+ when :post
76
+ assert_kind_of(PostSerializer, serializer)
77
+ when :author
78
+ assert_nil serializer
79
+ else
80
+ flunk "Unknown association: #{key}"
81
+ end
82
+
83
+ assert_equal({ include_data: true }, association.options)
84
+ end
85
+ end
86
+
87
+ def test_belongs_to_with_custom_method
88
+ assert(
89
+ @post_serializer.associations.any? do |association|
90
+ association.key == :blog
91
+ end
92
+ )
93
+ end
94
+
95
+ def test_associations_inheritance
96
+ inherited_klass = Class.new(PostSerializer)
97
+
98
+ assert_equal(PostSerializer._reflections, inherited_klass._reflections)
99
+ end
100
+
101
+ def test_associations_inheritance_with_new_association
102
+ inherited_klass = Class.new(PostSerializer) do
103
+ has_many :top_comments, serializer: CommentSerializer
104
+ end
105
+
106
+ assert(
107
+ PostSerializer._reflections.all? do |reflection|
108
+ inherited_klass._reflections.include?(reflection)
109
+ end
110
+ )
111
+
112
+ assert(
113
+ inherited_klass._reflections.any? do |reflection|
114
+ reflection.name == :top_comments
115
+ end
116
+ )
117
+ end
118
+
119
+ def test_associations_custom_keys
120
+ serializer = PostWithCustomKeysSerializer.new(@post)
121
+
122
+ expected_association_keys = serializer.associations.map(&:key)
123
+
124
+ assert expected_association_keys.include? :reviews
125
+ assert expected_association_keys.include? :writer
126
+ assert expected_association_keys.include? :site
127
+ end
128
+
129
+ class InlineAssociationTestPostSerializer < ActiveModel::Serializer
130
+ has_many :comments
131
+ has_many :comments, key: :last_comments do
132
+ object.comments.last(1)
133
+ end
134
+ end
135
+
136
+ def test_virtual_attribute_block
137
+ comment1 = ::ARModels::Comment.create!(contents: 'first comment')
138
+ comment2 = ::ARModels::Comment.create!(contents: 'last comment')
139
+ post = ::ARModels::Post.create!(
140
+ title: 'inline association test',
141
+ body: 'etc',
142
+ comments: [comment1, comment2]
143
+ )
144
+ actual = serializable(post, adapter: :attributes, serializer: InlineAssociationTestPostSerializer).as_json
145
+ expected = {
146
+ :comments => [
147
+ { :id => 1, :contents => 'first comment' },
148
+ { :id => 2, :contents => 'last comment' }
149
+ ],
150
+ :last_comments => [
151
+ { :id => 2, :contents => 'last comment' }
152
+ ]
153
+ }
154
+
155
+ assert_equal expected, actual
156
+ ensure
157
+ ::ARModels::Post.delete_all
158
+ ::ARModels::Comment.delete_all
159
+ end
160
+
161
+ class NamespacedResourcesTest < ActiveSupport::TestCase
162
+ class ResourceNamespace
163
+ Post = Class.new(::Model)
164
+ Comment = Class.new(::Model)
165
+ Author = Class.new(::Model)
166
+ Description = Class.new(::Model)
167
+ class PostSerializer < ActiveModel::Serializer
168
+ has_many :comments
169
+ belongs_to :author
170
+ has_one :description
171
+ end
172
+ CommentSerializer = Class.new(ActiveModel::Serializer)
173
+ AuthorSerializer = Class.new(ActiveModel::Serializer)
174
+ DescriptionSerializer = Class.new(ActiveModel::Serializer)
175
+ end
176
+
177
+ def setup
178
+ @comment = ResourceNamespace::Comment.new
179
+ @author = ResourceNamespace::Author.new
180
+ @description = ResourceNamespace::Description.new
181
+ @post = ResourceNamespace::Post.new(comments: [@comment],
182
+ author: @author,
183
+ description: @description)
184
+ @post_serializer = ResourceNamespace::PostSerializer.new(@post)
185
+ end
186
+
187
+ def test_associations_namespaced_resources
188
+ @post_serializer.associations.each do |association|
189
+ case association.key
190
+ when :comments
191
+ assert_instance_of(ResourceNamespace::CommentSerializer, association.serializer.first)
192
+ when :author
193
+ assert_instance_of(ResourceNamespace::AuthorSerializer, association.serializer)
194
+ when :description
195
+ assert_instance_of(ResourceNamespace::DescriptionSerializer, association.serializer)
196
+ else
197
+ flunk "Unknown association: #{key}"
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ class NestedSerializersTest < ActiveSupport::TestCase
204
+ Post = Class.new(::Model)
205
+ Comment = Class.new(::Model)
206
+ Author = Class.new(::Model)
207
+ Description = Class.new(::Model)
208
+ class PostSerializer < ActiveModel::Serializer
209
+ has_many :comments
210
+ CommentSerializer = Class.new(ActiveModel::Serializer)
211
+ belongs_to :author
212
+ AuthorSerializer = Class.new(ActiveModel::Serializer)
213
+ has_one :description
214
+ DescriptionSerializer = Class.new(ActiveModel::Serializer)
215
+ end
216
+
217
+ def setup
218
+ @comment = Comment.new
219
+ @author = Author.new
220
+ @description = Description.new
221
+ @post = Post.new(comments: [@comment],
222
+ author: @author,
223
+ description: @description)
224
+ @post_serializer = PostSerializer.new(@post)
225
+ end
226
+
227
+ def test_associations_namespaced_resources
228
+ @post_serializer.associations.each do |association|
229
+ case association.key
230
+ when :comments
231
+ assert_instance_of(PostSerializer::CommentSerializer, association.serializer.first)
232
+ when :author
233
+ assert_instance_of(PostSerializer::AuthorSerializer, association.serializer)
234
+ when :description
235
+ assert_instance_of(PostSerializer::DescriptionSerializer, association.serializer)
236
+ else
237
+ flunk "Unknown association: #{key}"
238
+ end
239
+ end
240
+ end
241
+
242
+ # rubocop:disable Metrics/AbcSize
243
+ def test_conditional_associations
244
+ model = ::Model.new(true: true, false: false)
245
+
246
+ scenarios = [
247
+ { options: { if: :true }, included: true },
248
+ { options: { if: :false }, included: false },
249
+ { options: { unless: :false }, included: true },
250
+ { options: { unless: :true }, included: false },
251
+ { options: { if: 'object.true' }, included: true },
252
+ { options: { if: 'object.false' }, included: false },
253
+ { options: { unless: 'object.false' }, included: true },
254
+ { options: { unless: 'object.true' }, included: false },
255
+ { options: { if: -> { object.true } }, included: true },
256
+ { options: { if: -> { object.false } }, included: false },
257
+ { options: { unless: -> { object.false } }, included: true },
258
+ { options: { unless: -> { object.true } }, included: false },
259
+ { options: { if: -> (s) { s.object.true } }, included: true },
260
+ { options: { if: -> (s) { s.object.false } }, included: false },
261
+ { options: { unless: -> (s) { s.object.false } }, included: true },
262
+ { options: { unless: -> (s) { s.object.true } }, included: false }
263
+ ]
264
+
265
+ scenarios.each do |s|
266
+ serializer = Class.new(ActiveModel::Serializer) do
267
+ belongs_to :association, s[:options]
268
+
269
+ def true
270
+ true
271
+ end
272
+
273
+ def false
274
+ false
275
+ end
276
+ end
277
+
278
+ hash = serializable(model, serializer: serializer).serializable_hash
279
+ assert_equal(s[:included], hash.key?(:association), "Error with #{s[:options]}")
280
+ end
281
+ end
282
+
283
+ def test_illegal_conditional_associations
284
+ exception = assert_raises(TypeError) do
285
+ Class.new(ActiveModel::Serializer) do
286
+ belongs_to :x, if: nil
287
+ end
288
+ end
289
+
290
+ assert_match(/:if should be a Symbol, String or Proc/, exception.message)
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
@@ -0,0 +1,151 @@
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
+ PostWithVirtualAttribute = Class.new(::Model)
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 = ::Model.new(true: true, false: false)
102
+
103
+ scenarios = [
104
+ { options: { if: :true }, included: true },
105
+ { options: { if: :false }, included: false },
106
+ { options: { unless: :false }, included: true },
107
+ { options: { unless: :true }, included: false },
108
+ { options: { if: 'object.true' }, included: true },
109
+ { options: { if: 'object.false' }, included: false },
110
+ { options: { unless: 'object.false' }, included: true },
111
+ { options: { unless: 'object.true' }, included: false },
112
+ { options: { if: -> { object.true } }, included: true },
113
+ { options: { if: -> { object.false } }, included: false },
114
+ { options: { unless: -> { object.false } }, included: true },
115
+ { options: { unless: -> { object.true } }, included: false },
116
+ { options: { if: -> (s) { s.object.true } }, included: true },
117
+ { options: { if: -> (s) { s.object.false } }, included: false },
118
+ { options: { unless: -> (s) { s.object.false } }, included: true },
119
+ { options: { unless: -> (s) { s.object.true } }, included: false }
120
+ ]
121
+
122
+ scenarios.each do |s|
123
+ serializer = Class.new(ActiveModel::Serializer) do
124
+ attribute :attribute, s[:options]
125
+
126
+ def true
127
+ true
128
+ end
129
+
130
+ def false
131
+ false
132
+ end
133
+ end
134
+
135
+ hash = serializable(model, serializer: serializer).serializable_hash
136
+ assert_equal(s[:included], hash.key?(:attribute), "Error with #{s[:options]}")
137
+ end
138
+ end
139
+
140
+ def test_illegal_conditional_attributes
141
+ exception = assert_raises(TypeError) do
142
+ Class.new(ActiveModel::Serializer) do
143
+ attribute :x, if: nil
144
+ end
145
+ end
146
+
147
+ assert_match(/:if should be a Symbol, String or Proc/, exception.message)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class AttributesTest < ActiveSupport::TestCase
6
+ def setup
7
+ @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
8
+ @profile_serializer = ProfileSerializer.new(@profile)
9
+ @comment = Comment.new(id: 1, body: 'ZOMG!!', date: '2015')
10
+ @serializer_klass = Class.new(CommentSerializer)
11
+ @serializer_klass_with_new_attributes = Class.new(CommentSerializer) do
12
+ attributes :date, :likes
13
+ end
14
+ end
15
+
16
+ def test_attributes_definition
17
+ assert_equal([:name, :description],
18
+ @profile_serializer.class._attributes)
19
+ end
20
+
21
+ def test_attributes_inheritance_definition
22
+ assert_equal([:id, :body], @serializer_klass._attributes)
23
+ end
24
+
25
+ def test_attributes_inheritance
26
+ serializer = @serializer_klass.new(@comment)
27
+ assert_equal({ id: 1, body: 'ZOMG!!' },
28
+ serializer.attributes)
29
+ end
30
+
31
+ def test_attribute_inheritance_with_new_attribute_definition
32
+ assert_equal([:id, :body, :date, :likes], @serializer_klass_with_new_attributes._attributes)
33
+ assert_equal([:id, :body], CommentSerializer._attributes)
34
+ end
35
+
36
+ def test_attribute_inheritance_with_new_attribute
37
+ serializer = @serializer_klass_with_new_attributes.new(@comment)
38
+ assert_equal({ id: 1, body: 'ZOMG!!', date: '2015', likes: nil },
39
+ serializer.attributes)
40
+ end
41
+
42
+ def test_multiple_calls_with_the_same_attribute
43
+ serializer_class = Class.new(ActiveModel::Serializer) do
44
+ attributes :id, :title
45
+ attributes :id, :title, :title, :body
46
+ end
47
+
48
+ assert_equal([:id, :title, :body], serializer_class._attributes)
49
+ end
50
+ end
51
+ end
52
+ end