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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +29 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +104 -0
- data/.rubocop_todo.yml +167 -0
- data/.simplecov +110 -0
- data/.travis.yml +39 -24
- data/CHANGELOG.md +465 -6
- data/CONTRIBUTING.md +105 -0
- data/Gemfile +50 -1
- data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
- data/README.md +102 -590
- data/Rakefile +93 -8
- data/active_model_serializers.gemspec +65 -23
- data/appveyor.yml +24 -0
- data/bin/bench +171 -0
- data/bin/bench_regression +316 -0
- data/bin/serve_benchmark +39 -0
- data/docs/ARCHITECTURE.md +126 -0
- data/docs/README.md +40 -0
- data/docs/STYLE.md +58 -0
- data/docs/general/adapters.md +245 -0
- data/docs/general/caching.md +52 -0
- data/docs/general/configuration_options.md +100 -0
- data/docs/general/deserialization.md +100 -0
- data/docs/general/getting_started.md +133 -0
- data/docs/general/instrumentation.md +40 -0
- data/docs/general/key_transforms.md +40 -0
- data/docs/general/logging.md +14 -0
- data/docs/general/rendering.md +255 -0
- data/docs/general/serializers.md +372 -0
- data/docs/how-open-source-maintained.jpg +0 -0
- data/docs/howto/add_pagination_links.md +139 -0
- data/docs/howto/add_root_key.md +51 -0
- data/docs/howto/outside_controller_use.md +58 -0
- data/docs/howto/passing_arbitrary_options.md +27 -0
- data/docs/howto/serialize_poro.md +32 -0
- data/docs/howto/test.md +152 -0
- data/docs/integrations/ember-and-json-api.md +112 -0
- data/docs/integrations/grape.md +19 -0
- data/docs/jsonapi/errors.md +56 -0
- data/docs/jsonapi/schema/schema.json +366 -0
- data/docs/jsonapi/schema.md +151 -0
- data/docs/rfcs/0000-namespace.md +106 -0
- data/docs/rfcs/template.md +15 -0
- data/lib/action_controller/serialization.rb +31 -36
- data/lib/active_model/serializable_resource.rb +11 -0
- data/lib/active_model/serializer/adapter/attributes.rb +15 -0
- data/lib/active_model/serializer/adapter/base.rb +16 -0
- data/lib/active_model/serializer/adapter/json.rb +15 -0
- data/lib/active_model/serializer/adapter/json_api.rb +15 -0
- data/lib/active_model/serializer/adapter/null.rb +15 -0
- data/lib/active_model/serializer/adapter.rb +24 -0
- data/lib/active_model/serializer/array_serializer.rb +9 -0
- data/lib/active_model/serializer/association.rb +19 -0
- data/lib/active_model/serializer/associations.rb +87 -220
- data/lib/active_model/serializer/attribute.rb +25 -0
- data/lib/active_model/serializer/attributes.rb +82 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +10 -0
- data/lib/active_model/serializer/caching.rb +333 -0
- data/lib/active_model/serializer/collection_reflection.rb +7 -0
- data/lib/active_model/serializer/collection_serializer.rb +64 -0
- data/lib/active_model/serializer/configuration.rb +35 -0
- data/lib/active_model/serializer/error_serializer.rb +10 -0
- data/lib/active_model/serializer/errors_serializer.rb +27 -0
- data/lib/active_model/serializer/field.rb +90 -0
- data/lib/active_model/serializer/fieldset.rb +31 -0
- data/lib/active_model/serializer/has_many_reflection.rb +10 -0
- data/lib/active_model/serializer/has_one_reflection.rb +10 -0
- data/lib/active_model/serializer/include_tree.rb +111 -0
- data/lib/active_model/serializer/links.rb +35 -0
- data/lib/active_model/serializer/lint.rb +146 -0
- data/lib/active_model/serializer/meta.rb +29 -0
- data/lib/active_model/serializer/null.rb +17 -0
- data/lib/active_model/serializer/reflection.rb +147 -0
- data/lib/active_model/serializer/singular_reflection.rb +7 -0
- data/lib/active_model/serializer/type.rb +25 -0
- data/lib/active_model/{serializers → serializer}/version.rb +1 -1
- data/lib/active_model/serializer.rb +158 -481
- data/lib/active_model_serializers/adapter/attributes.rb +76 -0
- data/lib/active_model_serializers/adapter/base.rb +83 -0
- data/lib/active_model_serializers/adapter/json.rb +21 -0
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
- data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
- data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
- data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
- data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +62 -0
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +52 -0
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +37 -0
- data/lib/active_model_serializers/adapter/json_api.rb +516 -0
- data/lib/active_model_serializers/adapter/null.rb +9 -0
- data/lib/active_model_serializers/adapter.rb +92 -0
- data/lib/active_model_serializers/callbacks.rb +55 -0
- data/lib/active_model_serializers/deprecate.rb +55 -0
- data/lib/active_model_serializers/deserialization.rb +13 -0
- data/lib/active_model_serializers/json_pointer.rb +14 -0
- data/lib/active_model_serializers/key_transform.rb +70 -0
- data/lib/active_model_serializers/logging.rb +122 -0
- data/lib/active_model_serializers/model.rb +49 -0
- data/lib/active_model_serializers/railtie.rb +46 -0
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +65 -0
- data/lib/active_model_serializers/serializable_resource.rb +81 -0
- data/lib/active_model_serializers/serialization_context.rb +32 -0
- data/lib/active_model_serializers/test/schema.rb +138 -0
- data/lib/active_model_serializers/test/serializer.rb +125 -0
- data/lib/active_model_serializers/test.rb +7 -0
- data/lib/active_model_serializers.rb +32 -89
- data/lib/generators/rails/USAGE +6 -0
- data/lib/generators/rails/resource_override.rb +10 -0
- data/lib/generators/rails/serializer_generator.rb +36 -0
- data/lib/generators/rails/templates/serializer.rb.erb +8 -0
- data/lib/grape/active_model_serializers.rb +14 -0
- data/lib/grape/formatters/active_model_serializers.rb +15 -0
- data/lib/grape/helpers/active_model_serializers.rb +16 -0
- data/test/action_controller/adapter_selector_test.rb +53 -0
- data/test/action_controller/explicit_serializer_test.rb +134 -0
- data/test/action_controller/json/include_test.rb +167 -0
- data/test/action_controller/json_api/deserialization_test.rb +112 -0
- data/test/action_controller/json_api/errors_test.rb +41 -0
- data/test/action_controller/json_api/linked_test.rb +197 -0
- data/test/action_controller/json_api/pagination_test.rb +116 -0
- data/test/action_controller/json_api/transform_test.rb +181 -0
- data/test/action_controller/serialization_scope_name_test.rb +229 -0
- data/test/action_controller/serialization_test.rb +469 -0
- data/test/active_model_serializers/adapter_for_test.rb +208 -0
- data/test/active_model_serializers/json_pointer_test.rb +20 -0
- data/test/active_model_serializers/key_transform_test.rb +263 -0
- data/test/active_model_serializers/logging_test.rb +77 -0
- data/test/active_model_serializers/model_test.rb +9 -0
- data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +58 -0
- data/test/active_model_serializers/test/schema_test.rb +130 -0
- data/test/active_model_serializers/test/serializer_test.rb +62 -0
- data/test/active_record_test.rb +9 -0
- data/test/adapter/deprecation_test.rb +100 -0
- data/test/adapter/json/belongs_to_test.rb +45 -0
- data/test/adapter/json/collection_test.rb +90 -0
- data/test/adapter/json/has_many_test.rb +45 -0
- data/test/adapter/json/transform_test.rb +93 -0
- data/test/adapter/json_api/belongs_to_test.rb +155 -0
- data/test/adapter/json_api/collection_test.rb +95 -0
- data/test/adapter/json_api/errors_test.rb +78 -0
- data/test/adapter/json_api/fields_test.rb +87 -0
- data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
- data/test/adapter/json_api/has_many_test.rb +144 -0
- data/test/adapter/json_api/has_one_test.rb +80 -0
- data/test/adapter/json_api/json_api_test.rb +35 -0
- data/test/adapter/json_api/linked_test.rb +392 -0
- data/test/adapter/json_api/links_test.rb +93 -0
- data/test/adapter/json_api/pagination_links_test.rb +166 -0
- data/test/adapter/json_api/parse_test.rb +137 -0
- data/test/adapter/json_api/relationship_test.rb +161 -0
- data/test/adapter/json_api/relationships_test.rb +199 -0
- data/test/adapter/json_api/resource_identifier_test.rb +85 -0
- data/test/adapter/json_api/resource_meta_test.rb +100 -0
- data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
- data/test/adapter/json_api/transform_test.rb +502 -0
- data/test/adapter/json_api/type_test.rb +61 -0
- data/test/adapter/json_test.rb +45 -0
- data/test/adapter/null_test.rb +23 -0
- data/test/adapter/polymorphic_test.rb +171 -0
- data/test/adapter_test.rb +67 -0
- data/test/array_serializer_test.rb +20 -73
- data/test/benchmark/app.rb +65 -0
- data/test/benchmark/benchmarking_support.rb +67 -0
- data/test/benchmark/bm_caching.rb +119 -0
- data/test/benchmark/bm_transform.rb +34 -0
- data/test/benchmark/config.ru +3 -0
- data/test/benchmark/controllers.rb +84 -0
- data/test/benchmark/fixtures.rb +219 -0
- data/test/cache_test.rb +485 -0
- data/test/collection_serializer_test.rb +110 -0
- data/test/fixtures/active_record.rb +78 -0
- data/test/fixtures/poro.rb +282 -0
- data/test/generators/scaffold_controller_generator_test.rb +24 -0
- data/test/generators/serializer_generator_test.rb +57 -0
- data/test/grape_test.rb +82 -0
- data/test/include_tree/from_include_args_test.rb +26 -0
- data/test/include_tree/from_string_test.rb +94 -0
- data/test/include_tree/include_args_to_hash_test.rb +64 -0
- data/test/lint_test.rb +49 -0
- data/test/logger_test.rb +18 -0
- data/test/poro_test.rb +9 -0
- data/test/serializable_resource_test.rb +83 -0
- data/test/serializers/association_macros_test.rb +36 -0
- data/test/serializers/associations_test.rb +295 -0
- data/test/serializers/attribute_test.rb +151 -0
- data/test/serializers/attributes_test.rb +52 -0
- data/test/serializers/caching_configuration_test_isolated.rb +170 -0
- data/test/serializers/configuration_test.rb +32 -0
- data/test/serializers/fieldset_test.rb +14 -0
- data/test/serializers/meta_test.rb +196 -0
- data/test/serializers/options_test.rb +21 -0
- data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
- data/test/serializers/root_test.rb +21 -0
- data/test/serializers/serialization_test.rb +55 -0
- data/test/serializers/serializer_for_test.rb +134 -0
- data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/isolated_unit.rb +79 -0
- data/test/support/rails5_shims.rb +47 -0
- data/test/support/rails_app.rb +45 -0
- data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
- data/test/support/schemas/custom/show.json +7 -0
- data/test/support/schemas/hyper_schema.json +93 -0
- data/test/support/schemas/render_using_json_api.json +43 -0
- data/test/support/schemas/simple_json_pointers.json +10 -0
- data/test/support/serialization_testing.rb +53 -0
- data/test/test_helper.rb +48 -23
- metadata +449 -43
- data/DESIGN.textile +0 -586
- data/Gemfile.edge +0 -9
- data/bench/perf.rb +0 -43
- data/cruft.md +0 -19
- data/lib/active_model/array_serializer.rb +0 -104
- data/lib/active_record/serializer_override.rb +0 -16
- data/lib/generators/resource_override.rb +0 -13
- data/lib/generators/serializer/USAGE +0 -9
- data/lib/generators/serializer/serializer_generator.rb +0 -42
- data/lib/generators/serializer/templates/serializer.rb +0 -19
- data/test/association_test.rb +0 -592
- data/test/caching_test.rb +0 -96
- data/test/generators_test.rb +0 -85
- data/test/no_serialization_scope_test.rb +0 -34
- data/test/serialization_scope_name_test.rb +0 -67
- data/test/serialization_test.rb +0 -392
- data/test/serializer_support_test.rb +0 -51
- data/test/serializer_test.rb +0 -1465
- 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
|