active_model_serializers 0.9.0 → 0.10.12
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 +5 -5
- data/CHANGELOG.md +679 -9
- data/MIT-LICENSE +3 -2
- data/README.md +195 -753
- data/lib/action_controller/serialization.rb +45 -49
- data/lib/active_model/serializable_resource.rb +13 -0
- data/lib/active_model/serializer.rb +369 -212
- data/lib/active_model/serializer/adapter.rb +26 -0
- data/lib/active_model/serializer/adapter/attributes.rb +17 -0
- data/lib/active_model/serializer/adapter/base.rb +20 -0
- data/lib/active_model/serializer/adapter/json.rb +17 -0
- data/lib/active_model/serializer/adapter/json_api.rb +17 -0
- data/lib/active_model/serializer/adapter/null.rb +17 -0
- data/lib/active_model/serializer/array_serializer.rb +14 -0
- data/lib/active_model/serializer/association.rb +73 -0
- data/lib/active_model/serializer/attribute.rb +27 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
- data/lib/active_model/serializer/collection_serializer.rb +90 -0
- data/lib/active_model/serializer/concerns/caching.rb +305 -0
- data/lib/active_model/serializer/error_serializer.rb +16 -0
- data/lib/active_model/serializer/errors_serializer.rb +34 -0
- data/lib/active_model/serializer/field.rb +92 -0
- data/lib/active_model/serializer/fieldset.rb +33 -0
- data/lib/active_model/serializer/has_many_reflection.rb +12 -0
- data/lib/active_model/serializer/has_one_reflection.rb +9 -0
- data/lib/active_model/serializer/lazy_association.rb +99 -0
- data/lib/active_model/serializer/link.rb +23 -0
- data/lib/active_model/serializer/lint.rb +152 -0
- data/lib/active_model/serializer/null.rb +19 -0
- data/lib/active_model/serializer/reflection.rb +212 -0
- data/lib/active_model/serializer/version.rb +3 -1
- data/lib/active_model_serializers.rb +60 -17
- data/lib/active_model_serializers/adapter.rb +100 -0
- data/lib/active_model_serializers/adapter/attributes.rb +36 -0
- data/lib/active_model_serializers/adapter/base.rb +85 -0
- data/lib/active_model_serializers/adapter/json.rb +23 -0
- data/lib/active_model_serializers/adapter/json_api.rb +535 -0
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
- data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
- data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
- data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
- data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +90 -0
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
- data/lib/active_model_serializers/adapter/null.rb +11 -0
- data/lib/active_model_serializers/callbacks.rb +57 -0
- data/lib/active_model_serializers/deprecate.rb +56 -0
- data/lib/active_model_serializers/deserialization.rb +17 -0
- data/lib/active_model_serializers/json_pointer.rb +16 -0
- data/lib/active_model_serializers/logging.rb +124 -0
- data/lib/active_model_serializers/lookup_chain.rb +82 -0
- data/lib/active_model_serializers/model.rb +132 -0
- data/lib/active_model_serializers/model/caching.rb +25 -0
- data/lib/active_model_serializers/railtie.rb +52 -0
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
- data/lib/active_model_serializers/serializable_resource.rb +84 -0
- data/lib/active_model_serializers/serialization_context.rb +41 -0
- data/lib/active_model_serializers/test.rb +9 -0
- data/lib/active_model_serializers/test/schema.rb +140 -0
- data/lib/active_model_serializers/test/serializer.rb +127 -0
- data/lib/generators/rails/USAGE +6 -0
- data/lib/{active_model/serializer/generators → generators/rails}/resource_override.rb +3 -4
- data/lib/{active_model/serializer/generators/serializer → generators/rails}/serializer_generator.rb +6 -5
- data/lib/{active_model/serializer/generators/serializer/templates/serializer.rb → generators/rails/templates/serializer.rb.erb} +0 -0
- data/lib/grape/active_model_serializers.rb +18 -0
- data/lib/grape/formatters/active_model_serializers.rb +34 -0
- data/lib/grape/helpers/active_model_serializers.rb +19 -0
- data/lib/tasks/rubocop.rake +55 -0
- metadata +315 -99
- data/CONTRIBUTING.md +0 -20
- data/DESIGN.textile +0 -586
- data/lib/action_controller/serialization_test_case.rb +0 -79
- data/lib/active_model/array_serializer.rb +0 -65
- data/lib/active_model/default_serializer.rb +0 -32
- data/lib/active_model/serializable.rb +0 -40
- data/lib/active_model/serializer/associations.rb +0 -102
- data/lib/active_model/serializer/config.rb +0 -31
- data/lib/active_model/serializer/generators/serializer/USAGE +0 -9
- data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +0 -14
- data/lib/active_model/serializer/generators/serializer/templates/controller.rb +0 -93
- data/lib/active_model/serializer/railtie.rb +0 -10
- data/lib/active_model/serializer_support.rb +0 -5
- data/test/fixtures/active_record.rb +0 -92
- data/test/fixtures/poro.rb +0 -75
- data/test/integration/action_controller/serialization_test.rb +0 -287
- data/test/integration/action_controller/serialization_test_case_test.rb +0 -61
- data/test/integration/active_record/active_record_test.rb +0 -77
- data/test/integration/generators/resource_generator_test.rb +0 -26
- data/test/integration/generators/scaffold_controller_generator_test.rb +0 -64
- data/test/integration/generators/serializer_generator_test.rb +0 -41
- data/test/test_app.rb +0 -11
- data/test/test_helper.rb +0 -24
- data/test/unit/active_model/array_serializer/except_test.rb +0 -18
- data/test/unit/active_model/array_serializer/key_format_test.rb +0 -18
- data/test/unit/active_model/array_serializer/meta_test.rb +0 -53
- data/test/unit/active_model/array_serializer/only_test.rb +0 -18
- data/test/unit/active_model/array_serializer/root_test.rb +0 -102
- data/test/unit/active_model/array_serializer/scope_test.rb +0 -24
- data/test/unit/active_model/array_serializer/serialization_test.rb +0 -199
- data/test/unit/active_model/default_serializer_test.rb +0 -13
- data/test/unit/active_model/serializer/associations/build_serializer_test.rb +0 -21
- data/test/unit/active_model/serializer/associations_test.rb +0 -19
- data/test/unit/active_model/serializer/attributes_test.rb +0 -41
- data/test/unit/active_model/serializer/config_test.rb +0 -88
- data/test/unit/active_model/serializer/filter_test.rb +0 -69
- data/test/unit/active_model/serializer/has_many_test.rb +0 -230
- data/test/unit/active_model/serializer/has_one_test.rb +0 -207
- data/test/unit/active_model/serializer/key_format_test.rb +0 -25
- data/test/unit/active_model/serializer/meta_test.rb +0 -39
- data/test/unit/active_model/serializer/options_test.rb +0 -15
- data/test/unit/active_model/serializer/root_test.rb +0 -117
- data/test/unit/active_model/serializer/scope_test.rb +0 -49
|
@@ -1,79 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_support/core_ext/class/attribute'
|
|
4
|
+
require 'active_model_serializers/serialization_context'
|
|
2
5
|
|
|
3
6
|
module ActionController
|
|
4
|
-
# Action Controller Serialization
|
|
5
|
-
#
|
|
6
|
-
# Overrides render :json to check if the given object implements +active_model_serializer+
|
|
7
|
-
# as a method. If so, use the returned serializer instead of calling +to_json+ on the object.
|
|
8
|
-
#
|
|
9
|
-
# This module also provides a serialization_scope method that allows you to configure the
|
|
10
|
-
# +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+
|
|
11
|
-
# to the current user:
|
|
12
|
-
#
|
|
13
|
-
# class ApplicationController < ActionController::Base
|
|
14
|
-
# serialization_scope :current_user
|
|
15
|
-
# end
|
|
16
|
-
#
|
|
17
|
-
# If you need more complex scope rules, you can simply override the serialization_scope:
|
|
18
|
-
#
|
|
19
|
-
# class ApplicationController < ActionController::Base
|
|
20
|
-
# private
|
|
21
|
-
#
|
|
22
|
-
# def serialization_scope
|
|
23
|
-
# current_user
|
|
24
|
-
# end
|
|
25
|
-
# end
|
|
26
|
-
#
|
|
27
7
|
module Serialization
|
|
28
8
|
extend ActiveSupport::Concern
|
|
29
9
|
|
|
30
10
|
include ActionController::Renderers
|
|
31
11
|
|
|
32
|
-
|
|
33
|
-
|
|
12
|
+
module ClassMethods
|
|
13
|
+
def serialization_scope(scope)
|
|
14
|
+
self._serialization_scope = scope
|
|
15
|
+
end
|
|
34
16
|
end
|
|
35
|
-
self.enabled = true
|
|
36
17
|
|
|
37
18
|
included do
|
|
38
19
|
class_attribute :_serialization_scope
|
|
39
20
|
self._serialization_scope = :current_user
|
|
40
|
-
end
|
|
41
21
|
|
|
42
|
-
|
|
43
|
-
def serialization_scope(scope)
|
|
44
|
-
self._serialization_scope = scope
|
|
45
|
-
end
|
|
22
|
+
attr_writer :namespace_for_serializer
|
|
46
23
|
end
|
|
47
24
|
|
|
48
|
-
def
|
|
49
|
-
|
|
25
|
+
def namespace_for_serializer
|
|
26
|
+
@namespace_for_serializer ||= namespace_for_class(self.class) unless namespace_for_class(self.class) == Object
|
|
27
|
+
end
|
|
50
28
|
|
|
51
|
-
|
|
52
|
-
|
|
29
|
+
def namespace_for_class(klass)
|
|
30
|
+
if Module.method_defined?(:module_parent)
|
|
31
|
+
klass.module_parent
|
|
53
32
|
else
|
|
54
|
-
|
|
33
|
+
klass.parent
|
|
55
34
|
end
|
|
56
35
|
end
|
|
57
36
|
|
|
58
|
-
|
|
37
|
+
def serialization_scope
|
|
38
|
+
return unless _serialization_scope && respond_to?(_serialization_scope, true)
|
|
59
39
|
|
|
60
|
-
|
|
61
|
-
{}
|
|
40
|
+
send(_serialization_scope)
|
|
62
41
|
end
|
|
63
42
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
43
|
+
def get_serializer(resource, options = {})
|
|
44
|
+
unless use_adapter?
|
|
45
|
+
warn 'ActionController::Serialization#use_adapter? has been removed. '\
|
|
46
|
+
"Please pass 'adapter: false' or see ActiveSupport::SerializableResource.new"
|
|
47
|
+
options[:adapter] = false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
options.fetch(:namespace) { options[:namespace] = namespace_for_serializer }
|
|
68
51
|
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)
|
|
53
|
+
serializable_resource.serialization_scope ||= options.fetch(:scope) { serialization_scope }
|
|
54
|
+
serializable_resource.serialization_scope_name = options.fetch(:scope_name) { _serialization_scope }
|
|
55
|
+
# For compatibility with the JSON renderer: `json.to_json(options) if json.is_a?(String)`.
|
|
56
|
+
# Otherwise, since `serializable_resource` is not a string, the renderer would call
|
|
57
|
+
# `to_json` on a String and given odd results, such as `"".to_json #=> '""'`
|
|
58
|
+
serializable_resource.adapter.is_a?(String) ? serializable_resource.adapter : serializable_resource
|
|
59
|
+
end
|
|
71
60
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
61
|
+
# Deprecated
|
|
62
|
+
def use_adapter?
|
|
63
|
+
true
|
|
64
|
+
end
|
|
75
65
|
|
|
76
|
-
|
|
66
|
+
[:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
|
|
67
|
+
define_method renderer_method do |resource, options|
|
|
68
|
+
options.fetch(:serialization_context) do
|
|
69
|
+
options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request, options)
|
|
70
|
+
end
|
|
71
|
+
serializable_resource = get_serializer(resource, options)
|
|
72
|
+
super(serializable_resource, options)
|
|
77
73
|
end
|
|
78
74
|
end
|
|
79
75
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module ActiveModel
|
|
6
|
+
class SerializableResource
|
|
7
|
+
class << self
|
|
8
|
+
extend ActiveModelSerializers::Deprecate
|
|
9
|
+
|
|
10
|
+
delegate_and_deprecate :new, ActiveModelSerializers::SerializableResource
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -1,268 +1,425 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require '
|
|
4
|
-
require 'active_model/serializer/
|
|
5
|
-
|
|
6
|
-
require '
|
|
7
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'jsonapi/include_directive'
|
|
4
|
+
require 'active_model/serializer/collection_serializer'
|
|
5
|
+
require 'active_model/serializer/array_serializer'
|
|
6
|
+
require 'active_model/serializer/error_serializer'
|
|
7
|
+
require 'active_model/serializer/errors_serializer'
|
|
8
|
+
require 'active_model/serializer/concerns/caching'
|
|
9
|
+
require 'active_model/serializer/fieldset'
|
|
10
|
+
require 'active_model/serializer/lint'
|
|
11
|
+
|
|
12
|
+
# ActiveModel::Serializer is an abstract class that is
|
|
13
|
+
# reified when subclassed to decorate a resource.
|
|
8
14
|
module ActiveModel
|
|
9
15
|
class Serializer
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
undef_method :select, :display # These IO methods, which are mixed into Kernel,
|
|
17
|
+
# sometimes conflict with attribute names. We don't need these IO methods.
|
|
18
|
+
|
|
19
|
+
# @see #serializable_hash for more details on these valid keys.
|
|
20
|
+
SERIALIZABLE_HASH_VALID_KEYS = [:only, :except, :methods, :include, :root].freeze
|
|
21
|
+
extend ActiveSupport::Autoload
|
|
22
|
+
eager_autoload do
|
|
23
|
+
autoload :Adapter
|
|
24
|
+
autoload :Null
|
|
25
|
+
autoload :Attribute
|
|
26
|
+
autoload :Link
|
|
27
|
+
autoload :Association
|
|
28
|
+
autoload :Reflection
|
|
29
|
+
autoload :BelongsToReflection
|
|
30
|
+
autoload :HasOneReflection
|
|
31
|
+
autoload :HasManyReflection
|
|
32
|
+
end
|
|
33
|
+
include ActiveSupport::Configurable
|
|
34
|
+
include Caching
|
|
35
|
+
|
|
36
|
+
# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]
|
|
37
|
+
# @return [ActiveModel::Serializer]
|
|
38
|
+
# Preferentially returns
|
|
39
|
+
# 1. resource.serializer_class
|
|
40
|
+
# 2. ArraySerializer when resource is a collection
|
|
41
|
+
# 3. options[:serializer]
|
|
42
|
+
# 4. lookup serializer when resource is a Class
|
|
43
|
+
def self.serializer_for(resource_or_class, options = {})
|
|
44
|
+
if resource_or_class.respond_to?(:serializer_class)
|
|
45
|
+
resource_or_class.serializer_class
|
|
46
|
+
elsif resource_or_class.respond_to?(:to_ary)
|
|
47
|
+
config.collection_serializer
|
|
48
|
+
else
|
|
49
|
+
resource_class = resource_or_class.class == Class ? resource_or_class : resource_or_class.class
|
|
50
|
+
options.fetch(:serializer) { get_serializer_for(resource_class, options[:namespace]) }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
13
53
|
|
|
54
|
+
# @see ActiveModelSerializers::Adapter.lookup
|
|
55
|
+
# Deprecated
|
|
56
|
+
def self.adapter
|
|
57
|
+
ActiveModelSerializers::Adapter.lookup(config.adapter)
|
|
58
|
+
end
|
|
14
59
|
class << self
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
base._associations = (_associations || {}).dup
|
|
19
|
-
end
|
|
60
|
+
extend ActiveModelSerializers::Deprecate
|
|
61
|
+
deprecate :adapter, 'ActiveModelSerializers::Adapter.configured_adapter'
|
|
62
|
+
end
|
|
20
63
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
64
|
+
# @api private
|
|
65
|
+
def self.serializer_lookup_chain_for(klass, namespace = nil)
|
|
66
|
+
lookups = ActiveModelSerializers.config.serializer_lookup_chain
|
|
67
|
+
Array[*lookups].flat_map do |lookup|
|
|
68
|
+
lookup.call(klass, self, namespace)
|
|
69
|
+
end.compact
|
|
70
|
+
end
|
|
26
71
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
72
|
+
# Used to cache serializer name => serializer class
|
|
73
|
+
# when looked up by Serializer.get_serializer_for.
|
|
74
|
+
def self.serializers_cache
|
|
75
|
+
@serializers_cache ||= Concurrent::Map.new
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @api private
|
|
79
|
+
# Find a serializer from a class and caches the lookup.
|
|
80
|
+
# Preferentially returns:
|
|
81
|
+
# 1. class name appended with "Serializer"
|
|
82
|
+
# 2. try again with superclass, if present
|
|
83
|
+
# 3. nil
|
|
84
|
+
def self.get_serializer_for(klass, namespace = nil)
|
|
85
|
+
return nil unless config.serializer_lookup_enabled
|
|
86
|
+
|
|
87
|
+
cache_key = ActiveSupport::Cache.expand_cache_key(klass, namespace)
|
|
88
|
+
serializers_cache.fetch_or_store(cache_key) do
|
|
89
|
+
# NOTE(beauby): When we drop 1.9.3 support we can lazify the map for perfs.
|
|
90
|
+
lookup_chain = serializer_lookup_chain_for(klass, namespace)
|
|
91
|
+
serializer_class = lookup_chain.map(&:safe_constantize).find { |x| x && x < ActiveModel::Serializer }
|
|
92
|
+
|
|
93
|
+
if serializer_class
|
|
94
|
+
serializer_class
|
|
95
|
+
elsif klass.superclass
|
|
96
|
+
get_serializer_for(klass.superclass, namespace)
|
|
97
|
+
else
|
|
98
|
+
nil # No serializer found
|
|
41
99
|
end
|
|
42
|
-
ActiveSupport::Deprecation.warn <<-WARN
|
|
43
|
-
** Notice: embed is deprecated. **
|
|
44
|
-
The use of .embed method on a Serializer will be soon removed, as this should have a global scope and not a class scope.
|
|
45
|
-
Please use the global .setup method instead:
|
|
46
|
-
ActiveModel::Serializer.setup do |config|
|
|
47
|
-
config.embed = :#{type}
|
|
48
|
-
config.embed_in_root = #{CONFIG.embed_in_root || false}
|
|
49
|
-
end
|
|
50
|
-
WARN
|
|
51
100
|
end
|
|
101
|
+
end
|
|
52
102
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def serializer_for(resource)
|
|
60
|
-
if resource.respond_to?(:to_ary)
|
|
61
|
-
if Object.constants.include?(:ArraySerializer)
|
|
62
|
-
::ArraySerializer
|
|
63
|
-
else
|
|
64
|
-
ArraySerializer
|
|
65
|
-
end
|
|
66
|
-
else
|
|
67
|
-
begin
|
|
68
|
-
Object.const_get "#{resource.class.name}Serializer"
|
|
69
|
-
rescue NameError
|
|
70
|
-
nil
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
103
|
+
# @api private
|
|
104
|
+
def self.include_directive_from_options(options)
|
|
105
|
+
if options[:include_directive]
|
|
106
|
+
options[:include_directive]
|
|
107
|
+
elsif options[:include]
|
|
108
|
+
JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
|
|
74
109
|
else
|
|
75
|
-
|
|
76
|
-
if resource.respond_to?(:to_ary)
|
|
77
|
-
if Object.constants.include?(:ArraySerializer)
|
|
78
|
-
::ArraySerializer
|
|
79
|
-
else
|
|
80
|
-
ArraySerializer
|
|
81
|
-
end
|
|
82
|
-
else
|
|
83
|
-
"#{resource.class.name}Serializer".safe_constantize
|
|
84
|
-
end
|
|
85
|
-
end
|
|
110
|
+
ActiveModelSerializers.default_include_directive
|
|
86
111
|
end
|
|
112
|
+
end
|
|
87
113
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
114
|
+
# @api private
|
|
115
|
+
def self.serialization_adapter_instance
|
|
116
|
+
@serialization_adapter_instance ||= ActiveModelSerializers::Adapter::Attributes
|
|
117
|
+
end
|
|
91
118
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
119
|
+
# Preferred interface is ActiveModelSerializers.config
|
|
120
|
+
# BEGIN DEFAULT CONFIGURATION
|
|
121
|
+
config.collection_serializer = ActiveModel::Serializer::CollectionSerializer
|
|
122
|
+
config.serializer_lookup_enabled = true
|
|
95
123
|
|
|
96
|
-
|
|
97
|
-
|
|
124
|
+
# @deprecated Use {#config.collection_serializer=} instead of this. Is
|
|
125
|
+
# compatibility layer for ArraySerializer.
|
|
126
|
+
def config.array_serializer=(collection_serializer)
|
|
127
|
+
self.collection_serializer = collection_serializer
|
|
128
|
+
end
|
|
98
129
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end
|
|
130
|
+
# @deprecated Use {#config.collection_serializer} instead of this. Is
|
|
131
|
+
# compatibility layer for ArraySerializer.
|
|
132
|
+
def config.array_serializer
|
|
133
|
+
collection_serializer
|
|
134
|
+
end
|
|
105
135
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
136
|
+
config.default_includes = '*'
|
|
137
|
+
config.adapter = :attributes
|
|
138
|
+
config.key_transform = nil
|
|
139
|
+
config.jsonapi_pagination_links_enabled = true
|
|
140
|
+
config.jsonapi_resource_type = :plural
|
|
141
|
+
config.jsonapi_namespace_separator = '-'.freeze
|
|
142
|
+
config.jsonapi_version = '1.0'
|
|
143
|
+
config.jsonapi_toplevel_meta = {}
|
|
144
|
+
# Make JSON API top-level jsonapi member opt-in
|
|
145
|
+
# ref: http://jsonapi.org/format/#document-top-level
|
|
146
|
+
config.jsonapi_include_toplevel_object = false
|
|
147
|
+
config.jsonapi_use_foreign_key_on_belongs_to_relationship = false
|
|
148
|
+
config.include_data_default = true
|
|
149
|
+
|
|
150
|
+
# For configuring how serializers are found.
|
|
151
|
+
# This should be an array of procs.
|
|
152
|
+
#
|
|
153
|
+
# The priority of the output is that the first item
|
|
154
|
+
# in the evaluated result array will take precedence
|
|
155
|
+
# over other possible serializer paths.
|
|
156
|
+
#
|
|
157
|
+
# i.e.: First match wins.
|
|
158
|
+
#
|
|
159
|
+
# @example output
|
|
160
|
+
# => [
|
|
161
|
+
# "CustomNamespace::ResourceSerializer",
|
|
162
|
+
# "ParentSerializer::ResourceSerializer",
|
|
163
|
+
# "ResourceNamespace::ResourceSerializer" ,
|
|
164
|
+
# "ResourceSerializer"]
|
|
165
|
+
#
|
|
166
|
+
# If CustomNamespace::ResourceSerializer exists, it will be used
|
|
167
|
+
# for serialization
|
|
168
|
+
config.serializer_lookup_chain = ActiveModelSerializers::LookupChain::DEFAULT.dup
|
|
169
|
+
|
|
170
|
+
config.schema_path = 'test/support/schemas'
|
|
171
|
+
# END DEFAULT CONFIGURATION
|
|
172
|
+
|
|
173
|
+
with_options instance_writer: false, instance_reader: false do |serializer|
|
|
174
|
+
serializer.class_attribute :_attributes_data # @api private
|
|
175
|
+
self._attributes_data ||= {}
|
|
176
|
+
end
|
|
177
|
+
with_options instance_writer: false, instance_reader: true do |serializer|
|
|
178
|
+
serializer.class_attribute :_reflections
|
|
179
|
+
self._reflections ||= {}
|
|
180
|
+
serializer.class_attribute :_links # @api private
|
|
181
|
+
self._links ||= {}
|
|
182
|
+
serializer.class_attribute :_meta # @api private
|
|
183
|
+
serializer.class_attribute :_type # @api private
|
|
184
|
+
end
|
|
109
185
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
186
|
+
def self.inherited(base)
|
|
187
|
+
super
|
|
188
|
+
base._attributes_data = _attributes_data.dup
|
|
189
|
+
base._reflections = _reflections.dup
|
|
190
|
+
base._links = _links.dup
|
|
191
|
+
end
|
|
113
192
|
|
|
114
|
-
|
|
193
|
+
# @return [Array<Symbol>] Key names of declared attributes
|
|
194
|
+
# @see Serializer::attribute
|
|
195
|
+
def self._attributes
|
|
196
|
+
_attributes_data.keys
|
|
197
|
+
end
|
|
115
198
|
|
|
116
|
-
|
|
117
|
-
options = attrs.extract_options!
|
|
199
|
+
# BEGIN SERIALIZER MACROS
|
|
118
200
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
201
|
+
# @example
|
|
202
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
203
|
+
# attributes :id, :name, :recent_edits
|
|
204
|
+
def self.attributes(*attrs)
|
|
205
|
+
attrs = attrs.first if attrs.first.class == Array
|
|
123
206
|
|
|
124
|
-
|
|
125
|
-
|
|
207
|
+
attrs.each do |attr|
|
|
208
|
+
attribute(attr)
|
|
126
209
|
end
|
|
127
210
|
end
|
|
128
211
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
212
|
+
# @example
|
|
213
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
214
|
+
# attributes :id, :recent_edits
|
|
215
|
+
# attribute :name, key: :title
|
|
216
|
+
#
|
|
217
|
+
# attribute :full_name do
|
|
218
|
+
# "#{object.first_name} #{object.last_name}"
|
|
219
|
+
# end
|
|
220
|
+
#
|
|
221
|
+
# def recent_edits
|
|
222
|
+
# object.edits.last(5)
|
|
223
|
+
# end
|
|
224
|
+
def self.attribute(attr, options = {}, &block)
|
|
225
|
+
key = options.fetch(:key, attr)
|
|
226
|
+
_attributes_data[key] = Attribute.new(attr, options, block)
|
|
140
227
|
end
|
|
141
|
-
attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context
|
|
142
228
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
229
|
+
# @param [Symbol] name of the association
|
|
230
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
231
|
+
# @return [void]
|
|
232
|
+
#
|
|
233
|
+
# @example
|
|
234
|
+
# has_many :comments, serializer: CommentSummarySerializer
|
|
235
|
+
#
|
|
236
|
+
def self.has_many(name, options = {}, &block) # rubocop:disable Style/PredicateName
|
|
237
|
+
associate(HasManyReflection.new(name, options, block))
|
|
238
|
+
end
|
|
149
239
|
|
|
150
|
-
|
|
240
|
+
# @param [Symbol] name of the association
|
|
241
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
242
|
+
# @return [void]
|
|
243
|
+
#
|
|
244
|
+
# @example
|
|
245
|
+
# belongs_to :author, serializer: AuthorSerializer
|
|
246
|
+
#
|
|
247
|
+
def self.belongs_to(name, options = {}, &block)
|
|
248
|
+
associate(BelongsToReflection.new(name, options, block))
|
|
151
249
|
end
|
|
152
250
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
251
|
+
# @param [Symbol] name of the association
|
|
252
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
253
|
+
# @return [void]
|
|
254
|
+
#
|
|
255
|
+
# @example
|
|
256
|
+
# has_one :author, serializer: AuthorSerializer
|
|
257
|
+
#
|
|
258
|
+
def self.has_one(name, options = {}, &block) # rubocop:disable Style/PredicateName
|
|
259
|
+
associate(HasOneReflection.new(name, options, block))
|
|
157
260
|
end
|
|
158
261
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
262
|
+
# Add reflection and define {name} accessor.
|
|
263
|
+
# @param [ActiveModel::Serializer::Reflection] reflection
|
|
264
|
+
# @return [void]
|
|
265
|
+
#
|
|
266
|
+
# @api private
|
|
267
|
+
def self.associate(reflection)
|
|
268
|
+
key = reflection.options[:key] || reflection.name
|
|
269
|
+
self._reflections[key] = reflection
|
|
270
|
+
end
|
|
271
|
+
private_class_method :associate
|
|
272
|
+
|
|
273
|
+
# Define a link on a serializer.
|
|
274
|
+
# @example
|
|
275
|
+
# link(:self) { resource_url(object) }
|
|
276
|
+
# @example
|
|
277
|
+
# link(:self) { "http://example.com/resource/#{object.id}" }
|
|
278
|
+
# @example
|
|
279
|
+
# link :resource, "http://example.com/resource"
|
|
280
|
+
# @example
|
|
281
|
+
# link(:callback, if: :internal?), { "http://example.com/callback" }
|
|
282
|
+
#
|
|
283
|
+
def self.link(name, *args, &block)
|
|
284
|
+
options = args.extract_options!
|
|
285
|
+
# For compatibility with the use case of passing link directly as string argument
|
|
286
|
+
# without block, we are creating a wrapping block
|
|
287
|
+
_links[name] = Link.new(name, options, block || ->(_serializer) { args.first })
|
|
180
288
|
end
|
|
181
289
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
290
|
+
# Set the JSON API meta attribute of a serializer.
|
|
291
|
+
# @example
|
|
292
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
293
|
+
# meta { stuff: 'value' }
|
|
294
|
+
# @example
|
|
295
|
+
# meta do
|
|
296
|
+
# { comment_count: object.comments.count }
|
|
297
|
+
# end
|
|
298
|
+
def self.meta(value = nil, &block)
|
|
299
|
+
self._meta = block || value
|
|
190
300
|
end
|
|
191
301
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if association.embed_in_root_key?
|
|
199
|
-
hash = hash[association.embed_in_root_key] ||= {}
|
|
200
|
-
end
|
|
201
|
-
association_serializer = build_serializer(association)
|
|
202
|
-
hash.merge!(association_serializer.embedded_in_root_associations) {|key, oldval, newval| [newval, oldval].flatten }
|
|
203
|
-
|
|
204
|
-
serialized_data = association_serializer.serializable_object
|
|
205
|
-
key = association.root_key
|
|
206
|
-
if hash.has_key?(key)
|
|
207
|
-
hash[key].concat(serialized_data).uniq!
|
|
208
|
-
else
|
|
209
|
-
hash[key] = serialized_data
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
end
|
|
302
|
+
# Set the JSON API type of a serializer.
|
|
303
|
+
# @example
|
|
304
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
305
|
+
# type 'authors'
|
|
306
|
+
def self.type(type)
|
|
307
|
+
self._type = type && type.to_s
|
|
214
308
|
end
|
|
215
309
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
310
|
+
# END SERIALIZER MACROS
|
|
311
|
+
|
|
312
|
+
attr_accessor :object, :root, :scope
|
|
313
|
+
|
|
314
|
+
# `scope_name` is set as :current_user by default in the controller.
|
|
315
|
+
# If the instance does not have a method named `scope_name`, it
|
|
316
|
+
# defines the method so that it calls the +scope+.
|
|
317
|
+
def initialize(object, options = {})
|
|
318
|
+
self.object = object
|
|
319
|
+
self.instance_options = options
|
|
320
|
+
self.root = instance_options[:root]
|
|
321
|
+
self.scope = instance_options[:scope]
|
|
322
|
+
|
|
323
|
+
return if !(scope_name = instance_options[:scope_name]) || respond_to?(scope_name)
|
|
324
|
+
|
|
325
|
+
define_singleton_method scope_name, -> { scope }
|
|
219
326
|
end
|
|
220
327
|
|
|
221
|
-
def
|
|
222
|
-
|
|
328
|
+
def success?
|
|
329
|
+
true
|
|
223
330
|
end
|
|
224
331
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
332
|
+
# Return the +attributes+ of +object+ as presented
|
|
333
|
+
# by the serializer.
|
|
334
|
+
def attributes(requested_attrs = nil, reload = false)
|
|
335
|
+
@attributes = nil if reload
|
|
336
|
+
@attributes ||= self.class._attributes_data.each_with_object({}) do |(key, attr), hash|
|
|
337
|
+
next if attr.excluded?(self)
|
|
338
|
+
next unless requested_attrs.nil? || requested_attrs.include?(key)
|
|
339
|
+
hash[key] = attr.value(self)
|
|
231
340
|
end
|
|
232
341
|
end
|
|
233
342
|
|
|
234
|
-
|
|
235
|
-
|
|
343
|
+
# @param [JSONAPI::IncludeDirective] include_directive (defaults to the
|
|
344
|
+
# +default_include_directive+ config value when not provided)
|
|
345
|
+
# @return [Enumerator<Association>]
|
|
346
|
+
def associations(include_directive = ActiveModelSerializers.default_include_directive, include_slice = nil)
|
|
347
|
+
include_slice ||= include_directive
|
|
348
|
+
return Enumerator.new {} unless object
|
|
349
|
+
|
|
350
|
+
Enumerator.new do |y|
|
|
351
|
+
(self.instance_reflections ||= self.class._reflections.deep_dup).each do |key, reflection|
|
|
352
|
+
next if reflection.excluded?(self)
|
|
353
|
+
next unless include_directive.key?(key)
|
|
354
|
+
|
|
355
|
+
association = reflection.build_association(self, instance_options, include_slice)
|
|
356
|
+
y.yield association
|
|
357
|
+
end
|
|
358
|
+
end
|
|
236
359
|
end
|
|
237
360
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
361
|
+
# @return [Hash] containing the attributes and first level
|
|
362
|
+
# associations, similar to how ActiveModel::Serializers::JSON is used
|
|
363
|
+
# in ActiveRecord::Base.
|
|
364
|
+
def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance)
|
|
365
|
+
adapter_options ||= {}
|
|
366
|
+
options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
|
|
367
|
+
if (fieldset = adapter_options[:fieldset])
|
|
368
|
+
options[:fields] = fieldset.fields_for(json_key)
|
|
243
369
|
end
|
|
370
|
+
resource = attributes_hash(adapter_options, options, adapter_instance)
|
|
371
|
+
relationships = associations_hash(adapter_options, options, adapter_instance)
|
|
372
|
+
resource.merge(relationships)
|
|
244
373
|
end
|
|
374
|
+
alias to_hash serializable_hash
|
|
375
|
+
alias to_h serializable_hash
|
|
245
376
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
377
|
+
# @see #serializable_hash
|
|
378
|
+
def as_json(adapter_opts = nil)
|
|
379
|
+
serializable_hash(adapter_opts)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Used by adapter as resource root.
|
|
383
|
+
def json_key
|
|
384
|
+
root || _type ||
|
|
385
|
+
begin
|
|
386
|
+
object.class.model_name.to_s.underscore
|
|
387
|
+
rescue ArgumentError
|
|
388
|
+
'anonymous_object'
|
|
252
389
|
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def read_attribute_for_serialization(attr)
|
|
393
|
+
if respond_to?(attr)
|
|
394
|
+
send(attr)
|
|
395
|
+
else
|
|
396
|
+
object.read_attribute_for_serialization(attr)
|
|
397
|
+
end
|
|
398
|
+
end
|
|
253
399
|
|
|
254
|
-
|
|
255
|
-
|
|
400
|
+
# @api private
|
|
401
|
+
def attributes_hash(_adapter_options, options, adapter_instance)
|
|
402
|
+
if self.class.cache_enabled?
|
|
403
|
+
fetch_attributes(options[:fields], options[:cached_attributes] || {}, adapter_instance)
|
|
404
|
+
elsif self.class.fragment_cache_enabled?
|
|
405
|
+
fetch_attributes_fragment(adapter_instance, options[:cached_attributes] || {})
|
|
406
|
+
else
|
|
407
|
+
attributes(options[:fields], true)
|
|
408
|
+
end
|
|
256
409
|
end
|
|
257
410
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
411
|
+
# @api private
|
|
412
|
+
def associations_hash(adapter_options, options, adapter_instance)
|
|
413
|
+
include_directive = options.fetch(:include_directive)
|
|
414
|
+
include_slice = options[:include_slice]
|
|
415
|
+
associations(include_directive, include_slice).each_with_object({}) do |association, relationships|
|
|
416
|
+
adapter_opts = adapter_options.merge(include_directive: include_directive[association.key], adapter_instance: adapter_instance)
|
|
417
|
+
relationships[association.key] = association.serializable_hash(adapter_opts, adapter_instance)
|
|
418
|
+
end
|
|
264
419
|
end
|
|
265
|
-
alias_method :serializable_hash, :serializable_object
|
|
266
|
-
end
|
|
267
420
|
|
|
421
|
+
protected
|
|
422
|
+
|
|
423
|
+
attr_accessor :instance_options, :instance_reflections
|
|
424
|
+
end
|
|
268
425
|
end
|