active_model_serializers 0.10.0 → 0.10.9

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 (206) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +10 -5
  3. data/.travis.yml +41 -21
  4. data/CHANGELOG.md +200 -2
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +25 -4
  7. data/README.md +166 -28
  8. data/Rakefile +5 -32
  9. data/active_model_serializers.gemspec +23 -25
  10. data/appveyor.yml +10 -6
  11. data/bin/rubocop +38 -0
  12. data/docs/README.md +2 -1
  13. data/docs/general/adapters.md +35 -11
  14. data/docs/general/caching.md +7 -1
  15. data/docs/general/configuration_options.md +86 -1
  16. data/docs/general/deserialization.md +1 -1
  17. data/docs/general/fields.md +31 -0
  18. data/docs/general/getting_started.md +1 -1
  19. data/docs/general/logging.md +7 -0
  20. data/docs/general/rendering.md +63 -25
  21. data/docs/general/serializers.md +137 -14
  22. data/docs/howto/add_pagination_links.md +16 -17
  23. data/docs/howto/add_relationship_links.md +140 -0
  24. data/docs/howto/add_root_key.md +11 -0
  25. data/docs/howto/grape_integration.md +42 -0
  26. data/docs/howto/outside_controller_use.md +12 -4
  27. data/docs/howto/passing_arbitrary_options.md +2 -2
  28. data/docs/howto/serialize_poro.md +46 -5
  29. data/docs/howto/test.md +2 -0
  30. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  31. data/docs/integrations/ember-and-json-api.md +67 -32
  32. data/docs/jsonapi/schema.md +1 -1
  33. data/lib/action_controller/serialization.rb +15 -3
  34. data/lib/active_model/serializable_resource.rb +2 -0
  35. data/lib/active_model/serializer/adapter/attributes.rb +2 -0
  36. data/lib/active_model/serializer/adapter/base.rb +4 -0
  37. data/lib/active_model/serializer/adapter/json.rb +2 -0
  38. data/lib/active_model/serializer/adapter/json_api.rb +2 -0
  39. data/lib/active_model/serializer/adapter/null.rb +2 -0
  40. data/lib/active_model/serializer/adapter.rb +2 -0
  41. data/lib/active_model/serializer/array_serializer.rb +10 -5
  42. data/lib/active_model/serializer/association.rb +64 -10
  43. data/lib/active_model/serializer/attribute.rb +2 -0
  44. data/lib/active_model/serializer/belongs_to_reflection.rb +6 -3
  45. data/lib/active_model/serializer/collection_serializer.rb +39 -13
  46. data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +87 -116
  47. data/lib/active_model/serializer/error_serializer.rb +13 -7
  48. data/lib/active_model/serializer/errors_serializer.rb +27 -20
  49. data/lib/active_model/serializer/field.rb +2 -0
  50. data/lib/active_model/serializer/fieldset.rb +2 -0
  51. data/lib/active_model/serializer/has_many_reflection.rb +5 -3
  52. data/lib/active_model/serializer/has_one_reflection.rb +3 -4
  53. data/lib/active_model/serializer/lazy_association.rb +99 -0
  54. data/lib/active_model/serializer/link.rb +23 -0
  55. data/lib/active_model/serializer/lint.rb +136 -130
  56. data/lib/active_model/serializer/null.rb +2 -0
  57. data/lib/active_model/serializer/reflection.rb +132 -67
  58. data/lib/active_model/serializer/version.rb +3 -1
  59. data/lib/active_model/serializer.rb +308 -82
  60. data/lib/active_model_serializers/adapter/attributes.rb +5 -66
  61. data/lib/active_model_serializers/adapter/base.rb +41 -39
  62. data/lib/active_model_serializers/adapter/json.rb +2 -0
  63. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +4 -2
  64. data/lib/active_model_serializers/adapter/json_api/error.rb +2 -0
  65. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +2 -0
  66. data/lib/active_model_serializers/adapter/json_api/link.rb +3 -1
  67. data/lib/active_model_serializers/adapter/json_api/meta.rb +2 -0
  68. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +49 -21
  69. data/lib/active_model_serializers/adapter/json_api/relationship.rb +77 -23
  70. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +41 -10
  71. data/lib/active_model_serializers/adapter/json_api.rb +84 -65
  72. data/lib/active_model_serializers/adapter/null.rb +2 -0
  73. data/lib/active_model_serializers/adapter.rb +9 -1
  74. data/lib/active_model_serializers/callbacks.rb +2 -0
  75. data/lib/active_model_serializers/deprecate.rb +3 -2
  76. data/lib/active_model_serializers/deserialization.rb +4 -0
  77. data/lib/active_model_serializers/json_pointer.rb +2 -0
  78. data/lib/active_model_serializers/logging.rb +2 -0
  79. data/lib/active_model_serializers/lookup_chain.rb +82 -0
  80. data/lib/active_model_serializers/model.rb +111 -28
  81. data/lib/active_model_serializers/railtie.rb +7 -1
  82. data/lib/active_model_serializers/register_jsonapi_renderer.rb +46 -31
  83. data/lib/active_model_serializers/serializable_resource.rb +10 -7
  84. data/lib/active_model_serializers/serialization_context.rb +12 -3
  85. data/lib/active_model_serializers/test/schema.rb +4 -2
  86. data/lib/active_model_serializers/test/serializer.rb +2 -0
  87. data/lib/active_model_serializers/test.rb +2 -0
  88. data/lib/active_model_serializers.rb +35 -10
  89. data/lib/generators/rails/resource_override.rb +3 -1
  90. data/lib/generators/rails/serializer_generator.rb +6 -4
  91. data/lib/grape/active_model_serializers.rb +9 -5
  92. data/lib/grape/formatters/active_model_serializers.rb +21 -2
  93. data/lib/grape/helpers/active_model_serializers.rb +3 -0
  94. data/lib/tasks/rubocop.rake +55 -0
  95. data/test/action_controller/adapter_selector_test.rb +16 -5
  96. data/test/action_controller/explicit_serializer_test.rb +7 -4
  97. data/test/action_controller/json/include_test.rb +108 -27
  98. data/test/action_controller/json_api/deserialization_test.rb +3 -1
  99. data/test/action_controller/json_api/errors_test.rb +10 -9
  100. data/test/action_controller/json_api/fields_test.rb +68 -0
  101. data/test/action_controller/json_api/linked_test.rb +31 -24
  102. data/test/action_controller/json_api/pagination_test.rb +33 -23
  103. data/test/action_controller/json_api/transform_test.rb +13 -3
  104. data/test/action_controller/lookup_proc_test.rb +51 -0
  105. data/test/action_controller/namespace_lookup_test.rb +234 -0
  106. data/test/action_controller/serialization_scope_name_test.rb +14 -6
  107. data/test/action_controller/serialization_test.rb +23 -12
  108. data/test/active_model_serializers/adapter_for_test.rb +2 -0
  109. data/test/active_model_serializers/json_pointer_test.rb +17 -13
  110. data/test/active_model_serializers/logging_test.rb +2 -0
  111. data/test/active_model_serializers/model_test.rb +139 -4
  112. data/test/active_model_serializers/railtie_test_isolated.rb +14 -7
  113. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +163 -0
  114. data/test/active_model_serializers/serialization_context_test_isolated.rb +25 -10
  115. data/test/active_model_serializers/test/schema_test.rb +5 -2
  116. data/test/active_model_serializers/test/serializer_test.rb +2 -0
  117. data/test/active_record_test.rb +2 -0
  118. data/test/adapter/attributes_test.rb +42 -0
  119. data/test/adapter/deprecation_test.rb +2 -0
  120. data/test/adapter/json/belongs_to_test.rb +2 -0
  121. data/test/adapter/json/collection_test.rb +16 -0
  122. data/test/adapter/json/has_many_test.rb +12 -2
  123. data/test/adapter/json/transform_test.rb +17 -15
  124. data/test/adapter/json_api/belongs_to_test.rb +2 -0
  125. data/test/adapter/json_api/collection_test.rb +6 -3
  126. data/test/adapter/json_api/errors_test.rb +19 -19
  127. data/test/adapter/json_api/fields_test.rb +14 -3
  128. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +2 -0
  129. data/test/adapter/json_api/has_many_test.rb +51 -20
  130. data/test/adapter/json_api/has_one_test.rb +2 -0
  131. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +215 -0
  132. data/test/adapter/json_api/json_api_test.rb +7 -7
  133. data/test/adapter/json_api/linked_test.rb +35 -12
  134. data/test/adapter/json_api/links_test.rb +22 -3
  135. data/test/adapter/json_api/pagination_links_test.rb +55 -13
  136. data/test/adapter/json_api/parse_test.rb +3 -1
  137. data/test/adapter/json_api/relationship_test.rb +311 -73
  138. data/test/adapter/json_api/resource_meta_test.rb +5 -3
  139. data/test/adapter/json_api/toplevel_jsonapi_test.rb +2 -0
  140. data/test/adapter/json_api/transform_test.rb +265 -253
  141. data/test/adapter/json_api/type_test.rb +170 -36
  142. data/test/adapter/json_test.rb +10 -7
  143. data/test/adapter/null_test.rb +3 -2
  144. data/test/adapter/polymorphic_test.rb +54 -5
  145. data/test/adapter_test.rb +3 -1
  146. data/test/array_serializer_test.rb +2 -0
  147. data/test/benchmark/app.rb +3 -1
  148. data/test/benchmark/benchmarking_support.rb +3 -1
  149. data/test/benchmark/bm_active_record.rb +83 -0
  150. data/test/benchmark/bm_adapter.rb +40 -0
  151. data/test/benchmark/bm_caching.rb +18 -16
  152. data/test/benchmark/bm_lookup_chain.rb +85 -0
  153. data/test/benchmark/bm_transform.rb +23 -10
  154. data/test/benchmark/controllers.rb +18 -17
  155. data/test/benchmark/fixtures.rb +74 -72
  156. data/test/cache_test.rb +301 -69
  157. data/test/collection_serializer_test.rb +33 -14
  158. data/test/fixtures/active_record.rb +47 -10
  159. data/test/fixtures/poro.rb +128 -183
  160. data/test/generators/scaffold_controller_generator_test.rb +2 -0
  161. data/test/generators/serializer_generator_test.rb +25 -5
  162. data/test/grape_test.rb +172 -56
  163. data/test/lint_test.rb +3 -1
  164. data/test/logger_test.rb +15 -11
  165. data/test/poro_test.rb +2 -0
  166. data/test/serializable_resource_test.rb +20 -22
  167. data/test/serializers/association_macros_test.rb +5 -2
  168. data/test/serializers/associations_test.rb +274 -49
  169. data/test/serializers/attribute_test.rb +7 -3
  170. data/test/serializers/attributes_test.rb +3 -1
  171. data/test/serializers/caching_configuration_test_isolated.rb +8 -6
  172. data/test/serializers/configuration_test.rb +2 -0
  173. data/test/serializers/fieldset_test.rb +3 -1
  174. data/test/serializers/meta_test.rb +14 -6
  175. data/test/serializers/options_test.rb +19 -6
  176. data/test/serializers/read_attribute_for_serialization_test.rb +5 -3
  177. data/test/serializers/reflection_test.rb +481 -0
  178. data/test/serializers/root_test.rb +3 -1
  179. data/test/serializers/serialization_test.rb +4 -2
  180. data/test/serializers/serializer_for_test.rb +14 -10
  181. data/test/serializers/serializer_for_with_namespace_test.rb +90 -0
  182. data/test/support/isolated_unit.rb +11 -4
  183. data/test/support/rails5_shims.rb +10 -2
  184. data/test/support/rails_app.rb +4 -9
  185. data/test/support/serialization_testing.rb +33 -5
  186. data/test/test_helper.rb +15 -0
  187. metadata +126 -46
  188. data/.rubocop_todo.yml +0 -167
  189. data/docs/ARCHITECTURE.md +0 -126
  190. data/lib/active_model/serializer/associations.rb +0 -100
  191. data/lib/active_model/serializer/attributes.rb +0 -82
  192. data/lib/active_model/serializer/collection_reflection.rb +0 -7
  193. data/lib/active_model/serializer/configuration.rb +0 -35
  194. data/lib/active_model/serializer/include_tree.rb +0 -111
  195. data/lib/active_model/serializer/links.rb +0 -35
  196. data/lib/active_model/serializer/meta.rb +0 -29
  197. data/lib/active_model/serializer/singular_reflection.rb +0 -7
  198. data/lib/active_model/serializer/type.rb +0 -25
  199. data/lib/active_model_serializers/key_transform.rb +0 -70
  200. data/test/active_model_serializers/key_transform_test.rb +0 -263
  201. data/test/adapter/json_api/has_many_embed_ids_test.rb +0 -43
  202. data/test/adapter/json_api/relationships_test.rb +0 -199
  203. data/test/adapter/json_api/resource_identifier_test.rb +0 -85
  204. data/test/include_tree/from_include_args_test.rb +0 -26
  205. data/test/include_tree/from_string_test.rb +0 -94
  206. data/test/include_tree/include_args_to_hash_test.rb +0 -64
@@ -1,146 +1,152 @@
1
- module ActiveModel::Serializer::Lint
2
- # == Active \Model \Serializer \Lint \Tests
3
- #
4
- # You can test whether an object is compliant with the Active \Model \Serializers
5
- # API by including <tt>ActiveModel::Serializer::Lint::Tests</tt> in your TestCase.
6
- # It will include tests that tell you whether your object is fully compliant,
7
- # or if not, which aspects of the API are not implemented.
8
- #
9
- # Note an object is not required to implement all APIs in order to work
10
- # with Active \Model \Serializers. This module only intends to provide guidance in case
11
- # you want all features out of the box.
12
- #
13
- # These tests do not attempt to determine the semantic correctness of the
14
- # returned values. For instance, you could implement <tt>serializable_hash</tt> to
15
- # always return +{}+, and the tests would pass. It is up to you to ensure
16
- # that the values are semantically meaningful.
17
- module Tests
18
- # Passes if the object responds to <tt>serializable_hash</tt> and if it takes
19
- # zero or one arguments.
20
- # Fails otherwise.
21
- #
22
- # <tt>serializable_hash</tt> returns a hash representation of a object's attributes.
23
- # Typically, it is implemented by including ActiveModel::Serialization.
24
- def test_serializable_hash
25
- assert_respond_to resource, :serializable_hash, 'The resource should respond to serializable_hash'
26
- resource.serializable_hash
27
- resource.serializable_hash(nil)
28
- end
1
+ # frozen_string_literal: true
29
2
 
30
- # Passes if the object responds to <tt>read_attribute_for_serialization</tt>
31
- # and if it requires one argument (the attribute to be read).
32
- # Fails otherwise.
33
- #
34
- # <tt>read_attribute_for_serialization</tt> gets the attribute value for serialization
35
- # Typically, it is implemented by including ActiveModel::Serialization.
36
- def test_read_attribute_for_serialization
37
- assert_respond_to resource, :read_attribute_for_serialization, 'The resource should respond to read_attribute_for_serialization'
38
- actual_arity = resource.method(:read_attribute_for_serialization).arity
39
- # using absolute value since arity is:
40
- # 1 for def read_attribute_for_serialization(name); end
41
- # -1 for alias :read_attribute_for_serialization :send
42
- assert_equal 1, actual_arity.abs, "expected #{actual_arity.inspect}.abs to be 1 or -1"
43
- end
3
+ module ActiveModel
4
+ class Serializer
5
+ module Lint
6
+ # == Active \Model \Serializer \Lint \Tests
7
+ #
8
+ # You can test whether an object is compliant with the Active \Model \Serializers
9
+ # API by including <tt>ActiveModel::Serializer::Lint::Tests</tt> in your TestCase.
10
+ # It will include tests that tell you whether your object is fully compliant,
11
+ # or if not, which aspects of the API are not implemented.
12
+ #
13
+ # Note an object is not required to implement all APIs in order to work
14
+ # with Active \Model \Serializers. This module only intends to provide guidance in case
15
+ # you want all features out of the box.
16
+ #
17
+ # These tests do not attempt to determine the semantic correctness of the
18
+ # returned values. For instance, you could implement <tt>serializable_hash</tt> to
19
+ # always return +{}+, and the tests would pass. It is up to you to ensure
20
+ # that the values are semantically meaningful.
21
+ module Tests
22
+ # Passes if the object responds to <tt>serializable_hash</tt> and if it takes
23
+ # zero or one arguments.
24
+ # Fails otherwise.
25
+ #
26
+ # <tt>serializable_hash</tt> returns a hash representation of a object's attributes.
27
+ # Typically, it is implemented by including ActiveModel::Serialization.
28
+ def test_serializable_hash
29
+ assert_respond_to resource, :serializable_hash, 'The resource should respond to serializable_hash'
30
+ resource.serializable_hash
31
+ resource.serializable_hash(nil)
32
+ end
44
33
 
45
- # Passes if the object responds to <tt>as_json</tt> and if it takes
46
- # zero or one arguments.
47
- # Fails otherwise.
48
- #
49
- # <tt>as_json</tt> returns a hash representation of a serialized object.
50
- # It may delegate to <tt>serializable_hash</tt>
51
- # Typically, it is implemented either by including ActiveModel::Serialization
52
- # which includes ActiveModel::Serializers::JSON.
53
- # or by the JSON gem when required.
54
- def test_as_json
55
- assert_respond_to resource, :as_json
56
- resource.as_json
57
- resource.as_json(nil)
58
- end
34
+ # Passes if the object responds to <tt>read_attribute_for_serialization</tt>
35
+ # and if it requires one argument (the attribute to be read).
36
+ # Fails otherwise.
37
+ #
38
+ # <tt>read_attribute_for_serialization</tt> gets the attribute value for serialization
39
+ # Typically, it is implemented by including ActiveModel::Serialization.
40
+ def test_read_attribute_for_serialization
41
+ assert_respond_to resource, :read_attribute_for_serialization, 'The resource should respond to read_attribute_for_serialization'
42
+ actual_arity = resource.method(:read_attribute_for_serialization).arity
43
+ # using absolute value since arity is:
44
+ # 1 for def read_attribute_for_serialization(name); end
45
+ # -1 for alias :read_attribute_for_serialization :send
46
+ assert_equal 1, actual_arity.abs, "expected #{actual_arity.inspect}.abs to be 1 or -1"
47
+ end
59
48
 
60
- # Passes if the object responds to <tt>to_json</tt> and if it takes
61
- # zero or one arguments.
62
- # Fails otherwise.
63
- #
64
- # <tt>to_json</tt> returns a string representation (JSON) of a serialized object.
65
- # It may be called on the result of <tt>as_json</tt>.
66
- # Typically, it is implemented on all objects when the JSON gem is required.
67
- def test_to_json
68
- assert_respond_to resource, :to_json
69
- resource.to_json
70
- resource.to_json(nil)
71
- end
49
+ # Passes if the object responds to <tt>as_json</tt> and if it takes
50
+ # zero or one arguments.
51
+ # Fails otherwise.
52
+ #
53
+ # <tt>as_json</tt> returns a hash representation of a serialized object.
54
+ # It may delegate to <tt>serializable_hash</tt>
55
+ # Typically, it is implemented either by including ActiveModel::Serialization
56
+ # which includes ActiveModel::Serializers::JSON.
57
+ # or by the JSON gem when required.
58
+ def test_as_json
59
+ assert_respond_to resource, :as_json
60
+ resource.as_json
61
+ resource.as_json(nil)
62
+ end
72
63
 
73
- # Passes if the object responds to <tt>cache_key</tt>
74
- # Fails otherwise.
75
- #
76
- # <tt>cache_key</tt> returns a (self-expiring) unique key for the object,
77
- # and is part of the (self-expiring) cache_key, which is used by the
78
- # adapter. It is not required unless caching is enabled.
79
- def test_cache_key
80
- assert_respond_to resource, :cache_key
81
- actual_arity = resource.method(:cache_key).arity
82
- assert_includes [-1, 0], actual_arity, "expected #{actual_arity.inspect} to be 0 or -1"
83
- end
64
+ # Passes if the object responds to <tt>to_json</tt> and if it takes
65
+ # zero or one arguments.
66
+ # Fails otherwise.
67
+ #
68
+ # <tt>to_json</tt> returns a string representation (JSON) of a serialized object.
69
+ # It may be called on the result of <tt>as_json</tt>.
70
+ # Typically, it is implemented on all objects when the JSON gem is required.
71
+ def test_to_json
72
+ assert_respond_to resource, :to_json
73
+ resource.to_json
74
+ resource.to_json(nil)
75
+ end
84
76
 
85
- # Passes if the object responds to <tt>updated_at</tt> and if it takes no
86
- # arguments.
87
- # Fails otherwise.
88
- #
89
- # <tt>updated_at</tt> returns a Time object or iso8601 string and
90
- # is part of the (self-expiring) cache_key, which is used by the adapter.
91
- # It is not required unless caching is enabled.
92
- def test_updated_at
93
- assert_respond_to resource, :updated_at
94
- actual_arity = resource.method(:updated_at).arity
95
- assert_equal 0, actual_arity
96
- end
77
+ # Passes if the object responds to <tt>cache_key</tt>
78
+ # Fails otherwise.
79
+ #
80
+ # <tt>cache_key</tt> returns a (self-expiring) unique key for the object,
81
+ # and is part of the (self-expiring) cache_key, which is used by the
82
+ # adapter. It is not required unless caching is enabled.
83
+ def test_cache_key
84
+ assert_respond_to resource, :cache_key
85
+ actual_arity = resource.method(:cache_key).arity
86
+ assert_includes [-1, 0], actual_arity, "expected #{actual_arity.inspect} to be 0 or -1"
87
+ end
97
88
 
98
- # Passes if the object responds to <tt>id</tt> and if it takes no
99
- # arguments.
100
- # Fails otherwise.
101
- #
102
- # <tt>id</tt> returns a unique identifier for the object.
103
- # It is not required unless caching is enabled.
104
- def test_id
105
- assert_respond_to resource, :id
106
- assert_equal 0, resource.method(:id).arity
107
- end
89
+ # Passes if the object responds to <tt>updated_at</tt> and if it takes no
90
+ # arguments.
91
+ # Fails otherwise.
92
+ #
93
+ # <tt>updated_at</tt> returns a Time object or iso8601 string and
94
+ # is part of the (self-expiring) cache_key, which is used by the adapter.
95
+ # It is not required unless caching is enabled.
96
+ def test_updated_at
97
+ assert_respond_to resource, :updated_at
98
+ actual_arity = resource.method(:updated_at).arity
99
+ assert_equal 0, actual_arity
100
+ end
108
101
 
109
- # Passes if the object's class responds to <tt>model_name</tt> and if it
110
- # is in an instance of +ActiveModel::Name+.
111
- # Fails otherwise.
112
- #
113
- # <tt>model_name</tt> returns an ActiveModel::Name instance.
114
- # It is used by the serializer to identify the object's type.
115
- # It is not required unless caching is enabled.
116
- def test_model_name
117
- resource_class = resource.class
118
- assert_respond_to resource_class, :model_name
119
- assert_instance_of resource_class.model_name, ActiveModel::Name
120
- end
102
+ # Passes if the object responds to <tt>id</tt> and if it takes no
103
+ # arguments.
104
+ # Fails otherwise.
105
+ #
106
+ # <tt>id</tt> returns a unique identifier for the object.
107
+ # It is not required unless caching is enabled.
108
+ def test_id
109
+ assert_respond_to resource, :id
110
+ assert_equal 0, resource.method(:id).arity
111
+ end
121
112
 
122
- def test_active_model_errors
123
- assert_respond_to resource, :errors
124
- end
113
+ # Passes if the object's class responds to <tt>model_name</tt> and if it
114
+ # is in an instance of +ActiveModel::Name+.
115
+ # Fails otherwise.
116
+ #
117
+ # <tt>model_name</tt> returns an ActiveModel::Name instance.
118
+ # It is used by the serializer to identify the object's type.
119
+ # It is not required unless caching is enabled.
120
+ def test_model_name
121
+ resource_class = resource.class
122
+ assert_respond_to resource_class, :model_name
123
+ assert_instance_of resource_class.model_name, ActiveModel::Name
124
+ end
125
125
 
126
- def test_active_model_errors_human_attribute_name
127
- assert_respond_to resource.class, :human_attribute_name
128
- assert_equal(-2, resource.class.method(:human_attribute_name).arity)
129
- end
126
+ def test_active_model_errors
127
+ assert_respond_to resource, :errors
128
+ end
130
129
 
131
- def test_active_model_errors_lookup_ancestors
132
- assert_respond_to resource.class, :lookup_ancestors
133
- assert_equal 0, resource.class.method(:lookup_ancestors).arity
134
- end
130
+ def test_active_model_errors_human_attribute_name
131
+ assert_respond_to resource.class, :human_attribute_name
132
+ assert_equal(-2, resource.class.method(:human_attribute_name).arity)
133
+ end
135
134
 
136
- private
135
+ def test_active_model_errors_lookup_ancestors
136
+ assert_respond_to resource.class, :lookup_ancestors
137
+ assert_equal 0, resource.class.method(:lookup_ancestors).arity
138
+ end
137
139
 
138
- def resource
139
- @resource or fail "'@resource' must be set as the linted object"
140
- end
140
+ private
141
+
142
+ def resource
143
+ @resource or fail "'@resource' must be set as the linted object"
144
+ end
141
145
 
142
- def assert_instance_of(result, name)
143
- assert result.instance_of?(name), "#{result} should be an instance of #{name}"
146
+ def assert_instance_of(result, name)
147
+ assert result.instance_of?(name), "#{result} should be an instance of #{name}"
148
+ end
149
+ end
144
150
  end
145
151
  end
146
152
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  class Serializer
3
5
  class Null < Serializer
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_model/serializer/field'
4
+ require 'active_model/serializer/association'
2
5
 
3
6
  module ActiveModel
4
7
  class Serializer
@@ -8,12 +11,26 @@ module ActiveModel
8
11
  # @example
9
12
  # class PostSerializer < ActiveModel::Serializer
10
13
  # has_one :author, serializer: AuthorSerializer
14
+ # belongs_to :boss, type: :users, foreign_key: :boss_id
11
15
  # has_many :comments
12
16
  # has_many :comments, key: :last_comments do
13
17
  # object.comments.last(1)
14
18
  # end
15
19
  # has_many :secret_meta_data, if: :is_admin?
16
20
  #
21
+ # has_one :blog do |serializer|
22
+ # meta count: object.roles.count
23
+ # serializer.cached_blog
24
+ # end
25
+ #
26
+ # private
27
+ #
28
+ # def cached_blog
29
+ # cache_store.fetch("cached_blog:#{object.updated_at}") do
30
+ # Blog.find(object.blog_id)
31
+ # end
32
+ # end
33
+ #
17
34
  # def is_admin?
18
35
  # current_user.admin?
19
36
  # end
@@ -23,72 +40,144 @@ module ActiveModel
23
40
  # 1) as 'comments' and named 'comments'.
24
41
  # 2) as 'object.comments.last(1)' and named 'last_comments'.
25
42
  #
26
- # PostSerializer._reflections #=>
27
- # # [
28
- # # HasOneReflection.new(:author, serializer: AuthorSerializer),
29
- # # HasManyReflection.new(:comments)
30
- # # HasManyReflection.new(:comments, { key: :last_comments }, #<Block>)
31
- # # HasManyReflection.new(:secret_meta_data, { if: :is_admin? })
32
- # # ]
43
+ # PostSerializer._reflections # =>
44
+ # # {
45
+ # # author: HasOneReflection.new(:author, serializer: AuthorSerializer),
46
+ # # comments: HasManyReflection.new(:comments)
47
+ # # last_comments: HasManyReflection.new(:comments, { key: :last_comments }, #<Block>)
48
+ # # secret_meta_data: HasManyReflection.new(:secret_meta_data, { if: :is_admin? })
49
+ # # }
33
50
  #
34
51
  # So you can inspect reflections in your Adapters.
35
- #
36
52
  class Reflection < Field
53
+ attr_reader :foreign_key, :type
54
+
37
55
  def initialize(*)
38
56
  super
39
- @_links = {}
40
- @_include_data = true
41
- @_meta = nil
57
+ options[:links] = {}
58
+ options[:include_data_setting] = Serializer.config.include_data_default
59
+ options[:meta] = nil
60
+ @type = options.fetch(:type) do
61
+ class_name = options.fetch(:class_name, name.to_s.camelize.singularize)
62
+ class_name.underscore.pluralize.to_sym
63
+ end
64
+ @foreign_key = options.fetch(:foreign_key) do
65
+ if collection?
66
+ "#{name.to_s.singularize}_ids".to_sym
67
+ else
68
+ "#{name}_id".to_sym
69
+ end
70
+ end
42
71
  end
43
72
 
44
- def link(name, value = nil, &block)
45
- @_links[name] = block || value
73
+ # @api public
74
+ # @example
75
+ # has_one :blog do
76
+ # include_data false
77
+ # link :self, 'a link'
78
+ # link :related, 'another link'
79
+ # link :self, '//example.com/link_author/relationships/bio'
80
+ # id = object.profile.id
81
+ # link :related do
82
+ # "//example.com/profiles/#{id}" if id != 123
83
+ # end
84
+ # link :related do
85
+ # ids = object.likes.map(&:id).join(',')
86
+ # href "//example.com/likes/#{ids}"
87
+ # meta ids: ids
88
+ # end
89
+ # end
90
+ def link(name, value = nil)
91
+ options[:links][name] = block_given? ? Proc.new : value
46
92
  :nil
47
93
  end
48
94
 
49
- def meta(value = nil, &block)
50
- @_meta = block || value
95
+ # @api public
96
+ # @example
97
+ # has_one :blog do
98
+ # include_data false
99
+ # meta(id: object.blog.id)
100
+ # meta liked: object.likes.any?
101
+ # link :self do
102
+ # href object.blog.id.to_s
103
+ # meta(id: object.blog.id)
104
+ # end
105
+ def meta(value = nil)
106
+ options[:meta] = block_given? ? Proc.new : value
51
107
  :nil
52
108
  end
53
109
 
110
+ # @api public
111
+ # @example
112
+ # has_one :blog do
113
+ # include_data false
114
+ # link :self, 'a link'
115
+ # link :related, 'another link'
116
+ # end
117
+ #
118
+ # has_one :blog do
119
+ # include_data false
120
+ # link :self, 'a link'
121
+ # link :related, 'another link'
122
+ # end
123
+ #
124
+ # belongs_to :reviewer do
125
+ # meta name: 'Dan Brown'
126
+ # include_data true
127
+ # end
128
+ #
129
+ # has_many :tags, serializer: TagSerializer do
130
+ # link :self, '//example.com/link_author/relationships/tags'
131
+ # include_data :if_sideloaded
132
+ # end
54
133
  def include_data(value = true)
55
- @_include_data = value
134
+ options[:include_data_setting] = value
56
135
  :nil
57
136
  end
58
137
 
138
+ def collection?
139
+ false
140
+ end
141
+
142
+ def include_data?(include_slice)
143
+ include_data_setting = options[:include_data_setting]
144
+ case include_data_setting
145
+ when :if_sideloaded then include_slice.key?(options.fetch(:key, name))
146
+ when true then true
147
+ when false then false
148
+ else fail ArgumentError, "Unknown include_data_setting '#{include_data_setting.inspect}'"
149
+ end
150
+ end
151
+
59
152
  # @param serializer [ActiveModel::Serializer]
60
153
  # @yield [ActiveModel::Serializer]
61
154
  # @return [:nil, associated resource or resource collection]
62
- # @example
63
- # has_one :blog do |serializer|
64
- # serializer.cached_blog
65
- # end
66
- #
67
- # def cached_blog
68
- # cache_store.fetch("cached_blog:#{object.updated_at}") do
69
- # Blog.find(object.blog_id)
70
- # end
71
- # end
72
- def value(serializer)
155
+ def value(serializer, include_slice)
156
+ # NOTE(BF): This method isn't thread-safe because the _reflections class attribute is not thread-safe
157
+ # Therefore, when we build associations from reflections, we dup the entire reflection instance.
158
+ # Better solutions much appreciated!
73
159
  @object = serializer.object
74
160
  @scope = serializer.scope
75
161
 
76
- if block
77
- block_value = instance_exec(serializer, &block)
78
- if block_value == :nil
79
- serializer.read_attribute_for_serialization(name)
80
- else
81
- block_value
82
- end
162
+ block_value = instance_exec(serializer, &block) if block
163
+ return unless include_data?(include_slice)
164
+
165
+ if block && block_value != :nil
166
+ block_value
83
167
  else
84
168
  serializer.read_attribute_for_serialization(name)
85
169
  end
86
170
  end
87
171
 
172
+ # @api private
173
+ def foreign_key_on
174
+ :related
175
+ end
176
+
88
177
  # Build association. This method is used internally to
89
178
  # build serializer's association by its reflection.
90
179
  #
91
- # @param [Serializer] subject is a parent serializer for given association
180
+ # @param [Serializer] parent_serializer for given association
92
181
  # @param [Hash{Symbol => Object}] parent_serializer_options
93
182
  #
94
183
  # @example
@@ -105,43 +194,19 @@ module ActiveModel
105
194
  # comments_reflection.build_association(post_serializer, foo: 'bar')
106
195
  #
107
196
  # @api private
108
- #
109
- def build_association(subject, parent_serializer_options)
110
- association_value = value(subject)
111
- reflection_options = options.dup
112
- serializer_class = subject.class.serializer_for(association_value, reflection_options)
113
- reflection_options[:include_data] = @_include_data
114
-
115
- if serializer_class
116
- begin
117
- serializer = serializer_class.new(
118
- association_value,
119
- serializer_options(subject, parent_serializer_options, reflection_options)
120
- )
121
- rescue ActiveModel::Serializer::CollectionSerializer::NoSerializerError
122
- reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
123
- end
124
- elsif !association_value.nil? && !association_value.instance_of?(Object)
125
- reflection_options[:virtual_value] = association_value
126
- end
127
-
128
- Association.new(name, serializer, reflection_options, @_links, @_meta)
197
+ def build_association(parent_serializer, parent_serializer_options, include_slice = {})
198
+ association_options = {
199
+ parent_serializer: parent_serializer,
200
+ parent_serializer_options: parent_serializer_options,
201
+ include_slice: include_slice
202
+ }
203
+ Association.new(self, association_options)
129
204
  end
130
205
 
131
206
  protected
132
207
 
208
+ # used in instance exec
133
209
  attr_accessor :object, :scope
134
-
135
- private
136
-
137
- def serializer_options(subject, parent_serializer_options, reflection_options)
138
- serializer = reflection_options.fetch(:serializer, nil)
139
-
140
- serializer_options = parent_serializer_options.except(:serializer)
141
- serializer_options[:serializer] = serializer if serializer
142
- serializer_options[:serializer_context_class] = subject.class
143
- serializer_options
144
- end
145
210
  end
146
211
  end
147
212
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  class Serializer
3
- VERSION = '0.10.0'.freeze
5
+ VERSION = '0.10.9'.freeze
4
6
  end
5
7
  end