agi_active_model_serializers 0.10.7
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 +7 -0
- data/.github/ISSUE_TEMPLATE.md +29 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- data/.gitignore +35 -0
- data/.rubocop.yml +102 -0
- data/.simplecov +110 -0
- data/.travis.yml +51 -0
- data/CHANGELOG.md +612 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +105 -0
- data/Gemfile +56 -0
- data/MIT-LICENSE +22 -0
- data/README.md +307 -0
- data/Rakefile +103 -0
- data/active_model_serializers.gemspec +63 -0
- data/appveyor.yml +24 -0
- data/bin/bench +171 -0
- data/bin/bench_regression +316 -0
- data/bin/serve_benchmark +39 -0
- data/docs/README.md +41 -0
- data/docs/STYLE.md +58 -0
- data/docs/general/adapters.md +247 -0
- data/docs/general/caching.md +58 -0
- data/docs/general/configuration_options.md +169 -0
- data/docs/general/deserialization.md +100 -0
- data/docs/general/fields.md +31 -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 +279 -0
- data/docs/general/serializers.md +461 -0
- data/docs/how-open-source-maintained.jpg +0 -0
- data/docs/howto/add_pagination_links.md +138 -0
- data/docs/howto/add_relationship_links.md +137 -0
- data/docs/howto/add_root_key.md +55 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +65 -0
- data/docs/howto/passing_arbitrary_options.md +27 -0
- data/docs/howto/serialize_poro.md +32 -0
- data/docs/howto/test.md +154 -0
- data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
- data/docs/integrations/ember-and-json-api.md +144 -0
- data/docs/integrations/grape.md +19 -0
- data/docs/jsonapi/errors.md +56 -0
- data/docs/jsonapi/schema.md +151 -0
- data/docs/jsonapi/schema/schema.json +366 -0
- data/docs/rfcs/0000-namespace.md +106 -0
- data/docs/rfcs/template.md +15 -0
- data/lib/action_controller/serialization.rb +66 -0
- data/lib/active_model/serializable_resource.rb +11 -0
- data/lib/active_model/serializer.rb +231 -0
- data/lib/active_model/serializer/adapter.rb +24 -0
- data/lib/active_model/serializer/adapter/attributes.rb +15 -0
- data/lib/active_model/serializer/adapter/base.rb +18 -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/array_serializer.rb +12 -0
- data/lib/active_model/serializer/association.rb +34 -0
- data/lib/active_model/serializer/attribute.rb +25 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +7 -0
- data/lib/active_model/serializer/collection_reflection.rb +7 -0
- data/lib/active_model/serializer/collection_serializer.rb +87 -0
- data/lib/active_model/serializer/concerns/associations.rb +102 -0
- data/lib/active_model/serializer/concerns/attributes.rb +82 -0
- data/lib/active_model/serializer/concerns/caching.rb +292 -0
- data/lib/active_model/serializer/concerns/configuration.rb +59 -0
- data/lib/active_model/serializer/concerns/links.rb +35 -0
- data/lib/active_model/serializer/concerns/meta.rb +29 -0
- data/lib/active_model/serializer/concerns/type.rb +25 -0
- data/lib/active_model/serializer/error_serializer.rb +14 -0
- data/lib/active_model/serializer/errors_serializer.rb +32 -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 +7 -0
- data/lib/active_model/serializer/has_one_reflection.rb +7 -0
- data/lib/active_model/serializer/lint.rb +150 -0
- data/lib/active_model/serializer/null.rb +17 -0
- data/lib/active_model/serializer/reflection.rb +163 -0
- data/lib/active_model/serializer/singular_reflection.rb +7 -0
- data/lib/active_model/serializer/version.rb +5 -0
- data/lib/active_model_serializers.rb +53 -0
- data/lib/active_model_serializers/adapter.rb +98 -0
- data/lib/active_model_serializers/adapter/attributes.rb +13 -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.rb +517 -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 +69 -0
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +63 -0
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +51 -0
- data/lib/active_model_serializers/adapter/null.rb +9 -0
- data/lib/active_model_serializers/callbacks.rb +55 -0
- data/lib/active_model_serializers/deprecate.rb +54 -0
- data/lib/active_model_serializers/deserialization.rb +15 -0
- data/lib/active_model_serializers/json_pointer.rb +14 -0
- data/lib/active_model_serializers/logging.rb +122 -0
- data/lib/active_model_serializers/lookup_chain.rb +80 -0
- data/lib/active_model_serializers/model.rb +71 -0
- data/lib/active_model_serializers/railtie.rb +48 -0
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +78 -0
- data/lib/active_model_serializers/serializable_resource.rb +82 -0
- data/lib/active_model_serializers/serialization_context.rb +39 -0
- data/lib/active_model_serializers/test.rb +7 -0
- data/lib/active_model_serializers/test/schema.rb +138 -0
- data/lib/active_model_serializers/test/serializer.rb +125 -0
- 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 +15 -0
- data/lib/grape/active_model_serializers.rb +16 -0
- data/lib/grape/formatters/active_model_serializers.rb +32 -0
- data/lib/grape/helpers/active_model_serializers.rb +17 -0
- data/test/action_controller/adapter_selector_test.rb +53 -0
- data/test/action_controller/explicit_serializer_test.rb +135 -0
- data/test/action_controller/json/include_test.rb +246 -0
- data/test/action_controller/json_api/deserialization_test.rb +112 -0
- data/test/action_controller/json_api/errors_test.rb +40 -0
- data/test/action_controller/json_api/fields_test.rb +66 -0
- data/test/action_controller/json_api/linked_test.rb +202 -0
- data/test/action_controller/json_api/pagination_test.rb +116 -0
- data/test/action_controller/json_api/transform_test.rb +189 -0
- data/test/action_controller/lookup_proc_test.rb +49 -0
- data/test/action_controller/namespace_lookup_test.rb +232 -0
- data/test/action_controller/serialization_scope_name_test.rb +229 -0
- data/test/action_controller/serialization_test.rb +472 -0
- data/test/active_model_serializers/adapter_for_test.rb +208 -0
- data/test/active_model_serializers/json_pointer_test.rb +22 -0
- data/test/active_model_serializers/logging_test.rb +77 -0
- data/test/active_model_serializers/model_test.rb +69 -0
- data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +71 -0
- data/test/active_model_serializers/test/schema_test.rb +131 -0
- data/test/active_model_serializers/test/serializer_test.rb +62 -0
- data/test/active_record_test.rb +9 -0
- data/test/adapter/attributes_test.rb +43 -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 +104 -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 +96 -0
- data/test/adapter/json_api/errors_test.rb +76 -0
- data/test/adapter/json_api/fields_test.rb +96 -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 +165 -0
- data/test/adapter/json_api/has_one_test.rb +80 -0
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +168 -0
- data/test/adapter/json_api/json_api_test.rb +33 -0
- data/test/adapter/json_api/linked_test.rb +413 -0
- data/test/adapter/json_api/links_test.rb +95 -0
- data/test/adapter/json_api/pagination_links_test.rb +193 -0
- data/test/adapter/json_api/parse_test.rb +137 -0
- data/test/adapter/json_api/relationship_test.rb +397 -0
- data/test/adapter/json_api/resource_identifier_test.rb +110 -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 +512 -0
- data/test/adapter/json_api/type_test.rb +61 -0
- data/test/adapter/json_test.rb +46 -0
- data/test/adapter/null_test.rb +22 -0
- data/test/adapter/polymorphic_test.rb +171 -0
- data/test/adapter_test.rb +67 -0
- data/test/array_serializer_test.rb +22 -0
- data/test/benchmark/app.rb +65 -0
- data/test/benchmark/benchmarking_support.rb +67 -0
- data/test/benchmark/bm_active_record.rb +81 -0
- data/test/benchmark/bm_adapter.rb +38 -0
- data/test/benchmark/bm_caching.rb +119 -0
- data/test/benchmark/bm_lookup_chain.rb +83 -0
- data/test/benchmark/bm_transform.rb +45 -0
- data/test/benchmark/config.ru +3 -0
- data/test/benchmark/controllers.rb +83 -0
- data/test/benchmark/fixtures.rb +219 -0
- data/test/cache_test.rb +595 -0
- data/test/collection_serializer_test.rb +123 -0
- data/test/fixtures/active_record.rb +113 -0
- data/test/fixtures/poro.rb +232 -0
- data/test/generators/scaffold_controller_generator_test.rb +24 -0
- data/test/generators/serializer_generator_test.rb +74 -0
- data/test/grape_test.rb +178 -0
- data/test/lint_test.rb +49 -0
- data/test/logger_test.rb +20 -0
- data/test/poro_test.rb +9 -0
- data/test/serializable_resource_test.rb +79 -0
- data/test/serializers/association_macros_test.rb +37 -0
- data/test/serializers/associations_test.rb +383 -0
- data/test/serializers/attribute_test.rb +153 -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 +202 -0
- data/test/serializers/options_test.rb +32 -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 +136 -0
- data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
- data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/isolated_unit.rb +82 -0
- data/test/support/rails5_shims.rb +53 -0
- data/test/support/rails_app.rb +36 -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 +71 -0
- data/test/test_helper.rb +58 -0
- metadata +602 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'active_model'
|
|
2
|
+
require 'active_support'
|
|
3
|
+
require 'active_support/core_ext/object/with_options'
|
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
|
5
|
+
require 'active_support/json'
|
|
6
|
+
module ActiveModelSerializers
|
|
7
|
+
extend ActiveSupport::Autoload
|
|
8
|
+
autoload :Model
|
|
9
|
+
autoload :Callbacks
|
|
10
|
+
autoload :Deserialization
|
|
11
|
+
autoload :SerializableResource
|
|
12
|
+
autoload :Logging
|
|
13
|
+
autoload :Test
|
|
14
|
+
autoload :Adapter
|
|
15
|
+
autoload :JsonPointer
|
|
16
|
+
autoload :Deprecate
|
|
17
|
+
autoload :LookupChain
|
|
18
|
+
|
|
19
|
+
class << self; attr_accessor :logger; end
|
|
20
|
+
self.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
|
21
|
+
|
|
22
|
+
def self.config
|
|
23
|
+
ActiveModel::Serializer.config
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The file name and line number of the caller of the caller of this method.
|
|
27
|
+
def self.location_of_caller
|
|
28
|
+
caller[1] =~ /(.*?):(\d+).*?$/i
|
|
29
|
+
file = Regexp.last_match(1)
|
|
30
|
+
lineno = Regexp.last_match(2).to_i
|
|
31
|
+
|
|
32
|
+
[file, lineno]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Memoized default include directive
|
|
36
|
+
# @return [JSONAPI::IncludeDirective]
|
|
37
|
+
def self.default_include_directive
|
|
38
|
+
@default_include_directive ||= JSONAPI::IncludeDirective.new(config.default_includes, allow_wildcard: true)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.silence_warnings
|
|
42
|
+
original_verbose = $VERBOSE
|
|
43
|
+
$VERBOSE = nil
|
|
44
|
+
yield
|
|
45
|
+
ensure
|
|
46
|
+
$VERBOSE = original_verbose
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
require 'active_model/serializer/version'
|
|
50
|
+
require 'active_model/serializer'
|
|
51
|
+
require 'active_model/serializable_resource'
|
|
52
|
+
require 'active_model_serializers/railtie' if defined?(::Rails)
|
|
53
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module ActiveModelSerializers
|
|
2
|
+
module Adapter
|
|
3
|
+
UnknownAdapterError = Class.new(ArgumentError)
|
|
4
|
+
ADAPTER_MAP = {} # rubocop:disable Style/MutableConstant
|
|
5
|
+
private_constant :ADAPTER_MAP if defined?(private_constant)
|
|
6
|
+
|
|
7
|
+
class << self # All methods are class functions
|
|
8
|
+
# :nocov:
|
|
9
|
+
def new(*args)
|
|
10
|
+
fail ArgumentError, 'Adapters inherit from Adapter::Base.' \
|
|
11
|
+
"Adapter.new called with args: '#{args.inspect}', from" \
|
|
12
|
+
"'caller[0]'."
|
|
13
|
+
end
|
|
14
|
+
# :nocov:
|
|
15
|
+
|
|
16
|
+
def configured_adapter
|
|
17
|
+
lookup(ActiveModelSerializers.config.adapter)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create(resource, options = {})
|
|
21
|
+
override = options.delete(:adapter)
|
|
22
|
+
klass = override ? adapter_class(override) : configured_adapter
|
|
23
|
+
klass.new(resource, options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @see ActiveModelSerializers::Adapter.lookup
|
|
27
|
+
def adapter_class(adapter)
|
|
28
|
+
ActiveModelSerializers::Adapter.lookup(adapter)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Hash<adapter_name, adapter_class>]
|
|
32
|
+
def adapter_map
|
|
33
|
+
ADAPTER_MAP
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Array<Symbol>] list of adapter names
|
|
37
|
+
def adapters
|
|
38
|
+
adapter_map.keys.sort
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Adds an adapter 'klass' with 'name' to the 'adapter_map'
|
|
42
|
+
# Names are stringified and underscored
|
|
43
|
+
# @param name [Symbol, String, Class] name of the registered adapter
|
|
44
|
+
# @param klass [Class] adapter class itself, optional if name is the class
|
|
45
|
+
# @example
|
|
46
|
+
# AMS::Adapter.register(:my_adapter, MyAdapter)
|
|
47
|
+
# @note The registered name strips out 'ActiveModelSerializers::Adapter::'
|
|
48
|
+
# so that registering 'ActiveModelSerializers::Adapter::Json' and
|
|
49
|
+
# 'Json' will both register as 'json'.
|
|
50
|
+
def register(name, klass = name)
|
|
51
|
+
name = name.to_s.gsub(/\AActiveModelSerializers::Adapter::/, ''.freeze)
|
|
52
|
+
adapter_map[name.underscore] = klass
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def registered_name(adapter_class)
|
|
57
|
+
ADAPTER_MAP.key adapter_class
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @param adapter [String, Symbol, Class] name to fetch adapter by
|
|
61
|
+
# @return [ActiveModelSerializers::Adapter] subclass of Adapter
|
|
62
|
+
# @raise [UnknownAdapterError]
|
|
63
|
+
def lookup(adapter)
|
|
64
|
+
# 1. return if is a class
|
|
65
|
+
return adapter if adapter.is_a?(Class)
|
|
66
|
+
adapter_name = adapter.to_s.underscore
|
|
67
|
+
# 2. return if registered
|
|
68
|
+
adapter_map.fetch(adapter_name) do
|
|
69
|
+
# 3. try to find adapter class from environment
|
|
70
|
+
adapter_class = find_by_name(adapter_name)
|
|
71
|
+
register(adapter_name, adapter_class)
|
|
72
|
+
adapter_class
|
|
73
|
+
end
|
|
74
|
+
rescue NameError, ArgumentError => e
|
|
75
|
+
failure_message =
|
|
76
|
+
"NameError: #{e.message}. Unknown adapter: #{adapter.inspect}. Valid adapters are: #{adapters}"
|
|
77
|
+
raise UnknownAdapterError, failure_message, e.backtrace
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @api private
|
|
81
|
+
def find_by_name(adapter_name)
|
|
82
|
+
adapter_name = adapter_name.to_s.classify.tr('API', 'Api')
|
|
83
|
+
"ActiveModelSerializers::Adapter::#{adapter_name}".safe_constantize ||
|
|
84
|
+
"ActiveModelSerializers::Adapter::#{adapter_name.pluralize}".safe_constantize or # rubocop:disable Style/AndOr
|
|
85
|
+
fail UnknownAdapterError
|
|
86
|
+
end
|
|
87
|
+
private :find_by_name
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Gotta be at the bottom to use the code above it :(
|
|
91
|
+
extend ActiveSupport::Autoload
|
|
92
|
+
autoload :Base
|
|
93
|
+
autoload :Null
|
|
94
|
+
autoload :Attributes
|
|
95
|
+
autoload :Json
|
|
96
|
+
autoload :JsonApi
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module ActiveModelSerializers
|
|
2
|
+
module Adapter
|
|
3
|
+
class Attributes < Base
|
|
4
|
+
def serializable_hash(options = nil)
|
|
5
|
+
options = serialization_options(options)
|
|
6
|
+
options[:fields] ||= instance_options[:fields]
|
|
7
|
+
serialized_hash = serializer.serializable_hash(instance_options, options, self)
|
|
8
|
+
|
|
9
|
+
self.class.transform_key_casing!(serialized_hash, instance_options)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'case_transform'
|
|
2
|
+
|
|
3
|
+
module ActiveModelSerializers
|
|
4
|
+
module Adapter
|
|
5
|
+
class Base
|
|
6
|
+
# Automatically register adapters when subclassing
|
|
7
|
+
def self.inherited(subclass)
|
|
8
|
+
ActiveModelSerializers::Adapter.register(subclass)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Sets the default transform for the adapter.
|
|
12
|
+
#
|
|
13
|
+
# @return [Symbol] the default transform for the adapter
|
|
14
|
+
def self.default_key_transform
|
|
15
|
+
:unaltered
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Determines the transform to use in order of precedence:
|
|
19
|
+
# adapter option, global config, adapter default.
|
|
20
|
+
#
|
|
21
|
+
# @param options [Object]
|
|
22
|
+
# @return [Symbol] the transform to use
|
|
23
|
+
def self.transform(options)
|
|
24
|
+
return options[:key_transform] if options && options[:key_transform]
|
|
25
|
+
ActiveModelSerializers.config.key_transform || default_key_transform
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Transforms the casing of the supplied value.
|
|
29
|
+
#
|
|
30
|
+
# @param value [Object] the value to be transformed
|
|
31
|
+
# @param options [Object] serializable resource options
|
|
32
|
+
# @return [Symbol] the default transform for the adapter
|
|
33
|
+
def self.transform_key_casing!(value, options)
|
|
34
|
+
CaseTransform.send(transform(options), value)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.cache_key
|
|
38
|
+
@cache_key ||= ActiveModelSerializers::Adapter.registered_name(self)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.fragment_cache(cached_hash, non_cached_hash)
|
|
42
|
+
non_cached_hash.merge cached_hash
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
attr_reader :serializer, :instance_options
|
|
46
|
+
|
|
47
|
+
def initialize(serializer, options = {})
|
|
48
|
+
@serializer = serializer
|
|
49
|
+
@instance_options = options
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Subclasses that implement this method must first call
|
|
53
|
+
# options = serialization_options(options)
|
|
54
|
+
def serializable_hash(_options = nil)
|
|
55
|
+
fail NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def as_json(options = nil)
|
|
59
|
+
serializable_hash(options)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def cache_key
|
|
63
|
+
self.class.cache_key
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def fragment_cache(cached_hash, non_cached_hash)
|
|
67
|
+
self.class.fragment_cache(cached_hash, non_cached_hash)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# see https://github.com/rails-api/active_model_serializers/pull/965
|
|
73
|
+
# When <tt>options</tt> is +nil+, sets it to +{}+
|
|
74
|
+
def serialization_options(options)
|
|
75
|
+
options ||= {} # rubocop:disable Lint/UselessAssignment
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def root
|
|
79
|
+
serializer.json_key.to_sym if serializer.json_key
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module ActiveModelSerializers
|
|
2
|
+
module Adapter
|
|
3
|
+
class Json < Base
|
|
4
|
+
def serializable_hash(options = nil)
|
|
5
|
+
options = serialization_options(options)
|
|
6
|
+
serialized_hash = { root => Attributes.new(serializer, instance_options).serializable_hash(options) }
|
|
7
|
+
serialized_hash[meta_key] = meta unless meta.blank?
|
|
8
|
+
|
|
9
|
+
self.class.transform_key_casing!(serialized_hash, instance_options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def meta
|
|
13
|
+
instance_options.fetch(:meta, nil)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def meta_key
|
|
17
|
+
instance_options.fetch(:meta_key, 'meta'.freeze)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,517 @@
|
|
|
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 self.default_key_transform
|
|
35
|
+
:dash
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.fragment_cache(cached_hash, non_cached_hash, root = true)
|
|
39
|
+
core_cached = cached_hash.first
|
|
40
|
+
core_non_cached = non_cached_hash.first
|
|
41
|
+
no_root_cache = cached_hash.delete_if { |key, _value| key == core_cached[0] }
|
|
42
|
+
no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] }
|
|
43
|
+
cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
|
|
44
|
+
hash = root ? { root => cached_resource } : cached_resource
|
|
45
|
+
|
|
46
|
+
hash.deep_merge no_root_non_cache.deep_merge no_root_cache
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def initialize(serializer, options = {})
|
|
50
|
+
super
|
|
51
|
+
@include_directive = JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
|
|
52
|
+
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
|
|
56
|
+
# {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}
|
|
57
|
+
def serializable_hash(*)
|
|
58
|
+
document = if serializer.success?
|
|
59
|
+
success_document
|
|
60
|
+
else
|
|
61
|
+
failure_document
|
|
62
|
+
end
|
|
63
|
+
self.class.transform_key_casing!(document, instance_options)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def fragment_cache(cached_hash, non_cached_hash)
|
|
67
|
+
root = !instance_options.include?(:include)
|
|
68
|
+
self.class.fragment_cache(cached_hash, non_cached_hash, root)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# {http://jsonapi.org/format/#document-top-level Primary data}
|
|
72
|
+
# definition:
|
|
73
|
+
# ☐ toplevel_data (required)
|
|
74
|
+
# ☐ toplevel_included
|
|
75
|
+
# ☑ toplevel_meta
|
|
76
|
+
# ☑ toplevel_links
|
|
77
|
+
# ☑ toplevel_jsonapi
|
|
78
|
+
# structure:
|
|
79
|
+
# {
|
|
80
|
+
# data: toplevel_data,
|
|
81
|
+
# included: toplevel_included,
|
|
82
|
+
# meta: toplevel_meta,
|
|
83
|
+
# links: toplevel_links,
|
|
84
|
+
# jsonapi: toplevel_jsonapi
|
|
85
|
+
# }.reject! {|_,v| v.nil? }
|
|
86
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
87
|
+
def success_document
|
|
88
|
+
is_collection = serializer.respond_to?(:each)
|
|
89
|
+
serializers = is_collection ? serializer : [serializer]
|
|
90
|
+
primary_data, included = resource_objects_for(serializers)
|
|
91
|
+
|
|
92
|
+
hash = {}
|
|
93
|
+
# toplevel_data
|
|
94
|
+
# definition:
|
|
95
|
+
# oneOf
|
|
96
|
+
# resource
|
|
97
|
+
# array of unique items of type 'resource'
|
|
98
|
+
# null
|
|
99
|
+
#
|
|
100
|
+
# description:
|
|
101
|
+
# The document's "primary data" is a representation of the resource or collection of resources
|
|
102
|
+
# targeted by a request.
|
|
103
|
+
#
|
|
104
|
+
# Singular: the resource object.
|
|
105
|
+
#
|
|
106
|
+
# Collection: one of an array of resource objects, an array of resource identifier objects, or
|
|
107
|
+
# an empty array ([]), for requests that target resource collections.
|
|
108
|
+
#
|
|
109
|
+
# None: null if the request is one that might correspond to a single resource, but doesn't currently.
|
|
110
|
+
# structure:
|
|
111
|
+
# if serializable_resource.resource?
|
|
112
|
+
# resource
|
|
113
|
+
# elsif serializable_resource.collection?
|
|
114
|
+
# [
|
|
115
|
+
# resource,
|
|
116
|
+
# resource
|
|
117
|
+
# ]
|
|
118
|
+
# else
|
|
119
|
+
# nil
|
|
120
|
+
# end
|
|
121
|
+
hash[:data] = is_collection ? primary_data : primary_data[0]
|
|
122
|
+
# toplevel_included
|
|
123
|
+
# alias included
|
|
124
|
+
# definition:
|
|
125
|
+
# array of unique items of type 'resource'
|
|
126
|
+
#
|
|
127
|
+
# description:
|
|
128
|
+
# To reduce the number of HTTP requests, servers **MAY** allow
|
|
129
|
+
# responses that include related resources along with the requested primary
|
|
130
|
+
# resources. Such responses are called "compound documents".
|
|
131
|
+
# structure:
|
|
132
|
+
# [
|
|
133
|
+
# resource,
|
|
134
|
+
# resource
|
|
135
|
+
# ]
|
|
136
|
+
hash[:included] = included if included.any?
|
|
137
|
+
|
|
138
|
+
Jsonapi.add!(hash)
|
|
139
|
+
|
|
140
|
+
if instance_options[:links]
|
|
141
|
+
hash[:links] ||= {}
|
|
142
|
+
hash[:links].update(instance_options[:links])
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
if is_collection && serializer.paginated?
|
|
146
|
+
hash[:links] ||= {}
|
|
147
|
+
hash[:links].update(pagination_links_for(serializer))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
hash[:meta] = instance_options[:meta] unless instance_options[:meta].blank?
|
|
151
|
+
|
|
152
|
+
hash
|
|
153
|
+
end
|
|
154
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
155
|
+
|
|
156
|
+
# {http://jsonapi.org/format/#errors JSON API Errors}
|
|
157
|
+
# TODO: look into caching
|
|
158
|
+
# definition:
|
|
159
|
+
# ☑ toplevel_errors array (required)
|
|
160
|
+
# ☐ toplevel_meta
|
|
161
|
+
# ☐ toplevel_jsonapi
|
|
162
|
+
# structure:
|
|
163
|
+
# {
|
|
164
|
+
# errors: toplevel_errors,
|
|
165
|
+
# meta: toplevel_meta,
|
|
166
|
+
# jsonapi: toplevel_jsonapi
|
|
167
|
+
# }.reject! {|_,v| v.nil? }
|
|
168
|
+
# prs:
|
|
169
|
+
# https://github.com/rails-api/active_model_serializers/pull/1004
|
|
170
|
+
def failure_document
|
|
171
|
+
hash = {}
|
|
172
|
+
# PR Please :)
|
|
173
|
+
# Jsonapi.add!(hash)
|
|
174
|
+
|
|
175
|
+
# toplevel_errors
|
|
176
|
+
# definition:
|
|
177
|
+
# array of unique items of type 'error'
|
|
178
|
+
# structure:
|
|
179
|
+
# [
|
|
180
|
+
# error,
|
|
181
|
+
# error
|
|
182
|
+
# ]
|
|
183
|
+
if serializer.respond_to?(:each)
|
|
184
|
+
hash[:errors] = serializer.flat_map do |error_serializer|
|
|
185
|
+
Error.resource_errors(error_serializer, instance_options)
|
|
186
|
+
end
|
|
187
|
+
else
|
|
188
|
+
hash[:errors] = Error.resource_errors(serializer, instance_options)
|
|
189
|
+
end
|
|
190
|
+
hash
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
protected
|
|
194
|
+
|
|
195
|
+
attr_reader :fieldset
|
|
196
|
+
|
|
197
|
+
private
|
|
198
|
+
|
|
199
|
+
# {http://jsonapi.org/format/#document-resource-objects Primary data}
|
|
200
|
+
# resource
|
|
201
|
+
# definition:
|
|
202
|
+
# JSON Object
|
|
203
|
+
#
|
|
204
|
+
# properties:
|
|
205
|
+
# type (required) : String
|
|
206
|
+
# id (required) : String
|
|
207
|
+
# attributes
|
|
208
|
+
# relationships
|
|
209
|
+
# links
|
|
210
|
+
# meta
|
|
211
|
+
#
|
|
212
|
+
# description:
|
|
213
|
+
# "Resource objects" appear in a JSON API document to represent resources
|
|
214
|
+
# structure:
|
|
215
|
+
# {
|
|
216
|
+
# type: 'admin--some-user',
|
|
217
|
+
# id: '1336',
|
|
218
|
+
# attributes: attributes,
|
|
219
|
+
# relationships: relationships,
|
|
220
|
+
# links: links,
|
|
221
|
+
# meta: meta,
|
|
222
|
+
# }.reject! {|_,v| v.nil? }
|
|
223
|
+
# prs:
|
|
224
|
+
# type
|
|
225
|
+
# https://github.com/rails-api/active_model_serializers/pull/1122
|
|
226
|
+
# [x] https://github.com/rails-api/active_model_serializers/pull/1213
|
|
227
|
+
# https://github.com/rails-api/active_model_serializers/pull/1216
|
|
228
|
+
# https://github.com/rails-api/active_model_serializers/pull/1029
|
|
229
|
+
# links
|
|
230
|
+
# [x] https://github.com/rails-api/active_model_serializers/pull/1246
|
|
231
|
+
# [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
|
|
232
|
+
# meta
|
|
233
|
+
# [x] https://github.com/rails-api/active_model_serializers/pull/1340
|
|
234
|
+
def resource_objects_for(serializers)
|
|
235
|
+
@primary = []
|
|
236
|
+
@included = []
|
|
237
|
+
@resource_identifiers = Set.new
|
|
238
|
+
serializers.each { |serializer| process_resource(serializer, true, @include_directive) }
|
|
239
|
+
serializers.each { |serializer| process_relationships(serializer, @include_directive) }
|
|
240
|
+
|
|
241
|
+
[@primary, @included]
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def process_resource(serializer, primary, include_slice = {})
|
|
245
|
+
resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json
|
|
246
|
+
return false unless @resource_identifiers.add?(resource_identifier)
|
|
247
|
+
|
|
248
|
+
resource_object = resource_object_for(serializer, include_slice)
|
|
249
|
+
if primary
|
|
250
|
+
@primary << resource_object
|
|
251
|
+
else
|
|
252
|
+
@included << resource_object
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
true
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def process_relationships(serializer, include_slice)
|
|
259
|
+
serializer.associations(include_slice).each do |association|
|
|
260
|
+
process_relationship(association.serializer, include_slice[association.key])
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def process_relationship(serializer, include_slice)
|
|
265
|
+
if serializer.respond_to?(:each)
|
|
266
|
+
serializer.each { |s| process_relationship(s, include_slice) }
|
|
267
|
+
return
|
|
268
|
+
end
|
|
269
|
+
return unless serializer && serializer.object
|
|
270
|
+
return unless process_resource(serializer, false, include_slice)
|
|
271
|
+
|
|
272
|
+
process_relationships(serializer, include_slice)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
|
|
276
|
+
# attributes
|
|
277
|
+
# definition:
|
|
278
|
+
# JSON Object
|
|
279
|
+
#
|
|
280
|
+
# patternProperties:
|
|
281
|
+
# ^(?!relationships$|links$)\\w[-\\w_]*$
|
|
282
|
+
#
|
|
283
|
+
# description:
|
|
284
|
+
# Members of the attributes object ("attributes") represent information about the resource
|
|
285
|
+
# object in which it's defined.
|
|
286
|
+
# Attributes may contain any valid JSON value
|
|
287
|
+
# structure:
|
|
288
|
+
# {
|
|
289
|
+
# foo: 'bar'
|
|
290
|
+
# }
|
|
291
|
+
def attributes_for(serializer, fields)
|
|
292
|
+
serializer.attributes(fields).except(:id)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# {http://jsonapi.org/format/#document-resource-objects Document Resource Objects}
|
|
296
|
+
def resource_object_for(serializer, include_slice = {})
|
|
297
|
+
resource_object = serializer.fetch(self) do
|
|
298
|
+
resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
|
|
299
|
+
|
|
300
|
+
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
|
|
301
|
+
attributes = attributes_for(serializer, requested_fields)
|
|
302
|
+
resource_object[:attributes] = attributes if attributes.any?
|
|
303
|
+
resource_object
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
|
|
307
|
+
relationships = relationships_for(serializer, requested_associations, include_slice)
|
|
308
|
+
resource_object[:relationships] = relationships if relationships.any?
|
|
309
|
+
|
|
310
|
+
links = links_for(serializer)
|
|
311
|
+
# toplevel_links
|
|
312
|
+
# definition:
|
|
313
|
+
# allOf
|
|
314
|
+
# ☐ links
|
|
315
|
+
# ☐ pagination
|
|
316
|
+
#
|
|
317
|
+
# description:
|
|
318
|
+
# Link members related to the primary data.
|
|
319
|
+
# structure:
|
|
320
|
+
# links.merge!(pagination)
|
|
321
|
+
# prs:
|
|
322
|
+
# https://github.com/rails-api/active_model_serializers/pull/1247
|
|
323
|
+
# https://github.com/rails-api/active_model_serializers/pull/1018
|
|
324
|
+
resource_object[:links] = links if links.any?
|
|
325
|
+
|
|
326
|
+
# toplevel_meta
|
|
327
|
+
# alias meta
|
|
328
|
+
# definition:
|
|
329
|
+
# meta
|
|
330
|
+
# structure
|
|
331
|
+
# {
|
|
332
|
+
# :'git-ref' => 'abc123'
|
|
333
|
+
# }
|
|
334
|
+
meta = meta_for(serializer)
|
|
335
|
+
resource_object[:meta] = meta unless meta.blank?
|
|
336
|
+
|
|
337
|
+
resource_object
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# {http://jsonapi.org/format/#document-resource-object-relationships Document Resource Object Relationship}
|
|
341
|
+
# relationships
|
|
342
|
+
# definition:
|
|
343
|
+
# JSON Object
|
|
344
|
+
#
|
|
345
|
+
# patternProperties:
|
|
346
|
+
# ^\\w[-\\w_]*$"
|
|
347
|
+
#
|
|
348
|
+
# properties:
|
|
349
|
+
# data : relationshipsData
|
|
350
|
+
# links
|
|
351
|
+
# meta
|
|
352
|
+
#
|
|
353
|
+
# description:
|
|
354
|
+
#
|
|
355
|
+
# Members of the relationships object ("relationships") represent references from the
|
|
356
|
+
# resource object in which it's defined to other resource objects."
|
|
357
|
+
# structure:
|
|
358
|
+
# {
|
|
359
|
+
# links: links,
|
|
360
|
+
# meta: meta,
|
|
361
|
+
# data: relationshipsData
|
|
362
|
+
# }.reject! {|_,v| v.nil? }
|
|
363
|
+
#
|
|
364
|
+
# prs:
|
|
365
|
+
# links
|
|
366
|
+
# [x] https://github.com/rails-api/active_model_serializers/pull/1454
|
|
367
|
+
# meta
|
|
368
|
+
# [x] https://github.com/rails-api/active_model_serializers/pull/1454
|
|
369
|
+
# polymorphic
|
|
370
|
+
# [ ] https://github.com/rails-api/active_model_serializers/pull/1420
|
|
371
|
+
#
|
|
372
|
+
# relationshipsData
|
|
373
|
+
# definition:
|
|
374
|
+
# oneOf
|
|
375
|
+
# relationshipToOne
|
|
376
|
+
# relationshipToMany
|
|
377
|
+
#
|
|
378
|
+
# description:
|
|
379
|
+
# Member, whose value represents "resource linkage"
|
|
380
|
+
# structure:
|
|
381
|
+
# if has_one?
|
|
382
|
+
# relationshipToOne
|
|
383
|
+
# else
|
|
384
|
+
# relationshipToMany
|
|
385
|
+
# end
|
|
386
|
+
#
|
|
387
|
+
# definition:
|
|
388
|
+
# anyOf
|
|
389
|
+
# null
|
|
390
|
+
# linkage
|
|
391
|
+
#
|
|
392
|
+
# relationshipToOne
|
|
393
|
+
# description:
|
|
394
|
+
#
|
|
395
|
+
# References to other resource objects in a to-one ("relationship"). Relationships can be
|
|
396
|
+
# specified by including a member in a resource's links object.
|
|
397
|
+
#
|
|
398
|
+
# None: Describes an empty to-one relationship.
|
|
399
|
+
# structure:
|
|
400
|
+
# if has_related?
|
|
401
|
+
# linkage
|
|
402
|
+
# else
|
|
403
|
+
# nil
|
|
404
|
+
# end
|
|
405
|
+
#
|
|
406
|
+
# relationshipToMany
|
|
407
|
+
# definition:
|
|
408
|
+
# array of unique items of type 'linkage'
|
|
409
|
+
#
|
|
410
|
+
# description:
|
|
411
|
+
# An array of objects each containing "type" and "id" members for to-many relationships
|
|
412
|
+
# structure:
|
|
413
|
+
# [
|
|
414
|
+
# linkage,
|
|
415
|
+
# linkage
|
|
416
|
+
# ]
|
|
417
|
+
# prs:
|
|
418
|
+
# polymorphic
|
|
419
|
+
# [ ] https://github.com/rails-api/active_model_serializers/pull/1282
|
|
420
|
+
#
|
|
421
|
+
# linkage
|
|
422
|
+
# definition:
|
|
423
|
+
# type (required) : String
|
|
424
|
+
# id (required) : String
|
|
425
|
+
# meta
|
|
426
|
+
#
|
|
427
|
+
# description:
|
|
428
|
+
# The "type" and "id" to non-empty members.
|
|
429
|
+
# structure:
|
|
430
|
+
# {
|
|
431
|
+
# type: 'required-type',
|
|
432
|
+
# id: 'required-id',
|
|
433
|
+
# meta: meta
|
|
434
|
+
# }.reject! {|_,v| v.nil? }
|
|
435
|
+
def relationships_for(serializer, requested_associations, include_slice)
|
|
436
|
+
include_directive = JSONAPI::IncludeDirective.new(
|
|
437
|
+
requested_associations,
|
|
438
|
+
allow_wildcard: true
|
|
439
|
+
)
|
|
440
|
+
serializer.associations(include_directive, include_slice).each_with_object({}) do |association, hash|
|
|
441
|
+
hash[association.key] = Relationship.new(serializer, instance_options, association).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
|
+
result = Link.new(serializer, value).as_json
|
|
471
|
+
hash[name] = result if result
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# {http://jsonapi.org/format/#fetching-pagination Pagination Links}
|
|
476
|
+
# pagination
|
|
477
|
+
# definition:
|
|
478
|
+
# first : pageObject
|
|
479
|
+
# last : pageObject
|
|
480
|
+
# prev : pageObject
|
|
481
|
+
# next : pageObject
|
|
482
|
+
# structure:
|
|
483
|
+
# {
|
|
484
|
+
# first: pageObject,
|
|
485
|
+
# last: pageObject,
|
|
486
|
+
# prev: pageObject,
|
|
487
|
+
# next: pageObject
|
|
488
|
+
# }
|
|
489
|
+
#
|
|
490
|
+
# pageObject
|
|
491
|
+
# definition:
|
|
492
|
+
# oneOf
|
|
493
|
+
# URI
|
|
494
|
+
# null
|
|
495
|
+
#
|
|
496
|
+
# description:
|
|
497
|
+
# The <x> page of data
|
|
498
|
+
# structure:
|
|
499
|
+
# if has_page?
|
|
500
|
+
# 'http://example.com/some-page?page[number][x]'
|
|
501
|
+
# else
|
|
502
|
+
# nil
|
|
503
|
+
# end
|
|
504
|
+
# prs:
|
|
505
|
+
# https://github.com/rails-api/active_model_serializers/pull/1041
|
|
506
|
+
def pagination_links_for(serializer)
|
|
507
|
+
PaginationLinks.new(serializer.object, instance_options).as_json
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# {http://jsonapi.org/format/#document-meta Docment Meta}
|
|
511
|
+
def meta_for(serializer)
|
|
512
|
+
Meta.new(serializer).as_json
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
# rubocop:enable Style/AsciiComments
|