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,111 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # TODO: description of this class, and overview of how it's used
4
+ class IncludeTree
5
+ module Parsing
6
+ module_function
7
+
8
+ # Translates a comma separated list of dot separated paths (JSON API format) into a Hash.
9
+ #
10
+ # @example
11
+ # `'posts.author, posts.comments.upvotes, posts.comments.author'`
12
+ #
13
+ # would become
14
+ #
15
+ # `{ posts: { author: {}, comments: { author: {}, upvotes: {} } } }`.
16
+ #
17
+ # @param [String] included
18
+ # @return [Hash] a Hash representing the same tree structure
19
+ def include_string_to_hash(included)
20
+ # TODO: Needs comment walking through the process of what all this is doing.
21
+ included.delete(' ').split(',').reduce({}) do |hash, path|
22
+ include_tree = path.split('.').reverse_each.reduce({}) { |a, e| { e.to_sym => a } }
23
+ hash.deep_merge!(include_tree)
24
+ end
25
+ end
26
+
27
+ # Translates the arguments passed to the include option into a Hash. The format can be either
28
+ # a String (see #include_string_to_hash), an Array of Symbols and Hashes, or a mix of both.
29
+ #
30
+ # @example
31
+ # `posts: [:author, comments: [:author, :upvotes]]`
32
+ #
33
+ # would become
34
+ #
35
+ # `{ posts: { author: {}, comments: { author: {}, upvotes: {} } } }`.
36
+ #
37
+ # @example
38
+ # `[:author, :comments => [:author]]`
39
+ #
40
+ # would become
41
+ #
42
+ # `{:author => {}, :comments => { author: {} } }`
43
+ #
44
+ # @param [Symbol, Hash, Array, String] included
45
+ # @return [Hash] a Hash representing the same tree structure
46
+ def include_args_to_hash(included)
47
+ case included
48
+ when Symbol
49
+ { included => {} }
50
+ when Hash
51
+ included.each_with_object({}) do |(key, value), hash|
52
+ hash[key] = include_args_to_hash(value)
53
+ end
54
+ when Array
55
+ included.reduce({}) { |a, e| a.deep_merge!(include_args_to_hash(e)) }
56
+ when String
57
+ include_string_to_hash(included)
58
+ else
59
+ {}
60
+ end
61
+ end
62
+ end
63
+
64
+ # Builds an IncludeTree from a comma separated list of dot separated paths (JSON API format).
65
+ # @example `'posts.author, posts.comments.upvotes, posts.comments.author'`
66
+ #
67
+ # @param [String] included
68
+ # @return [IncludeTree]
69
+ #
70
+ def self.from_string(included)
71
+ new(Parsing.include_string_to_hash(included))
72
+ end
73
+
74
+ # Translates the arguments passed to the include option into an IncludeTree.
75
+ # The format can be either a String (see #from_string), an Array of Symbols and Hashes, or a mix of both.
76
+ # @example `posts: [:author, comments: [:author, :upvotes]]`
77
+ #
78
+ # @param [Symbol, Hash, Array, String] included
79
+ # @return [IncludeTree]
80
+ #
81
+ def self.from_include_args(included)
82
+ return included if included.is_a?(IncludeTree)
83
+
84
+ new(Parsing.include_args_to_hash(included))
85
+ end
86
+
87
+ # @param [Hash] hash
88
+ def initialize(hash = {})
89
+ @hash = hash
90
+ end
91
+
92
+ def key?(key)
93
+ @hash.key?(key) || @hash.key?(:*) || @hash.key?(:**)
94
+ end
95
+
96
+ def [](key)
97
+ # TODO(beauby): Adopt a lazy caching strategy for generating subtrees.
98
+ case
99
+ when @hash.key?(key)
100
+ self.class.new(@hash[key])
101
+ when @hash.key?(:*)
102
+ self.class.new(@hash[:*])
103
+ when @hash.key?(:**)
104
+ self.class.new(:** => {})
105
+ else
106
+ nil
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,35 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Links
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ with_options instance_writer: false, instance_reader: true do |serializer|
8
+ serializer.class_attribute :_links # @api private
9
+ self._links ||= {}
10
+ end
11
+
12
+ extend ActiveSupport::Autoload
13
+ end
14
+
15
+ module ClassMethods
16
+ def inherited(base)
17
+ super
18
+ base._links = _links.dup
19
+ end
20
+
21
+ # Define a link on a serializer.
22
+ # @example
23
+ # link(:self) { resource_url(object) }
24
+ # @example
25
+ # link(:self) { "http://example.com/resource/#{object.id}" }
26
+ # @example
27
+ # link :resource, "http://example.com/resource"
28
+ #
29
+ def link(name, value = nil, &block)
30
+ _links[name] = block || value
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,146 @@
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
29
+
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
44
+
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
59
+
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
72
+
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
84
+
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
97
+
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
108
+
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
121
+
122
+ def test_active_model_errors
123
+ assert_respond_to resource, :errors
124
+ end
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
130
+
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
135
+
136
+ private
137
+
138
+ def resource
139
+ @resource or fail "'@resource' must be set as the linted object"
140
+ end
141
+
142
+ def assert_instance_of(result, name)
143
+ assert result.instance_of?(name), "#{result} should be an instance of #{name}"
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Meta
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ with_options instance_writer: false, instance_reader: true do |serializer|
8
+ serializer.class_attribute :_meta # @api private
9
+ end
10
+
11
+ extend ActiveSupport::Autoload
12
+ end
13
+
14
+ module ClassMethods
15
+ # Set the JSON API meta attribute of a serializer.
16
+ # @example
17
+ # class AdminAuthorSerializer < ActiveModel::Serializer
18
+ # meta { stuff: 'value' }
19
+ # @example
20
+ # meta do
21
+ # { comment_count: object.comments.count }
22
+ # end
23
+ def meta(value = nil, &block)
24
+ self._meta = block || value
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Null < Serializer
4
+ def attributes(*)
5
+ {}
6
+ end
7
+
8
+ def associations(*)
9
+ {}
10
+ end
11
+
12
+ def serializable_hash(*)
13
+ {}
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,147 @@
1
+ require 'active_model/serializer/field'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ # Holds all the meta-data about an association as it was specified in the
6
+ # ActiveModel::Serializer class.
7
+ #
8
+ # @example
9
+ # class PostSerializer < ActiveModel::Serializer
10
+ # has_one :author, serializer: AuthorSerializer
11
+ # has_many :comments
12
+ # has_many :comments, key: :last_comments do
13
+ # object.comments.last(1)
14
+ # end
15
+ # has_many :secret_meta_data, if: :is_admin?
16
+ #
17
+ # def is_admin?
18
+ # current_user.admin?
19
+ # end
20
+ # end
21
+ #
22
+ # Specifically, the association 'comments' is evaluated two different ways:
23
+ # 1) as 'comments' and named 'comments'.
24
+ # 2) as 'object.comments.last(1)' and named 'last_comments'.
25
+ #
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
+ # # ]
33
+ #
34
+ # So you can inspect reflections in your Adapters.
35
+ #
36
+ class Reflection < Field
37
+ def initialize(*)
38
+ super
39
+ @_links = {}
40
+ @_include_data = true
41
+ @_meta = nil
42
+ end
43
+
44
+ def link(name, value = nil, &block)
45
+ @_links[name] = block || value
46
+ :nil
47
+ end
48
+
49
+ def meta(value = nil, &block)
50
+ @_meta = block || value
51
+ :nil
52
+ end
53
+
54
+ def include_data(value = true)
55
+ @_include_data = value
56
+ :nil
57
+ end
58
+
59
+ # @param serializer [ActiveModel::Serializer]
60
+ # @yield [ActiveModel::Serializer]
61
+ # @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)
73
+ @object = serializer.object
74
+ @scope = serializer.scope
75
+
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
83
+ else
84
+ serializer.read_attribute_for_serialization(name)
85
+ end
86
+ end
87
+
88
+ # Build association. This method is used internally to
89
+ # build serializer's association by its reflection.
90
+ #
91
+ # @param [Serializer] subject is a parent serializer for given association
92
+ # @param [Hash{Symbol => Object}] parent_serializer_options
93
+ #
94
+ # @example
95
+ # # Given the following serializer defined:
96
+ # class PostSerializer < ActiveModel::Serializer
97
+ # has_many :comments, serializer: CommentSummarySerializer
98
+ # end
99
+ #
100
+ # # Then you instantiate your serializer
101
+ # post_serializer = PostSerializer.new(post, foo: 'bar') #
102
+ # # to build association for comments you need to get reflection
103
+ # comments_reflection = PostSerializer._reflections.detect { |r| r.name == :comments }
104
+ # # and #build_association
105
+ # comments_reflection.build_association(post_serializer, foo: 'bar')
106
+ #
107
+ # @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)
129
+ end
130
+
131
+ protected
132
+
133
+ 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
+ end
146
+ end
147
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # @api private
4
+ class SingularReflection < Reflection
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Type
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ with_options instance_writer: false, instance_reader: true do |serializer|
8
+ serializer.class_attribute :_type # @api private
9
+ end
10
+
11
+ extend ActiveSupport::Autoload
12
+ end
13
+
14
+ module ClassMethods
15
+ # Set the JSON API type of a serializer.
16
+ # @example
17
+ # class AdminAuthorSerializer < ActiveModel::Serializer
18
+ # type 'authors'
19
+ def type(type)
20
+ self._type = type && type.to_s
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  module ActiveModel
2
2
  class Serializer
3
- VERSION = "0.8.3"
3
+ VERSION = '0.10.0'.freeze
4
4
  end
5
5
  end