active_model_serializers_custom 0.10.90

Sign up to get free protection for your applications and to get access to all the features.
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,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class Json < Base
6
+ def serializable_hash(options = nil)
7
+ options = serialization_options(options)
8
+ serialized_hash = { root => Attributes.new(serializer, instance_options).serializable_hash(options) }
9
+ serialized_hash[meta_key] = meta unless meta.blank?
10
+
11
+ self.class.transform_key_casing!(serialized_hash, instance_options)
12
+ end
13
+
14
+ def meta
15
+ instance_options.fetch(:meta, nil)
16
+ end
17
+
18
+ def meta_key
19
+ instance_options.fetch(:meta_key, 'meta'.freeze)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,535 @@
1
+ # frozen_string_literal: true
2
+
3
+ # {http://jsonapi.org/format/ JSON API specification}
4
+ # rubocop:disable Style/AsciiComments
5
+ # TODO: implement!
6
+ # ☐ https://github.com/rails-api/active_model_serializers/issues/1235
7
+ # TODO: use uri_template in link generation?
8
+ # ☐ https://github.com/rails-api/active_model_serializers/pull/1282#discussion_r42528812
9
+ # see gem https://github.com/hannesg/uri_template
10
+ # spec http://tools.ietf.org/html/rfc6570
11
+ # impl https://developer.github.com/v3/#schema https://api.github.com/
12
+ # TODO: validate against a JSON schema document?
13
+ # ☐ https://github.com/rails-api/active_model_serializers/issues/1162
14
+ # ☑ https://github.com/rails-api/active_model_serializers/pull/1270
15
+ # TODO: Routing
16
+ # ☐ https://github.com/rails-api/active_model_serializers/pull/1476
17
+ # TODO: Query Params
18
+ # ☑ `include` https://github.com/rails-api/active_model_serializers/pull/1131
19
+ # ☑ `fields` https://github.com/rails-api/active_model_serializers/pull/700
20
+ # ☑ `page[number]=3&page[size]=1` https://github.com/rails-api/active_model_serializers/pull/1041
21
+ # ☐ `filter`
22
+ # ☐ `sort`
23
+ module ActiveModelSerializers
24
+ module Adapter
25
+ class JsonApi < Base
26
+ extend ActiveSupport::Autoload
27
+ eager_autoload do
28
+ autoload :Jsonapi
29
+ autoload :ResourceIdentifier
30
+ autoload :Link
31
+ autoload :PaginationLinks
32
+ autoload :Meta
33
+ autoload :Error
34
+ autoload :Deserialization
35
+ autoload :Relationship
36
+ end
37
+
38
+ def self.default_key_transform
39
+ :dash
40
+ end
41
+
42
+ def self.fragment_cache(cached_hash, non_cached_hash, root = true)
43
+ core_cached = cached_hash.first
44
+ core_non_cached = non_cached_hash.first
45
+ no_root_cache = cached_hash.delete_if { |key, _value| key == core_cached[0] }
46
+ no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] }
47
+ cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
48
+ hash = root ? { root => cached_resource } : cached_resource
49
+
50
+ hash.deep_merge no_root_non_cache.deep_merge no_root_cache
51
+ end
52
+
53
+ def initialize(serializer, options = {})
54
+ super
55
+ @include_directive = JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
56
+ @fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
57
+ end
58
+
59
+ # {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
60
+ # {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}
61
+ def serializable_hash(*)
62
+ document = if serializer.success?
63
+ success_document
64
+ else
65
+ failure_document
66
+ end
67
+ self.class.transform_key_casing!(document, instance_options)
68
+ end
69
+
70
+ def fragment_cache(cached_hash, non_cached_hash)
71
+ root = !instance_options.include?(:include)
72
+ self.class.fragment_cache(cached_hash, non_cached_hash, root)
73
+ end
74
+
75
+ # {http://jsonapi.org/format/#document-top-level Primary data}
76
+ # definition:
77
+ # ☐ toplevel_data (required)
78
+ # ☐ toplevel_included
79
+ # ☑ toplevel_meta
80
+ # ☑ toplevel_links
81
+ # ☑ toplevel_jsonapi
82
+ # structure:
83
+ # {
84
+ # data: toplevel_data,
85
+ # included: toplevel_included,
86
+ # meta: toplevel_meta,
87
+ # links: toplevel_links,
88
+ # jsonapi: toplevel_jsonapi
89
+ # }.reject! {|_,v| v.nil? }
90
+ # rubocop:disable Metrics/CyclomaticComplexity
91
+ def success_document
92
+ is_collection = serializer.respond_to?(:each)
93
+ serializers = is_collection ? serializer : [serializer]
94
+ primary_data, included = resource_objects_for(serializers)
95
+
96
+ hash = {}
97
+ # toplevel_data
98
+ # definition:
99
+ # oneOf
100
+ # resource
101
+ # array of unique items of type 'resource'
102
+ # null
103
+ #
104
+ # description:
105
+ # The document's "primary data" is a representation of the resource or collection of resources
106
+ # targeted by a request.
107
+ #
108
+ # Singular: the resource object.
109
+ #
110
+ # Collection: one of an array of resource objects, an array of resource identifier objects, or
111
+ # an empty array ([]), for requests that target resource collections.
112
+ #
113
+ # None: null if the request is one that might correspond to a single resource, but doesn't currently.
114
+ # structure:
115
+ # if serializable_resource.resource?
116
+ # resource
117
+ # elsif serializable_resource.collection?
118
+ # [
119
+ # resource,
120
+ # resource
121
+ # ]
122
+ # else
123
+ # nil
124
+ # end
125
+ hash[:data] = is_collection ? primary_data : primary_data[0]
126
+ # toplevel_included
127
+ # alias included
128
+ # definition:
129
+ # array of unique items of type 'resource'
130
+ #
131
+ # description:
132
+ # To reduce the number of HTTP requests, servers **MAY** allow
133
+ # responses that include related resources along with the requested primary
134
+ # resources. Such responses are called "compound documents".
135
+ # structure:
136
+ # [
137
+ # resource,
138
+ # resource
139
+ # ]
140
+ hash[:included] = included if included.any?
141
+
142
+ Jsonapi.add!(hash)
143
+
144
+ if instance_options[:links]
145
+ hash[:links] ||= {}
146
+ hash[:links].update(instance_options[:links])
147
+ end
148
+
149
+ if is_collection && serializer.paginated?
150
+ hash[:links] ||= {}
151
+ hash[:links].update(pagination_links_for(serializer))
152
+ end
153
+
154
+ hash[:meta] = instance_options[:meta] unless instance_options[:meta].blank?
155
+
156
+ hash
157
+ end
158
+ # rubocop:enable Metrics/CyclomaticComplexity
159
+
160
+ # {http://jsonapi.org/format/#errors JSON API Errors}
161
+ # TODO: look into caching
162
+ # definition:
163
+ # ☑ toplevel_errors array (required)
164
+ # ☐ toplevel_meta
165
+ # ☐ toplevel_jsonapi
166
+ # structure:
167
+ # {
168
+ # errors: toplevel_errors,
169
+ # meta: toplevel_meta,
170
+ # jsonapi: toplevel_jsonapi
171
+ # }.reject! {|_,v| v.nil? }
172
+ # prs:
173
+ # https://github.com/rails-api/active_model_serializers/pull/1004
174
+ def failure_document
175
+ hash = {}
176
+ # PR Please :)
177
+ # Jsonapi.add!(hash)
178
+
179
+ # toplevel_errors
180
+ # definition:
181
+ # array of unique items of type 'error'
182
+ # structure:
183
+ # [
184
+ # error,
185
+ # error
186
+ # ]
187
+ if serializer.respond_to?(:each)
188
+ hash[:errors] = serializer.flat_map do |error_serializer|
189
+ Error.resource_errors(error_serializer, instance_options)
190
+ end
191
+ else
192
+ hash[:errors] = Error.resource_errors(serializer, instance_options)
193
+ end
194
+ hash
195
+ end
196
+
197
+ protected
198
+
199
+ attr_reader :fieldset
200
+
201
+ private
202
+
203
+ # {http://jsonapi.org/format/#document-resource-objects Primary data}
204
+ # resource
205
+ # definition:
206
+ # JSON Object
207
+ #
208
+ # properties:
209
+ # type (required) : String
210
+ # id (required) : String
211
+ # attributes
212
+ # relationships
213
+ # links
214
+ # meta
215
+ #
216
+ # description:
217
+ # "Resource objects" appear in a JSON API document to represent resources
218
+ # structure:
219
+ # {
220
+ # type: 'admin--some-user',
221
+ # id: '1336',
222
+ # attributes: attributes,
223
+ # relationships: relationships,
224
+ # links: links,
225
+ # meta: meta,
226
+ # }.reject! {|_,v| v.nil? }
227
+ # prs:
228
+ # type
229
+ # https://github.com/rails-api/active_model_serializers/pull/1122
230
+ # [x] https://github.com/rails-api/active_model_serializers/pull/1213
231
+ # https://github.com/rails-api/active_model_serializers/pull/1216
232
+ # https://github.com/rails-api/active_model_serializers/pull/1029
233
+ # links
234
+ # [x] https://github.com/rails-api/active_model_serializers/pull/1246
235
+ # [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
236
+ # meta
237
+ # [x] https://github.com/rails-api/active_model_serializers/pull/1340
238
+ def resource_objects_for(serializers)
239
+ @primary = []
240
+ @included = []
241
+ @resource_identifiers = Set.new
242
+ serializers.each { |serializer| process_resource(serializer, true, @include_directive) }
243
+ serializers.each { |serializer| process_relationships(serializer, @include_directive) }
244
+
245
+ [@primary, @included]
246
+ end
247
+
248
+ def process_resource(serializer, primary, include_slice = {})
249
+ resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json
250
+ return false unless @resource_identifiers.add?(resource_identifier)
251
+
252
+ resource_object = resource_object_for(serializer, include_slice)
253
+ if primary
254
+ @primary << resource_object
255
+ else
256
+ @included << resource_object
257
+ end
258
+
259
+ true
260
+ end
261
+
262
+ def process_relationships(serializer, include_slice)
263
+ serializer.associations(include_slice).each do |association|
264
+ # TODO(BF): Process relationship without evaluating lazy_association
265
+ process_relationship(association.lazy_association.serializer, include_slice[association.key])
266
+ end
267
+ end
268
+
269
+ def process_relationship(serializer, include_slice)
270
+ if serializer.respond_to?(:each)
271
+ serializer.each { |s| process_relationship(s, include_slice) }
272
+ return
273
+ end
274
+ return unless serializer && serializer.object
275
+ return unless process_resource(serializer, false, include_slice)
276
+
277
+ process_relationships(serializer, include_slice)
278
+ end
279
+
280
+ # {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
281
+ # attributes
282
+ # definition:
283
+ # JSON Object
284
+ #
285
+ # patternProperties:
286
+ # ^(?!relationships$|links$)\\w[-\\w_]*$
287
+ #
288
+ # description:
289
+ # Members of the attributes object ("attributes") represent information about the resource
290
+ # object in which it's defined.
291
+ # Attributes may contain any valid JSON value
292
+ # structure:
293
+ # {
294
+ # foo: 'bar'
295
+ # }
296
+ def attributes_for(serializer, fields)
297
+ serializer.attributes(fields).except(:id)
298
+ end
299
+
300
+ # {http://jsonapi.org/format/#document-resource-objects Document Resource Objects}
301
+ def resource_object_for(serializer, include_slice = {})
302
+ resource_object = data_for(serializer, include_slice)
303
+
304
+ # toplevel_links
305
+ # definition:
306
+ # allOf
307
+ # ☐ links
308
+ # ☐ pagination
309
+ #
310
+ # description:
311
+ # Link members related to the primary data.
312
+ # structure:
313
+ # links.merge!(pagination)
314
+ # prs:
315
+ # https://github.com/rails-api/active_model_serializers/pull/1247
316
+ # https://github.com/rails-api/active_model_serializers/pull/1018
317
+ if (links = links_for(serializer)).any?
318
+ resource_object ||= {}
319
+ resource_object[:links] = links
320
+ end
321
+
322
+ # toplevel_meta
323
+ # alias meta
324
+ # definition:
325
+ # meta
326
+ # structure
327
+ # {
328
+ # :'git-ref' => 'abc123'
329
+ # }
330
+ if (meta = meta_for(serializer)).present?
331
+ resource_object ||= {}
332
+ resource_object[:meta] = meta
333
+ end
334
+
335
+ resource_object
336
+ end
337
+
338
+ def data_for(serializer, include_slice)
339
+ data = serializer.fetch(self) do
340
+ resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
341
+ break nil if resource_object.nil?
342
+
343
+ requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
344
+ attributes = attributes_for(serializer, requested_fields)
345
+ resource_object[:attributes] = attributes if attributes.any?
346
+ resource_object
347
+ end
348
+ data.tap do |resource_object|
349
+ next if resource_object.nil?
350
+ # NOTE(BF): the attributes are cached above, separately from the relationships, below.
351
+ requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
352
+ relationships = relationships_for(serializer, requested_associations, include_slice)
353
+ resource_object[:relationships] = relationships if relationships.any?
354
+ end
355
+ end
356
+
357
+ # {http://jsonapi.org/format/#document-resource-object-relationships Document Resource Object Relationship}
358
+ # relationships
359
+ # definition:
360
+ # JSON Object
361
+ #
362
+ # patternProperties:
363
+ # ^\\w[-\\w_]*$"
364
+ #
365
+ # properties:
366
+ # data : relationshipsData
367
+ # links
368
+ # meta
369
+ #
370
+ # description:
371
+ #
372
+ # Members of the relationships object ("relationships") represent references from the
373
+ # resource object in which it's defined to other resource objects."
374
+ # structure:
375
+ # {
376
+ # links: links,
377
+ # meta: meta,
378
+ # data: relationshipsData
379
+ # }.reject! {|_,v| v.nil? }
380
+ #
381
+ # prs:
382
+ # links
383
+ # [x] https://github.com/rails-api/active_model_serializers/pull/1454
384
+ # meta
385
+ # [x] https://github.com/rails-api/active_model_serializers/pull/1454
386
+ # polymorphic
387
+ # [ ] https://github.com/rails-api/active_model_serializers/pull/1420
388
+ #
389
+ # relationshipsData
390
+ # definition:
391
+ # oneOf
392
+ # relationshipToOne
393
+ # relationshipToMany
394
+ #
395
+ # description:
396
+ # Member, whose value represents "resource linkage"
397
+ # structure:
398
+ # if has_one?
399
+ # relationshipToOne
400
+ # else
401
+ # relationshipToMany
402
+ # end
403
+ #
404
+ # definition:
405
+ # anyOf
406
+ # null
407
+ # linkage
408
+ #
409
+ # relationshipToOne
410
+ # description:
411
+ #
412
+ # References to other resource objects in a to-one ("relationship"). Relationships can be
413
+ # specified by including a member in a resource's links object.
414
+ #
415
+ # None: Describes an empty to-one relationship.
416
+ # structure:
417
+ # if has_related?
418
+ # linkage
419
+ # else
420
+ # nil
421
+ # end
422
+ #
423
+ # relationshipToMany
424
+ # definition:
425
+ # array of unique items of type 'linkage'
426
+ #
427
+ # description:
428
+ # An array of objects each containing "type" and "id" members for to-many relationships
429
+ # structure:
430
+ # [
431
+ # linkage,
432
+ # linkage
433
+ # ]
434
+ # prs:
435
+ # polymorphic
436
+ # [ ] https://github.com/rails-api/active_model_serializers/pull/1282
437
+ #
438
+ # linkage
439
+ # definition:
440
+ # type (required) : String
441
+ # id (required) : String
442
+ # meta
443
+ #
444
+ # description:
445
+ # The "type" and "id" to non-empty members.
446
+ # structure:
447
+ # {
448
+ # type: 'required-type',
449
+ # id: 'required-id',
450
+ # meta: meta
451
+ # }.reject! {|_,v| v.nil? }
452
+ def relationships_for(serializer, requested_associations, include_slice)
453
+ include_directive = JSONAPI::IncludeDirective.new(
454
+ requested_associations,
455
+ allow_wildcard: true
456
+ )
457
+ serializer.associations(include_directive, include_slice).each_with_object({}) do |association, hash|
458
+ hash[association.key] = Relationship.new(serializer, instance_options, association).as_json
459
+ end
460
+ end
461
+
462
+ # {http://jsonapi.org/format/#document-links Document Links}
463
+ # links
464
+ # definition:
465
+ # JSON Object
466
+ #
467
+ # properties:
468
+ # self : URI
469
+ # related : link
470
+ #
471
+ # description:
472
+ # A resource object **MAY** contain references to other resource objects ("relationships").
473
+ # Relationships may be to-one or to-many. Relationships can be specified by including a member
474
+ # in a resource's links object.
475
+ #
476
+ # A `self` member’s value is a URL for the relationship itself (a "relationship URL"). This
477
+ # URL allows the client to directly manipulate the relationship. For example, it would allow
478
+ # a client to remove an `author` from an `article` without deleting the people resource
479
+ # itself.
480
+ # structure:
481
+ # {
482
+ # self: 'http://example.com/etc',
483
+ # related: link
484
+ # }.reject! {|_,v| v.nil? }
485
+ def links_for(serializer)
486
+ serializer._links.each_with_object({}) do |(name, value), hash|
487
+ next if value.excluded?(serializer)
488
+ result = Link.new(serializer, value.block).as_json
489
+ hash[name] = result if result
490
+ end
491
+ end
492
+
493
+ # {http://jsonapi.org/format/#fetching-pagination Pagination Links}
494
+ # pagination
495
+ # definition:
496
+ # first : pageObject
497
+ # last : pageObject
498
+ # prev : pageObject
499
+ # next : pageObject
500
+ # structure:
501
+ # {
502
+ # first: pageObject,
503
+ # last: pageObject,
504
+ # prev: pageObject,
505
+ # next: pageObject
506
+ # }
507
+ #
508
+ # pageObject
509
+ # definition:
510
+ # oneOf
511
+ # URI
512
+ # null
513
+ #
514
+ # description:
515
+ # The <x> page of data
516
+ # structure:
517
+ # if has_page?
518
+ # 'http://example.com/some-page?page[number][x]'
519
+ # else
520
+ # nil
521
+ # end
522
+ # prs:
523
+ # https://github.com/rails-api/active_model_serializers/pull/1041
524
+ def pagination_links_for(serializer)
525
+ PaginationLinks.new(serializer.object, instance_options).as_json
526
+ end
527
+
528
+ # {http://jsonapi.org/format/#document-meta Docment Meta}
529
+ def meta_for(serializer)
530
+ Meta.new(serializer).as_json
531
+ end
532
+ end
533
+ end
534
+ end
535
+ # rubocop:enable Style/AsciiComments