active_model_serializers_custom 0.10.90

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 (215) 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 +109 -0
  6. data/.simplecov +110 -0
  7. data/.travis.yml +63 -0
  8. data/CHANGELOG.md +727 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +74 -0
  12. data/MIT-LICENSE +22 -0
  13. data/README.md +305 -0
  14. data/Rakefile +76 -0
  15. data/active_model_serializers.gemspec +64 -0
  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.md +151 -0
  48. data/docs/jsonapi/schema/schema.json +366 -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 +76 -0
  52. data/lib/active_model/serializable_resource.rb +13 -0
  53. data/lib/active_model/serializer.rb +418 -0
  54. data/lib/active_model/serializer/adapter.rb +26 -0
  55. data/lib/active_model/serializer/adapter/attributes.rb +17 -0
  56. data/lib/active_model/serializer/adapter/base.rb +20 -0
  57. data/lib/active_model/serializer/adapter/json.rb +17 -0
  58. data/lib/active_model/serializer/adapter/json_api.rb +17 -0
  59. data/lib/active_model/serializer/adapter/null.rb +17 -0
  60. data/lib/active_model/serializer/array_serializer.rb +14 -0
  61. data/lib/active_model/serializer/association.rb +91 -0
  62. data/lib/active_model/serializer/attribute.rb +27 -0
  63. data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
  64. data/lib/active_model/serializer/collection_serializer.rb +90 -0
  65. data/lib/active_model/serializer/concerns/caching.rb +304 -0
  66. data/lib/active_model/serializer/error_serializer.rb +16 -0
  67. data/lib/active_model/serializer/errors_serializer.rb +34 -0
  68. data/lib/active_model/serializer/field.rb +92 -0
  69. data/lib/active_model/serializer/fieldset.rb +33 -0
  70. data/lib/active_model/serializer/has_many_reflection.rb +12 -0
  71. data/lib/active_model/serializer/has_one_reflection.rb +9 -0
  72. data/lib/active_model/serializer/lazy_association.rb +99 -0
  73. data/lib/active_model/serializer/link.rb +23 -0
  74. data/lib/active_model/serializer/lint.rb +152 -0
  75. data/lib/active_model/serializer/null.rb +19 -0
  76. data/lib/active_model/serializer/reflection.rb +212 -0
  77. data/lib/active_model/serializer/version.rb +7 -0
  78. data/lib/active_model_serializers.rb +63 -0
  79. data/lib/active_model_serializers/adapter.rb +100 -0
  80. data/lib/active_model_serializers/adapter/attributes.rb +15 -0
  81. data/lib/active_model_serializers/adapter/base.rb +85 -0
  82. data/lib/active_model_serializers/adapter/json.rb +23 -0
  83. data/lib/active_model_serializers/adapter/json_api.rb +535 -0
  84. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
  85. data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
  86. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
  87. data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
  88. data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
  89. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +90 -0
  90. data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
  91. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
  92. data/lib/active_model_serializers/adapter/null.rb +11 -0
  93. data/lib/active_model_serializers/callbacks.rb +57 -0
  94. data/lib/active_model_serializers/deprecate.rb +56 -0
  95. data/lib/active_model_serializers/deserialization.rb +17 -0
  96. data/lib/active_model_serializers/json_pointer.rb +16 -0
  97. data/lib/active_model_serializers/logging.rb +124 -0
  98. data/lib/active_model_serializers/lookup_chain.rb +82 -0
  99. data/lib/active_model_serializers/model.rb +132 -0
  100. data/lib/active_model_serializers/railtie.rb +52 -0
  101. data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
  102. data/lib/active_model_serializers/serializable_resource.rb +84 -0
  103. data/lib/active_model_serializers/serialization_context.rb +41 -0
  104. data/lib/active_model_serializers/test.rb +9 -0
  105. data/lib/active_model_serializers/test/schema.rb +140 -0
  106. data/lib/active_model_serializers/test/serializer.rb +127 -0
  107. data/lib/generators/rails/USAGE +6 -0
  108. data/lib/generators/rails/resource_override.rb +12 -0
  109. data/lib/generators/rails/serializer_generator.rb +38 -0
  110. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  111. data/lib/grape/active_model_serializers.rb +18 -0
  112. data/lib/grape/formatters/active_model_serializers.rb +34 -0
  113. data/lib/grape/helpers/active_model_serializers.rb +19 -0
  114. data/lib/tasks/rubocop.rake +55 -0
  115. data/test/action_controller/adapter_selector_test.rb +64 -0
  116. data/test/action_controller/explicit_serializer_test.rb +137 -0
  117. data/test/action_controller/json/include_test.rb +248 -0
  118. data/test/action_controller/json_api/deserialization_test.rb +114 -0
  119. data/test/action_controller/json_api/errors_test.rb +42 -0
  120. data/test/action_controller/json_api/fields_test.rb +68 -0
  121. data/test/action_controller/json_api/linked_test.rb +204 -0
  122. data/test/action_controller/json_api/pagination_test.rb +126 -0
  123. data/test/action_controller/json_api/transform_test.rb +191 -0
  124. data/test/action_controller/lookup_proc_test.rb +51 -0
  125. data/test/action_controller/namespace_lookup_test.rb +239 -0
  126. data/test/action_controller/serialization_scope_name_test.rb +237 -0
  127. data/test/action_controller/serialization_test.rb +480 -0
  128. data/test/active_model_serializers/adapter_for_test.rb +210 -0
  129. data/test/active_model_serializers/json_pointer_test.rb +24 -0
  130. data/test/active_model_serializers/logging_test.rb +79 -0
  131. data/test/active_model_serializers/model_test.rb +144 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +70 -0
  133. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +163 -0
  134. data/test/active_model_serializers/serialization_context_test_isolated.rb +73 -0
  135. data/test/active_model_serializers/test/schema_test.rb +133 -0
  136. data/test/active_model_serializers/test/serializer_test.rb +64 -0
  137. data/test/active_record_test.rb +11 -0
  138. data/test/adapter/attributes_test.rb +42 -0
  139. data/test/adapter/deprecation_test.rb +102 -0
  140. data/test/adapter/json/belongs_to_test.rb +47 -0
  141. data/test/adapter/json/collection_test.rb +106 -0
  142. data/test/adapter/json/has_many_test.rb +55 -0
  143. data/test/adapter/json/transform_test.rb +95 -0
  144. data/test/adapter/json_api/belongs_to_test.rb +157 -0
  145. data/test/adapter/json_api/collection_test.rb +98 -0
  146. data/test/adapter/json_api/errors_test.rb +78 -0
  147. data/test/adapter/json_api/fields_test.rb +98 -0
  148. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
  149. data/test/adapter/json_api/has_many_test.rb +175 -0
  150. data/test/adapter/json_api/has_one_test.rb +82 -0
  151. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +215 -0
  152. data/test/adapter/json_api/json_api_test.rb +35 -0
  153. data/test/adapter/json_api/linked_test.rb +415 -0
  154. data/test/adapter/json_api/links_test.rb +112 -0
  155. data/test/adapter/json_api/pagination_links_test.rb +208 -0
  156. data/test/adapter/json_api/parse_test.rb +139 -0
  157. data/test/adapter/json_api/relationship_test.rb +399 -0
  158. data/test/adapter/json_api/resource_meta_test.rb +102 -0
  159. data/test/adapter/json_api/toplevel_jsonapi_test.rb +84 -0
  160. data/test/adapter/json_api/transform_test.rb +514 -0
  161. data/test/adapter/json_api/type_test.rb +195 -0
  162. data/test/adapter/json_test.rb +48 -0
  163. data/test/adapter/null_test.rb +24 -0
  164. data/test/adapter/polymorphic_test.rb +220 -0
  165. data/test/adapter_test.rb +69 -0
  166. data/test/array_serializer_test.rb +24 -0
  167. data/test/benchmark/app.rb +67 -0
  168. data/test/benchmark/benchmarking_support.rb +69 -0
  169. data/test/benchmark/bm_active_record.rb +83 -0
  170. data/test/benchmark/bm_adapter.rb +40 -0
  171. data/test/benchmark/bm_caching.rb +121 -0
  172. data/test/benchmark/bm_lookup_chain.rb +85 -0
  173. data/test/benchmark/bm_transform.rb +47 -0
  174. data/test/benchmark/config.ru +3 -0
  175. data/test/benchmark/controllers.rb +85 -0
  176. data/test/benchmark/fixtures.rb +221 -0
  177. data/test/cache_test.rb +717 -0
  178. data/test/collection_serializer_test.rb +129 -0
  179. data/test/fixtures/active_record.rb +115 -0
  180. data/test/fixtures/poro.rb +227 -0
  181. data/test/generators/scaffold_controller_generator_test.rb +26 -0
  182. data/test/generators/serializer_generator_test.rb +77 -0
  183. data/test/grape_test.rb +198 -0
  184. data/test/lint_test.rb +51 -0
  185. data/test/logger_test.rb +22 -0
  186. data/test/poro_test.rb +11 -0
  187. data/test/serializable_resource_test.rb +81 -0
  188. data/test/serializers/association_macros_test.rb +39 -0
  189. data/test/serializers/associations_test.rb +520 -0
  190. data/test/serializers/attribute_test.rb +155 -0
  191. data/test/serializers/attributes_test.rb +54 -0
  192. data/test/serializers/caching_configuration_test_isolated.rb +172 -0
  193. data/test/serializers/configuration_test.rb +34 -0
  194. data/test/serializers/fieldset_test.rb +16 -0
  195. data/test/serializers/meta_test.rb +204 -0
  196. data/test/serializers/options_test.rb +34 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +81 -0
  198. data/test/serializers/reflection_test.rb +481 -0
  199. data/test/serializers/root_test.rb +23 -0
  200. data/test/serializers/serialization_test.rb +57 -0
  201. data/test/serializers/serializer_for_test.rb +138 -0
  202. data/test/serializers/serializer_for_with_namespace_test.rb +90 -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 +86 -0
  205. data/test/support/rails5_shims.rb +55 -0
  206. data/test/support/rails_app.rb +40 -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 +81 -0
  214. data/test/test_helper.rb +72 -0
  215. metadata +622 -0
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module ActiveModel
6
+ class Serializer
7
+ class OptionsTest < ActiveSupport::TestCase
8
+ class ModelWithOptions < ActiveModelSerializers::Model
9
+ attributes :name, :description
10
+ end
11
+ class ModelWithOptionsSerializer < ActiveModel::Serializer
12
+ attributes :name, :description
13
+
14
+ def arguments_passed_in?
15
+ instance_options[:my_options] == :accessible
16
+ end
17
+ end
18
+
19
+ setup do
20
+ @model_with_options = ModelWithOptions.new(name: 'Name 1', description: 'Description 1')
21
+ end
22
+
23
+ def test_options_are_accessible
24
+ model_with_options_serializer = ModelWithOptionsSerializer.new(@model_with_options, my_options: :accessible)
25
+ assert model_with_options_serializer.arguments_passed_in?
26
+ end
27
+
28
+ def test_no_option_is_passed_in
29
+ model_with_options_serializer = ModelWithOptionsSerializer.new(@model_with_options)
30
+ refute model_with_options_serializer.arguments_passed_in?
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module ActiveModel
6
+ class Serializer
7
+ class ReadAttributeForSerializationTest < ActiveSupport::TestCase
8
+ # https://github.com/rails-api/active_model_serializers/issues/1653
9
+ class Parent < ActiveModelSerializers::Model
10
+ attributes :id
11
+ end
12
+ class Child < Parent
13
+ attributes :name
14
+ end
15
+ class ParentSerializer < ActiveModel::Serializer
16
+ attributes :$id
17
+
18
+ define_method(:$id) do
19
+ object.id
20
+ end
21
+ end
22
+ class ChildSerializer < ParentSerializer
23
+ attributes :name
24
+ end
25
+
26
+ def test_child_serializer_calls_dynamic_method_in_parent_serializer
27
+ parent = ParentSerializer.new(Parent.new(id: 5))
28
+ child = ChildSerializer.new(Child.new(id: 6, name: 'Child'))
29
+ assert_equal 5, parent.read_attribute_for_serialization(:$id)
30
+ assert_equal 6, child.read_attribute_for_serialization(:$id)
31
+ end
32
+
33
+ # https://github.com/rails-api/active_model_serializers/issues/1658
34
+ class ErrorResponse < ActiveModelSerializers::Model
35
+ attributes :error
36
+ end
37
+ class ApplicationSerializer < ActiveModel::Serializer
38
+ attributes :status
39
+
40
+ def status
41
+ object.try(:errors).blank? && object.try(:error).blank?
42
+ end
43
+ end
44
+ class ErrorResponseSerializer < ApplicationSerializer
45
+ attributes :error
46
+ end
47
+ class ErrorResponseWithSuperSerializer < ApplicationSerializer
48
+ attributes :error
49
+
50
+ def success
51
+ super
52
+ end
53
+ end
54
+
55
+ def test_child_serializer_with_error_attribute
56
+ error = ErrorResponse.new(error: 'i have an error')
57
+ serializer = ErrorResponseSerializer.new(error)
58
+ serializer_with_super = ErrorResponseWithSuperSerializer.new(error)
59
+ assert_equal false, serializer.read_attribute_for_serialization(:status)
60
+ assert_equal false, serializer_with_super.read_attribute_for_serialization(:status)
61
+ end
62
+
63
+ def test_child_serializer_with_errors
64
+ error = ErrorResponse.new
65
+ error.errors.add(:invalid, 'i am not valid')
66
+ serializer = ErrorResponseSerializer.new(error)
67
+ serializer_with_super = ErrorResponseWithSuperSerializer.new(error)
68
+ assert_equal false, serializer.read_attribute_for_serialization(:status)
69
+ assert_equal false, serializer_with_super.read_attribute_for_serialization(:status)
70
+ end
71
+
72
+ def test_child_serializer_no_error_attribute_or_errors
73
+ error = ErrorResponse.new
74
+ serializer = ErrorResponseSerializer.new(error)
75
+ serializer_with_super = ErrorResponseWithSuperSerializer.new(error)
76
+ assert_equal true, serializer.read_attribute_for_serialization(:status)
77
+ assert_equal true, serializer_with_super.read_attribute_for_serialization(:status)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,481 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ module ActiveModel
5
+ class Serializer
6
+ class ReflectionTest < ActiveSupport::TestCase
7
+ class Blog < ActiveModelSerializers::Model
8
+ attributes :id
9
+ end
10
+ class BlogSerializer < ActiveModel::Serializer
11
+ type 'blog'
12
+ attributes :id
13
+ end
14
+
15
+ setup do
16
+ @expected_meta = { id: 1 }
17
+ @expected_links = { self: 'no_uri_validation' }
18
+ @empty_links = {}
19
+ model_attributes = { blog: Blog.new(@expected_meta) }
20
+ @model = Class.new(ActiveModelSerializers::Model) do
21
+ attributes(*model_attributes.keys)
22
+
23
+ def self.name
24
+ 'TestModel'
25
+ end
26
+ end.new(model_attributes)
27
+ @instance_options = {}
28
+ end
29
+
30
+ def evaluate_association_value(association)
31
+ association.lazy_association.eval_reflection_block
32
+ end
33
+
34
+ # TODO: Remaining tests
35
+ # test_reflection_value_block_with_scope
36
+ # test_reflection_value_uses_serializer_instance_method
37
+ # test_reflection_excluded_eh_blank_is_false
38
+ # test_reflection_excluded_eh_if
39
+ # test_reflection_excluded_eh_unless
40
+ # test_evaluate_condition_symbol_serializer_method
41
+ # test_evaluate_condition_string_serializer_method
42
+ # test_evaluate_condition_proc
43
+ # test_evaluate_condition_proc_yields_serializer
44
+ # test_evaluate_condition_other
45
+ # test_options_key
46
+ # test_options_polymorphic
47
+ # test_options_serializer
48
+ # test_options_virtual_value
49
+ # test_options_namespace
50
+
51
+ def test_reflection_value
52
+ serializer_class = Class.new(ActiveModel::Serializer) do
53
+ has_one :blog
54
+ end
55
+ serializer_instance = serializer_class.new(@model, @instance_options)
56
+
57
+ # Get Reflection
58
+ reflection = serializer_class._reflections.fetch(:blog)
59
+
60
+ # Assert
61
+ assert_nil reflection.block
62
+ assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
63
+ assert_equal true, reflection.options.fetch(:include_data_setting)
64
+
65
+ include_slice = :does_not_matter
66
+ assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice)
67
+ end
68
+
69
+ def test_reflection_value_block
70
+ serializer_class = Class.new(ActiveModel::Serializer) do
71
+ has_one :blog do
72
+ object.blog
73
+ end
74
+ end
75
+ serializer_instance = serializer_class.new(@model, @instance_options)
76
+
77
+ # Get Reflection
78
+ reflection = serializer_class._reflections.fetch(:blog)
79
+
80
+ # Assert
81
+ assert_respond_to reflection.block, :call
82
+ assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
83
+ assert_equal true, reflection.options.fetch(:include_data_setting)
84
+
85
+ include_slice = :does_not_matter
86
+ assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice)
87
+ end
88
+
89
+ def test_reflection_value_block_with_explicit_include_data_true
90
+ serializer_class = Class.new(ActiveModel::Serializer) do
91
+ has_one :blog do
92
+ include_data true
93
+ object.blog
94
+ end
95
+ end
96
+ serializer_instance = serializer_class.new(@model, @instance_options)
97
+
98
+ # Get Reflection
99
+ reflection = serializer_class._reflections.fetch(:blog)
100
+
101
+ # Assert
102
+ assert_respond_to reflection.block, :call
103
+ assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
104
+ assert_equal true, reflection.options.fetch(:include_data_setting)
105
+
106
+ include_slice = :does_not_matter
107
+ assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice)
108
+ end
109
+
110
+ def test_reflection_value_block_with_include_data_false_mutates_the_reflection_include_data
111
+ serializer_class = Class.new(ActiveModel::Serializer) do
112
+ has_one :blog do
113
+ include_data false
114
+ object.blog
115
+ end
116
+ end
117
+ serializer_instance = serializer_class.new(@model, @instance_options)
118
+
119
+ # Get Reflection
120
+ reflection = serializer_class._reflections.fetch(:blog)
121
+
122
+ # Assert
123
+ assert_respond_to reflection.block, :call
124
+ assert_equal true, reflection.options.fetch(:include_data_setting)
125
+ include_slice = :does_not_matter
126
+ assert_nil reflection.send(:value, serializer_instance, include_slice)
127
+ assert_equal false, reflection.options.fetch(:include_data_setting)
128
+ end
129
+
130
+ def test_reflection_value_block_with_include_data_if_sideloaded_included_mutates_the_reflection_include_data
131
+ serializer_class = Class.new(ActiveModel::Serializer) do
132
+ has_one :blog do
133
+ include_data :if_sideloaded
134
+ object.blog
135
+ end
136
+ end
137
+ serializer_instance = serializer_class.new(@model, @instance_options)
138
+
139
+ # Get Reflection
140
+ reflection = serializer_class._reflections.fetch(:blog)
141
+
142
+ # Assert
143
+ assert_respond_to reflection.block, :call
144
+ assert_equal true, reflection.options.fetch(:include_data_setting)
145
+ include_slice = {}
146
+ assert_nil reflection.send(:value, serializer_instance, include_slice)
147
+ assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting)
148
+ end
149
+
150
+ def test_reflection_value_block_with_include_data_if_sideloaded_excluded_mutates_the_reflection_include_data
151
+ serializer_class = Class.new(ActiveModel::Serializer) do
152
+ has_one :blog do
153
+ include_data :if_sideloaded
154
+ object.blog
155
+ end
156
+ end
157
+ serializer_instance = serializer_class.new(@model, @instance_options)
158
+
159
+ # Get Reflection
160
+ reflection = serializer_class._reflections.fetch(:blog)
161
+
162
+ # Assert
163
+ assert_respond_to reflection.block, :call
164
+ assert_equal true, reflection.options.fetch(:include_data_setting)
165
+ include_slice = { blog: :does_not_matter }
166
+ assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice)
167
+ assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting)
168
+ end
169
+
170
+ def test_reflection_block_with_link_mutates_the_reflection_links
171
+ serializer_class = Class.new(ActiveModel::Serializer) do
172
+ has_one :blog do
173
+ link :self, 'no_uri_validation'
174
+ end
175
+ end
176
+ serializer_instance = serializer_class.new(@model, @instance_options)
177
+
178
+ # Get Reflection
179
+ reflection = serializer_class._reflections.fetch(:blog)
180
+ assert_equal @empty_links, reflection.options.fetch(:links)
181
+
182
+ # Build Association
183
+ association = reflection.build_association(serializer_instance, @instance_options)
184
+
185
+ # Assert association links empty when not yet evaluated
186
+ assert_equal @empty_links, reflection.options.fetch(:links)
187
+ assert_equal @empty_links, association.links
188
+
189
+ evaluate_association_value(association)
190
+
191
+ assert_equal @expected_links, association.links
192
+ assert_equal @expected_links, reflection.options.fetch(:links)
193
+ end
194
+
195
+ def test_reflection_block_with_link_block_mutates_the_reflection_links
196
+ serializer_class = Class.new(ActiveModel::Serializer) do
197
+ has_one :blog do
198
+ link :self do
199
+ 'no_uri_validation'
200
+ end
201
+ end
202
+ end
203
+ serializer_instance = serializer_class.new(@model, @instance_options)
204
+
205
+ # Get Reflection
206
+ reflection = serializer_class._reflections.fetch(:blog)
207
+ assert_equal @empty_links, reflection.options.fetch(:links)
208
+
209
+ # Build Association
210
+ association = reflection.build_association(serializer_instance, @instance_options)
211
+
212
+ # Assert association links empty when not yet evaluated
213
+ assert_equal @empty_links, association.links
214
+
215
+ evaluate_association_value(association)
216
+
217
+ # Assert before instance_eval link
218
+ link = association.links.fetch(:self)
219
+ assert_respond_to link, :call
220
+ assert_respond_to reflection.options.fetch(:links).fetch(:self), :call
221
+
222
+ # Assert after instance_eval link
223
+ assert_equal @expected_links.fetch(:self), reflection.instance_eval(&link)
224
+ assert_respond_to reflection.options.fetch(:links).fetch(:self), :call
225
+ end
226
+
227
+ def test_reflection_block_with_meta_mutates_the_reflection_meta
228
+ serializer_class = Class.new(ActiveModel::Serializer) do
229
+ has_one :blog do
230
+ meta(id: object.blog.id)
231
+ end
232
+ end
233
+ serializer_instance = serializer_class.new(@model, @instance_options)
234
+
235
+ # Get Reflection
236
+ reflection = serializer_class._reflections.fetch(:blog)
237
+ assert_nil reflection.options.fetch(:meta)
238
+
239
+ # Build Association
240
+ association = reflection.build_association(serializer_instance, @instance_options)
241
+
242
+ evaluate_association_value(association)
243
+
244
+ assert_equal @expected_meta, association.meta
245
+ assert_equal @expected_meta, reflection.options.fetch(:meta)
246
+ end
247
+
248
+ def test_reflection_block_with_meta_block_mutates_the_reflection_meta
249
+ serializer_class = Class.new(ActiveModel::Serializer) do
250
+ has_one :blog do
251
+ meta do
252
+ { id: object.blog.id }
253
+ end
254
+ end
255
+ end
256
+ serializer_instance = serializer_class.new(@model, @instance_options)
257
+
258
+ # Get Reflection
259
+ reflection = serializer_class._reflections.fetch(:blog)
260
+ assert_nil reflection.options.fetch(:meta)
261
+
262
+ # Build Association
263
+ association = reflection.build_association(serializer_instance, @instance_options)
264
+ # Assert before instance_eval meta
265
+
266
+ evaluate_association_value(association)
267
+
268
+ assert_respond_to association.meta, :call
269
+ assert_respond_to reflection.options.fetch(:meta), :call
270
+
271
+ # Assert after instance_eval meta
272
+ assert_equal @expected_meta, reflection.instance_eval(&association.meta)
273
+ assert_respond_to reflection.options.fetch(:meta), :call
274
+ assert_respond_to association.meta, :call
275
+ end
276
+
277
+ # rubocop:disable Metrics/AbcSize
278
+ def test_reflection_block_with_meta_in_link_block_mutates_the_reflection_meta
279
+ serializer_class = Class.new(ActiveModel::Serializer) do
280
+ has_one :blog do
281
+ link :self do
282
+ meta(id: object.blog.id)
283
+ 'no_uri_validation'
284
+ end
285
+ end
286
+ end
287
+ serializer_instance = serializer_class.new(@model, @instance_options)
288
+
289
+ # Get Reflection
290
+ reflection = serializer_class._reflections.fetch(:blog)
291
+ assert_nil reflection.options.fetch(:meta)
292
+ assert_equal @empty_links, reflection.options.fetch(:links)
293
+
294
+ # Build Association
295
+ association = reflection.build_association(serializer_instance, @instance_options)
296
+ # Assert before instance_eval link meta
297
+ assert_nil association.meta
298
+ assert_nil reflection.options.fetch(:meta)
299
+
300
+ evaluate_association_value(association)
301
+
302
+ link = association.links.fetch(:self)
303
+ assert_respond_to link, :call
304
+ assert_respond_to reflection.options.fetch(:links).fetch(:self), :call
305
+ assert_nil reflection.options.fetch(:meta)
306
+
307
+ # Assert after instance_eval link
308
+ assert_equal 'no_uri_validation', reflection.instance_eval(&link)
309
+ assert_equal @expected_meta, reflection.options.fetch(:meta)
310
+ assert_equal @expected_meta, association.meta
311
+ end
312
+ # rubocop:enable Metrics/AbcSize
313
+
314
+ # rubocop:disable Metrics/AbcSize
315
+ def test_reflection_block_with_meta_block_in_link_block_mutates_the_reflection_meta
316
+ serializer_class = Class.new(ActiveModel::Serializer) do
317
+ has_one :blog do
318
+ link :self do
319
+ meta do
320
+ { id: object.blog.id }
321
+ end
322
+ 'no_uri_validation'
323
+ end
324
+ end
325
+ end
326
+ serializer_instance = serializer_class.new(@model, @instance_options)
327
+
328
+ # Get Reflection
329
+ reflection = serializer_class._reflections.fetch(:blog)
330
+ assert_nil reflection.options.fetch(:meta)
331
+
332
+ # Build Association
333
+ association = reflection.build_association(serializer_instance, @instance_options)
334
+ assert_nil association.meta
335
+ assert_nil reflection.options.fetch(:meta)
336
+
337
+ # Assert before instance_eval link
338
+
339
+ evaluate_association_value(association)
340
+
341
+ link = association.links.fetch(:self)
342
+ assert_nil reflection.options.fetch(:meta)
343
+ assert_respond_to link, :call
344
+ assert_respond_to association.links.fetch(:self), :call
345
+
346
+ # Assert after instance_eval link
347
+ assert_equal 'no_uri_validation', reflection.instance_eval(&link)
348
+ assert_respond_to association.links.fetch(:self), :call
349
+ # Assert before instance_eval link meta
350
+ assert_respond_to reflection.options.fetch(:meta), :call
351
+ assert_respond_to association.meta, :call
352
+
353
+ # Assert after instance_eval link meta
354
+ assert_equal @expected_meta, reflection.instance_eval(&reflection.options.fetch(:meta))
355
+ assert_respond_to association.meta, :call
356
+ end
357
+ # rubocop:enable Metrics/AbcSize
358
+
359
+ def test_no_href_in_vanilla_reflection
360
+ serializer_class = Class.new(ActiveModel::Serializer) do
361
+ has_one :blog do
362
+ link :self do
363
+ href 'no_uri_validation'
364
+ end
365
+ end
366
+ end
367
+ serializer_instance = serializer_class.new(@model, @instance_options)
368
+
369
+ # Get Reflection
370
+ reflection = serializer_class._reflections.fetch(:blog)
371
+ assert_equal @empty_links, reflection.options.fetch(:links)
372
+
373
+ # Build Association
374
+ association = reflection.build_association(serializer_instance, @instance_options)
375
+ # Assert before instance_eval link
376
+
377
+ evaluate_association_value(association)
378
+
379
+ link = association.links.fetch(:self)
380
+ assert_respond_to link, :call
381
+
382
+ # Assert after instance_eval link
383
+ exception = assert_raise(NoMethodError) do
384
+ reflection.instance_eval(&link)
385
+ end
386
+ assert_match(/undefined method `href'/, exception.message)
387
+ end
388
+
389
+ # rubocop:disable Metrics/AbcSize
390
+ def test_mutating_reflection_block_is_not_thread_safe
391
+ serializer_class = Class.new(ActiveModel::Serializer) do
392
+ has_one :blog do
393
+ meta(id: object.blog.id)
394
+ end
395
+ end
396
+ model1_meta = @expected_meta
397
+ # Evaluate reflection meta for model with id 1
398
+ serializer_instance = serializer_class.new(@model, @instance_options)
399
+ reflection = serializer_class._reflections.fetch(:blog)
400
+ assert_nil reflection.options.fetch(:meta)
401
+ association = reflection.build_association(serializer_instance, @instance_options)
402
+
403
+ evaluate_association_value(association)
404
+
405
+ assert_equal model1_meta, association.meta
406
+ assert_equal model1_meta, reflection.options.fetch(:meta)
407
+
408
+ model2_meta = @expected_meta.merge(id: 2)
409
+ # Evaluate reflection meta for model with id 2
410
+ @model.blog.id = 2
411
+ assert_equal 2, @model.blog.id # sanity check
412
+ serializer_instance = serializer_class.new(@model, @instance_options)
413
+ reflection = serializer_class._reflections.fetch(:blog)
414
+
415
+ # WARN: Thread-safety issue
416
+ # Before the reflection is evaluated, it has the value from the previous evaluation
417
+ assert_equal model1_meta, reflection.options.fetch(:meta)
418
+
419
+ association = reflection.build_association(serializer_instance, @instance_options)
420
+
421
+ evaluate_association_value(association)
422
+
423
+ assert_equal model2_meta, association.meta
424
+ assert_equal model2_meta, reflection.options.fetch(:meta)
425
+ end
426
+ # rubocop:enable Metrics/AbcSize
427
+ end
428
+ class ThreadedReflectionTest < ActiveSupport::TestCase
429
+ class Post < ::Model
430
+ attributes :id, :title, :body
431
+ associations :comments
432
+ end
433
+ class Comment < ::Model
434
+ attributes :id, :body
435
+ associations :post
436
+ end
437
+ class CommentSerializer < ActiveModel::Serializer
438
+ type 'comment'
439
+ attributes :id, :body
440
+ has_one :post
441
+ end
442
+ class PostSerializer < ActiveModel::Serializer
443
+ type 'post'
444
+ attributes :id, :title, :body
445
+ has_many :comments, serializer: CommentSerializer do
446
+ sleep 0.1
447
+ object.comments
448
+ end
449
+ end
450
+
451
+ # per https://github.com/rails-api/active_model_serializers/issues/2270
452
+ def test_concurrent_serialization
453
+ post1 = Post.new(id: 1, title: 'Post 1 Title', body: 'Post 1 Body')
454
+ post1.comments = [Comment.new(id: 1, body: 'Comment on Post 1', post: post1)]
455
+ post2 = Post.new(id: 2, title: 'Post 2 Title', body: 'Post 2 Body')
456
+ post2.comments = [Comment.new(id: 2, body: 'Comment on Post 2', post: post2)]
457
+ serialized_posts = {
458
+ first: Set.new,
459
+ second: Set.new
460
+ }
461
+ t1 = Thread.new do
462
+ 10.times do
463
+ serialized_posts[:first] << PostSerializer.new(post1, {}).to_json
464
+ end
465
+ end
466
+ t2 = Thread.new do
467
+ 10.times do
468
+ serialized_posts[:second] << PostSerializer.new(post2, {}).to_json
469
+ end
470
+ end
471
+ t1.join
472
+ t2.join
473
+ expected_first_post_serialization = '{"id":1,"title":"Post 1 Title","body":"Post 1 Body","comments":[{"id":1,"body":"Comment on Post 1"}]}'
474
+ expected_second_post_serialization = '{"id":2,"title":"Post 2 Title","body":"Post 2 Body","comments":[{"id":2,"body":"Comment on Post 2"}]}'
475
+
476
+ assert_equal [expected_second_post_serialization], serialized_posts[:second].to_a
477
+ assert_equal [expected_first_post_serialization], serialized_posts[:first].to_a
478
+ end
479
+ end
480
+ end
481
+ end