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