active_model_serializers 0.8.3 → 0.10.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.rubocop.yml +86 -0
  4. data/.rubocop_todo.yml +240 -0
  5. data/.simplecov +111 -0
  6. data/.travis.yml +33 -22
  7. data/CHANGELOG.md +358 -6
  8. data/CONTRIBUTING.md +220 -0
  9. data/Gemfile +46 -1
  10. data/{MIT-LICENSE.txt → LICENSE.txt} +3 -2
  11. data/README.md +81 -591
  12. data/Rakefile +68 -11
  13. data/active_model_serializers.gemspec +57 -23
  14. data/appveyor.yml +27 -0
  15. data/docs/ARCHITECTURE.md +120 -0
  16. data/docs/DESIGN.textile +8 -0
  17. data/docs/README.md +35 -0
  18. data/docs/general/adapters.md +162 -0
  19. data/docs/general/caching.md +52 -0
  20. data/docs/general/configuration_options.md +27 -0
  21. data/docs/general/getting_started.md +98 -0
  22. data/docs/general/instrumentation.md +40 -0
  23. data/docs/general/logging.md +14 -0
  24. data/docs/general/rendering.md +153 -0
  25. data/docs/general/serializers.md +207 -0
  26. data/docs/how-open-source-maintained.jpg +0 -0
  27. data/docs/howto/add_pagination_links.md +121 -0
  28. data/docs/howto/add_root_key.md +51 -0
  29. data/docs/howto/outside_controller_use.md +58 -0
  30. data/docs/howto/test.md +152 -0
  31. data/docs/integrations/ember-and-json-api.md +112 -0
  32. data/docs/integrations/grape.md +19 -0
  33. data/docs/jsonapi/schema/schema.json +366 -0
  34. data/docs/jsonapi/schema.md +140 -0
  35. data/lib/action_controller/serialization.rb +41 -37
  36. data/lib/active_model/serializable_resource.rb +72 -0
  37. data/lib/active_model/serializer/adapter/attributes.rb +66 -0
  38. data/lib/active_model/serializer/adapter/base.rb +58 -0
  39. data/lib/active_model/serializer/adapter/cached_serializer.rb +45 -0
  40. data/lib/active_model/serializer/adapter/fragment_cache.rb +111 -0
  41. data/lib/active_model/serializer/adapter/json/fragment_cache.rb +13 -0
  42. data/lib/active_model/serializer/adapter/json.rb +21 -0
  43. data/lib/active_model/serializer/adapter/json_api/deserialization.rb +207 -0
  44. data/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +21 -0
  45. data/lib/active_model/serializer/adapter/json_api/link.rb +44 -0
  46. data/lib/active_model/serializer/adapter/json_api/pagination_links.rb +58 -0
  47. data/lib/active_model/serializer/adapter/json_api.rb +223 -0
  48. data/lib/active_model/serializer/adapter/null.rb +11 -0
  49. data/lib/active_model/serializer/adapter.rb +91 -0
  50. data/lib/active_model/serializer/array_serializer.rb +9 -0
  51. data/lib/active_model/serializer/association.rb +20 -0
  52. data/lib/active_model/serializer/associations.rb +87 -220
  53. data/lib/active_model/serializer/attribute.rb +25 -0
  54. data/lib/active_model/serializer/attributes.rb +82 -0
  55. data/lib/active_model/serializer/belongs_to_reflection.rb +10 -0
  56. data/lib/active_model/serializer/caching.rb +100 -0
  57. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  58. data/lib/active_model/serializer/collection_serializer.rb +47 -0
  59. data/lib/active_model/serializer/configuration.rb +28 -0
  60. data/lib/active_model/serializer/field.rb +56 -0
  61. data/lib/active_model/serializer/fieldset.rb +31 -0
  62. data/lib/active_model/serializer/has_many_reflection.rb +10 -0
  63. data/lib/active_model/serializer/has_one_reflection.rb +10 -0
  64. data/lib/active_model/serializer/include_tree.rb +111 -0
  65. data/lib/active_model/serializer/links.rb +33 -0
  66. data/lib/active_model/serializer/lint.rb +142 -0
  67. data/lib/active_model/serializer/reflection.rb +91 -0
  68. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  69. data/lib/active_model/serializer/type.rb +25 -0
  70. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  71. data/lib/active_model/serializer.rb +99 -479
  72. data/lib/active_model_serializers/callbacks.rb +55 -0
  73. data/lib/active_model_serializers/deserialization.rb +13 -0
  74. data/lib/active_model_serializers/logging.rb +119 -0
  75. data/lib/active_model_serializers/model.rb +39 -0
  76. data/lib/active_model_serializers/railtie.rb +38 -0
  77. data/lib/active_model_serializers/serialization_context.rb +10 -0
  78. data/lib/active_model_serializers/test/schema.rb +103 -0
  79. data/lib/active_model_serializers/test/serializer.rb +125 -0
  80. data/lib/active_model_serializers/test.rb +7 -0
  81. data/lib/active_model_serializers.rb +20 -92
  82. data/lib/generators/rails/USAGE +6 -0
  83. data/lib/generators/rails/resource_override.rb +10 -0
  84. data/lib/generators/rails/serializer_generator.rb +36 -0
  85. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  86. data/lib/grape/active_model_serializers.rb +14 -0
  87. data/lib/grape/formatters/active_model_serializers.rb +15 -0
  88. data/lib/grape/helpers/active_model_serializers.rb +16 -0
  89. data/test/action_controller/adapter_selector_test.rb +53 -0
  90. data/test/action_controller/explicit_serializer_test.rb +134 -0
  91. data/test/action_controller/json/include_test.rb +167 -0
  92. data/test/action_controller/json_api/deserialization_test.rb +59 -0
  93. data/test/action_controller/json_api/linked_test.rb +196 -0
  94. data/test/action_controller/json_api/pagination_test.rb +116 -0
  95. data/test/{serialization_scope_name_test.rb → action_controller/serialization_scope_name_test.rb} +11 -15
  96. data/test/action_controller/serialization_test.rb +435 -0
  97. data/test/active_model_serializers/logging_test.rb +77 -0
  98. data/test/active_model_serializers/model_test.rb +9 -0
  99. data/test/active_model_serializers/railtie_test_isolated.rb +57 -0
  100. data/test/active_model_serializers/serialization_context_test.rb +18 -0
  101. data/test/active_model_serializers/test/schema_test.rb +128 -0
  102. data/test/active_model_serializers/test/serializer_test.rb +63 -0
  103. data/test/active_record_test.rb +9 -0
  104. data/test/adapter/fragment_cache_test.rb +38 -0
  105. data/test/adapter/json/belongs_to_test.rb +47 -0
  106. data/test/adapter/json/collection_test.rb +92 -0
  107. data/test/adapter/json/has_many_test.rb +47 -0
  108. data/test/adapter/json_api/belongs_to_test.rb +157 -0
  109. data/test/adapter/json_api/collection_test.rb +97 -0
  110. data/test/adapter/json_api/fields_test.rb +89 -0
  111. data/test/adapter/json_api/has_many_embed_ids_test.rb +45 -0
  112. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
  113. data/test/adapter/json_api/has_many_test.rb +145 -0
  114. data/test/adapter/json_api/has_one_test.rb +81 -0
  115. data/test/adapter/json_api/json_api_test.rb +37 -0
  116. data/test/adapter/json_api/linked_test.rb +394 -0
  117. data/test/adapter/json_api/links_test.rb +68 -0
  118. data/test/adapter/json_api/pagination_links_test.rb +115 -0
  119. data/test/adapter/json_api/parse_test.rb +139 -0
  120. data/test/adapter/json_api/resource_type_config_test.rb +71 -0
  121. data/test/adapter/json_api/toplevel_jsonapi_test.rb +84 -0
  122. data/test/adapter/json_test.rb +47 -0
  123. data/test/adapter/null_test.rb +25 -0
  124. data/test/adapter_test.rb +42 -0
  125. data/test/array_serializer_test.rb +36 -73
  126. data/test/collection_serializer_test.rb +100 -0
  127. data/test/fixtures/active_record.rb +56 -0
  128. data/test/fixtures/poro.rb +229 -0
  129. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  130. data/test/generators/serializer_generator_test.rb +57 -0
  131. data/test/grape_test.rb +82 -0
  132. data/test/include_tree/from_include_args_test.rb +26 -0
  133. data/test/include_tree/from_string_test.rb +94 -0
  134. data/test/include_tree/include_args_to_hash_test.rb +64 -0
  135. data/test/lint_test.rb +40 -0
  136. data/test/logger_test.rb +18 -0
  137. data/test/poro_test.rb +9 -0
  138. data/test/serializable_resource_test.rb +27 -0
  139. data/test/serializers/adapter_for_test.rb +166 -0
  140. data/test/serializers/association_macros_test.rb +36 -0
  141. data/test/serializers/associations_test.rb +267 -0
  142. data/test/serializers/attribute_test.rb +123 -0
  143. data/test/serializers/attributes_test.rb +52 -0
  144. data/test/serializers/cache_test.rb +209 -0
  145. data/test/serializers/configuration_test.rb +32 -0
  146. data/test/serializers/fieldset_test.rb +14 -0
  147. data/test/serializers/meta_test.rb +130 -0
  148. data/test/serializers/options_test.rb +21 -0
  149. data/test/serializers/root_test.rb +21 -0
  150. data/test/serializers/serializer_for_test.rb +134 -0
  151. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  152. data/test/support/isolated_unit.rb +77 -0
  153. data/test/support/rails5_shims.rb +29 -0
  154. data/test/support/rails_app.rb +25 -0
  155. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  156. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  157. data/test/support/schemas/custom/show.json +7 -0
  158. data/test/support/schemas/hyper_schema.json +93 -0
  159. data/test/support/schemas/render_using_json_api.json +43 -0
  160. data/test/support/schemas/simple_json_pointers.json +10 -0
  161. data/test/support/serialization_testing.rb +53 -0
  162. data/test/support/simplecov.rb +6 -0
  163. data/test/support/stream_capture.rb +50 -0
  164. data/test/support/test_case.rb +19 -0
  165. data/test/test_helper.rb +55 -24
  166. metadata +358 -42
  167. data/DESIGN.textile +0 -586
  168. data/Gemfile.edge +0 -9
  169. data/bench/perf.rb +0 -43
  170. data/cruft.md +0 -19
  171. data/lib/active_model/array_serializer.rb +0 -104
  172. data/lib/active_record/serializer_override.rb +0 -16
  173. data/lib/generators/resource_override.rb +0 -13
  174. data/lib/generators/serializer/USAGE +0 -9
  175. data/lib/generators/serializer/serializer_generator.rb +0 -42
  176. data/lib/generators/serializer/templates/serializer.rb +0 -19
  177. data/test/association_test.rb +0 -592
  178. data/test/caching_test.rb +0 -96
  179. data/test/generators_test.rb +0 -85
  180. data/test/no_serialization_scope_test.rb +0 -34
  181. data/test/serialization_test.rb +0 -392
  182. data/test/serializer_support_test.rb +0 -51
  183. data/test/serializer_test.rb +0 -1465
  184. data/test/test_fakes.rb +0 -217
@@ -0,0 +1,31 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Fieldset
4
+ def initialize(fields)
5
+ @raw_fields = fields || {}
6
+ end
7
+
8
+ def fields
9
+ @fields ||= parsed_fields
10
+ end
11
+
12
+ def fields_for(type)
13
+ fields[type.singularize.to_sym] || fields[type.pluralize.to_sym]
14
+ end
15
+
16
+ protected
17
+
18
+ attr_reader :raw_fields
19
+
20
+ private
21
+
22
+ def parsed_fields
23
+ if raw_fields.is_a?(Hash)
24
+ raw_fields.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.map(&:to_sym) }
25
+ else
26
+ {}
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # @api private
4
+ class HasManyReflection < CollectionReflection
5
+ def macro
6
+ :has_many
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # @api private
4
+ class HasOneReflection < SingularReflection
5
+ def macro
6
+ :has_one
7
+ end
8
+ end
9
+ end
10
+ end
@@ -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,33 @@
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 { "//example.com/posts/#{object.id}" }
24
+ # @example
25
+ # link :self, "//example.com/user"
26
+ #
27
+ def link(name, value = nil, &block)
28
+ _links[name] = block || value
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,142 @@
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
+ if defined?(::Rubinius)
40
+ # 1 for def read_attribute_for_serialization(name); end
41
+ # -2 for alias :read_attribute_for_serialization :send for rbx because :shrug:
42
+ assert_includes [1, -2], actual_arity, "expected #{actual_arity.inspect} to be 1 or -2"
43
+ else
44
+ # using absolute value since arity is:
45
+ # 1 for def read_attribute_for_serialization(name); end
46
+ # -1 for alias :read_attribute_for_serialization :send
47
+ assert_includes [1, -1], actual_arity, "expected #{actual_arity.inspect} to be 1 or -1"
48
+ end
49
+ end
50
+
51
+ # Passes if the object responds to <tt>as_json</tt> and if it takes
52
+ # zero or one arguments.
53
+ # Fails otherwise.
54
+ #
55
+ # <tt>as_json</tt> returns a hash representation of a serialized object.
56
+ # It may delegate to <tt>serializable_hash</tt>
57
+ # Typically, it is implemented either by including ActiveModel::Serialization
58
+ # which includes ActiveModel::Serializers::JSON.
59
+ # or by the JSON gem when required.
60
+ def test_as_json
61
+ assert_respond_to resource, :as_json
62
+ resource.as_json
63
+ resource.as_json(nil)
64
+ end
65
+
66
+ # Passes if the object responds to <tt>to_json</tt> and if it takes
67
+ # zero or one arguments.
68
+ # Fails otherwise.
69
+ #
70
+ # <tt>to_json</tt> returns a string representation (JSON) of a serialized object.
71
+ # It may be called on the result of <tt>as_json</tt>.
72
+ # Typically, it is implemented on all objects when the JSON gem is required.
73
+ def test_to_json
74
+ assert_respond_to resource, :to_json
75
+ resource.to_json
76
+ resource.to_json(nil)
77
+ end
78
+
79
+ # Passes if the object responds to <tt>cache_key</tt> and if it takes no
80
+ # arguments (Rails 4.0) or a splat (Rails 4.1+).
81
+ # Fails otherwise.
82
+ #
83
+ # <tt>cache_key</tt> returns a (self-expiring) unique key for the object, and
84
+ # is part of the (self-expiring) cache_key, which is used by the adapter.
85
+ # It is not required unless caching is enabled.
86
+ def test_cache_key
87
+ assert_respond_to resource, :cache_key
88
+ actual_arity = resource.method(:cache_key).arity
89
+ # using absolute value since arity is:
90
+ # 0 for Rails 4.1+, *timestamp_names
91
+ # -1 for Rails 4.0, no arguments
92
+ assert_includes [-1, 0], actual_arity, "expected #{actual_arity.inspect} to be 0 or -1"
93
+ end
94
+
95
+ # Passes if the object responds to <tt>updated_at</tt> and if it takes no
96
+ # arguments.
97
+ # Fails otherwise.
98
+ #
99
+ # <tt>updated_at</tt> returns a Time object or iso8601 string and
100
+ # is part of the (self-expiring) cache_key, which is used by the adapter.
101
+ # It is not required unless caching is enabled.
102
+ def test_updated_at
103
+ assert_respond_to resource, :updated_at
104
+ actual_arity = resource.method(:updated_at).arity
105
+ assert_equal 0, actual_arity
106
+ end
107
+
108
+ # Passes if the object responds to <tt>id</tt> and if it takes no
109
+ # arguments.
110
+ # Fails otherwise.
111
+ #
112
+ # <tt>id</tt> returns a unique identifier for the object.
113
+ # It is not required unless caching is enabled.
114
+ def test_id
115
+ assert_respond_to resource, :id
116
+ assert_equal 0, resource.method(:id).arity
117
+ end
118
+
119
+ # Passes if the object's class responds to <tt>model_name</tt> and if it
120
+ # is in an instance of +ActiveModel::Name+.
121
+ # Fails otherwise.
122
+ #
123
+ # <tt>model_name</tt> returns an ActiveModel::Name instance.
124
+ # It is used by the serializer to identify the object's type.
125
+ # It is not required unless caching is enabled.
126
+ def test_model_name
127
+ resource_class = resource.class
128
+ assert_respond_to resource_class, :model_name
129
+ assert_instance_of resource_class.model_name, ActiveModel::Name
130
+ end
131
+
132
+ private
133
+
134
+ def resource
135
+ @resource or fail "'@resource' must be set as the linted object"
136
+ end
137
+
138
+ def assert_instance_of(result, name)
139
+ assert result.instance_of?(name), "#{result} should be an instance of #{name}"
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,91 @@
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
+ # Build association. This method is used internally to
38
+ # build serializer's association by its reflection.
39
+ #
40
+ # @param [Serializer] subject is a parent serializer for given association
41
+ # @param [Hash{Symbol => Object}] parent_serializer_options
42
+ #
43
+ # @example
44
+ # # Given the following serializer defined:
45
+ # class PostSerializer < ActiveModel::Serializer
46
+ # has_many :comments, serializer: CommentSummarySerializer
47
+ # end
48
+ #
49
+ # # Then you instantiate your serializer
50
+ # post_serializer = PostSerializer.new(post, foo: 'bar') #
51
+ # # to build association for comments you need to get reflection
52
+ # comments_reflection = PostSerializer._reflections.detect { |r| r.name == :comments }
53
+ # # and #build_association
54
+ # comments_reflection.build_association(post_serializer, foo: 'bar')
55
+ #
56
+ # @api private
57
+ #
58
+ def build_association(subject, parent_serializer_options)
59
+ association_value = value(subject)
60
+ reflection_options = options.dup
61
+ serializer_class = subject.class.serializer_for(association_value, reflection_options)
62
+
63
+ if serializer_class
64
+ begin
65
+ serializer = serializer_class.new(
66
+ association_value,
67
+ serializer_options(subject, parent_serializer_options, reflection_options)
68
+ )
69
+ rescue ActiveModel::Serializer::CollectionSerializer::NoSerializerError
70
+ reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
71
+ end
72
+ elsif !association_value.nil? && !association_value.instance_of?(Object)
73
+ reflection_options[:virtual_value] = association_value
74
+ end
75
+
76
+ Association.new(name, serializer, reflection_options)
77
+ end
78
+
79
+ private
80
+
81
+ def serializer_options(subject, parent_serializer_options, reflection_options)
82
+ serializer = reflection_options.fetch(:serializer, nil)
83
+
84
+ serializer_options = parent_serializer_options.except(:serializer)
85
+ serializer_options[:serializer] = serializer if serializer
86
+ serializer_options[:serializer_context_class] = subject.class
87
+ serializer_options
88
+ end
89
+ end
90
+ end
91
+ 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
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.rc4'
4
4
  end
5
5
  end