active_model_serializers 0.9.9 → 0.10.14
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/CHANGELOG.md +634 -61
- data/MIT-LICENSE +3 -2
- data/README.md +194 -846
- data/lib/action_controller/serialization.rb +35 -66
- data/lib/active_model/serializable_resource.rb +13 -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/adapter.rb +26 -0
- data/lib/active_model/serializer/array_serializer.rb +14 -0
- data/lib/active_model/serializer/association.rb +55 -40
- 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 +99 -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/serializer.rb +363 -269
- 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/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 +94 -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/json_api.rb +535 -0
- data/lib/active_model_serializers/adapter/null.rb +11 -0
- data/lib/active_model_serializers/adapter.rb +100 -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/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/schema.rb +140 -0
- data/lib/active_model_serializers/test/serializer.rb +127 -0
- data/lib/active_model_serializers/test.rb +9 -0
- data/lib/active_model_serializers.rb +60 -17
- 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/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 +60 -0
- metadata +248 -155
- 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 -68
- data/lib/active_model/default_serializer.rb +0 -28
- data/lib/active_model/serializable/utils.rb +0 -16
- data/lib/active_model/serializable.rb +0 -59
- data/lib/active_model/serializer/association/has_many.rb +0 -39
- data/lib/active_model/serializer/association/has_one.rb +0 -25
- 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 -22
- data/lib/active_model/serializer_support.rb +0 -5
- data/test/benchmark/app.rb +0 -60
- data/test/benchmark/benchmarking_support.rb +0 -67
- data/test/benchmark/bm_active_record.rb +0 -41
- data/test/benchmark/setup.rb +0 -75
- data/test/benchmark/tmp/miniprofiler/mp_timers_6eqewtfgrhitvq5gqm25 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_8083sx03hu72pxz1a4d0 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_fyz2gsml4z0ph9kpoy1c +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_hjry5rc32imd42oxoi48 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_m8fpoz2cvt3g9agz0bs3 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_p92m2drnj1i568u3sta0 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_qg52tpca3uesdfguee9i +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_s15t1a6mvxe0z7vjv790 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_x8kal3d17nfds6vp4kcj +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_views_127.0.0.1 +0 -0
- data/test/fixtures/active_record.rb +0 -96
- data/test/fixtures/poro.rb +0 -254
- data/test/fixtures/template.html.erb +0 -1
- data/test/integration/action_controller/namespaced_serialization_test.rb +0 -105
- data/test/integration/action_controller/serialization_test.rb +0 -287
- data/test/integration/action_controller/serialization_test_case_test.rb +0 -71
- data/test/integration/active_record/active_record_test.rb +0 -94
- 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 -14
- data/test/test_helper.rb +0 -26
- data/test/tmp/app/assets/javascripts/accounts.js +0 -2
- data/test/tmp/app/assets/stylesheets/accounts.css +0 -4
- data/test/tmp/app/controllers/accounts_controller.rb +0 -3
- data/test/tmp/app/helpers/accounts_helper.rb +0 -3
- data/test/tmp/app/serializers/account_serializer.rb +0 -4
- data/test/tmp/config/routes.rb +0 -2
- 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/options_test.rb +0 -16
- 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 -239
- data/test/unit/active_model/default_serializer_test.rb +0 -13
- data/test/unit/active_model/serializer/associations/build_serializer_test.rb +0 -36
- data/test/unit/active_model/serializer/associations_test.rb +0 -49
- data/test/unit/active_model/serializer/attributes_test.rb +0 -57
- data/test/unit/active_model/serializer/config_test.rb +0 -91
- data/test/unit/active_model/serializer/filter_test.rb +0 -69
- data/test/unit/active_model/serializer/has_many_polymorphic_test.rb +0 -189
- data/test/unit/active_model/serializer/has_many_test.rb +0 -265
- data/test/unit/active_model/serializer/has_one_and_has_many_test.rb +0 -27
- data/test/unit/active_model/serializer/has_one_polymorphic_test.rb +0 -196
- data/test/unit/active_model/serializer/has_one_test.rb +0 -253
- 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 -42
- data/test/unit/active_model/serializer/root_test.rb +0 -117
- data/test/unit/active_model/serializer/scope_test.rb +0 -49
- data/test/unit/active_model/serializer/url_helpers_test.rb +0 -35
- data/test/unit/active_model/serilizable_test.rb +0 -50
- /data/lib/{active_model/serializer/generators/serializer/templates/serializer.rb → generators/rails/templates/serializer.rb.erb} +0 -0
|
@@ -1,333 +1,427 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require '
|
|
4
|
-
require 'active_model/serializer/
|
|
5
|
-
|
|
6
|
-
require '
|
|
7
|
-
require '
|
|
8
|
-
|
|
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.
|
|
9
14
|
module ActiveModel
|
|
10
15
|
class Serializer
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
14
53
|
|
|
54
|
+
# @see ActiveModelSerializers::Adapter.lookup
|
|
55
|
+
# Deprecated
|
|
56
|
+
def self.adapter
|
|
57
|
+
ActiveModelSerializers::Adapter.lookup(config.adapter)
|
|
58
|
+
end
|
|
15
59
|
class << self
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
base._associations = (_associations || {}).dup
|
|
20
|
-
end
|
|
60
|
+
extend ActiveModelSerializers::Deprecate
|
|
61
|
+
deprecate :adapter, 'ActiveModelSerializers::Adapter.configured_adapter'
|
|
62
|
+
end
|
|
21
63
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
27
71
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
].freeze
|
|
34
|
-
|
|
35
|
-
def embed(type, options={})
|
|
36
|
-
CONFIG.embed = type
|
|
37
|
-
if EMBED_IN_ROOT_OPTIONS.any? { |opt| options[opt].present? }
|
|
38
|
-
CONFIG.embed_in_root = true
|
|
39
|
-
end
|
|
40
|
-
if options[:embed_in_root_key].present?
|
|
41
|
-
CONFIG.embed_in_root_key = options[:embed_in_root_key]
|
|
42
|
-
end
|
|
43
|
-
ActiveSupport::Deprecation.warn <<-WARN
|
|
44
|
-
** Notice: embed is deprecated. **
|
|
45
|
-
The use of .embed method on a Serializer will be soon removed, as this should have a global scope and not a class scope.
|
|
46
|
-
Please use the global .setup method instead:
|
|
47
|
-
ActiveModel::Serializer.setup do |config|
|
|
48
|
-
config.embed = :#{type}
|
|
49
|
-
config.embed_in_root = #{CONFIG.embed_in_root || false}
|
|
50
|
-
end
|
|
51
|
-
WARN
|
|
52
|
-
end
|
|
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
|
|
53
77
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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)
|
|
68
97
|
else
|
|
69
|
-
|
|
70
|
-
result = search_list.map do |klass_name|
|
|
71
|
-
Serializer.serializers_cache.fetch_or_store(klass_name) do
|
|
72
|
-
_const_get(klass_name)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
result.find { |serializer| !serializer.nil? }
|
|
98
|
+
nil # No serializer found
|
|
77
99
|
end
|
|
78
100
|
end
|
|
101
|
+
end
|
|
79
102
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
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)
|
|
109
|
+
else
|
|
110
|
+
ActiveModelSerializers.default_include_directive
|
|
89
111
|
end
|
|
112
|
+
end
|
|
90
113
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@_attributes << striped_attr
|
|
114
|
+
# @api private
|
|
115
|
+
def self.serialization_adapter_instance
|
|
116
|
+
@serialization_adapter_instance ||= ActiveModelSerializers::Adapter::Attributes
|
|
117
|
+
end
|
|
96
118
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
end
|
|
119
|
+
# Preferred interface is ActiveModelSerializers.config
|
|
120
|
+
# BEGIN DEFAULT CONFIGURATION
|
|
121
|
+
config.collection_serializer = ActiveModel::Serializer::CollectionSerializer
|
|
122
|
+
config.serializer_lookup_enabled = true
|
|
102
123
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|
106
129
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
110
135
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
+
# Raise ActiveModel::Serializer::CollectionSerializer::CannotInferRootKeyError when cannot infer root key from collection type
|
|
150
|
+
config.raise_cannot_infer_root_key_error = true
|
|
151
|
+
|
|
152
|
+
# For configuring how serializers are found.
|
|
153
|
+
# This should be an array of procs.
|
|
154
|
+
#
|
|
155
|
+
# The priority of the output is that the first item
|
|
156
|
+
# in the evaluated result array will take precedence
|
|
157
|
+
# over other possible serializer paths.
|
|
158
|
+
#
|
|
159
|
+
# i.e.: First match wins.
|
|
160
|
+
#
|
|
161
|
+
# @example output
|
|
162
|
+
# => [
|
|
163
|
+
# "CustomNamespace::ResourceSerializer",
|
|
164
|
+
# "ParentSerializer::ResourceSerializer",
|
|
165
|
+
# "ResourceNamespace::ResourceSerializer" ,
|
|
166
|
+
# "ResourceSerializer"]
|
|
167
|
+
#
|
|
168
|
+
# If CustomNamespace::ResourceSerializer exists, it will be used
|
|
169
|
+
# for serialization
|
|
170
|
+
config.serializer_lookup_chain = ActiveModelSerializers::LookupChain::DEFAULT.dup
|
|
171
|
+
|
|
172
|
+
config.schema_path = 'test/support/schemas'
|
|
173
|
+
# END DEFAULT CONFIGURATION
|
|
174
|
+
|
|
175
|
+
with_options instance_writer: false, instance_reader: false do |serializer|
|
|
176
|
+
serializer.class_attribute :_attributes_data # @api private
|
|
177
|
+
self._attributes_data ||= {}
|
|
178
|
+
end
|
|
179
|
+
with_options instance_writer: false, instance_reader: true do |serializer|
|
|
180
|
+
serializer.class_attribute :_reflections
|
|
181
|
+
self._reflections ||= {}
|
|
182
|
+
serializer.class_attribute :_links # @api private
|
|
183
|
+
self._links ||= {}
|
|
184
|
+
serializer.class_attribute :_meta # @api private
|
|
185
|
+
serializer.class_attribute :_type # @api private
|
|
186
|
+
end
|
|
114
187
|
|
|
115
|
-
|
|
188
|
+
def self.inherited(base)
|
|
189
|
+
super
|
|
190
|
+
base._attributes_data = _attributes_data.dup
|
|
191
|
+
base._reflections = _reflections.dup
|
|
192
|
+
base._links = _links.dup
|
|
193
|
+
end
|
|
116
194
|
|
|
117
|
-
|
|
118
|
-
|
|
195
|
+
# @return [Array<Symbol>] Key names of declared attributes
|
|
196
|
+
# @see Serializer::attribute
|
|
197
|
+
def self._attributes
|
|
198
|
+
_attributes_data.keys
|
|
199
|
+
end
|
|
119
200
|
|
|
120
|
-
|
|
121
|
-
attr = attr.to_sym if symbolized
|
|
122
|
-
attr
|
|
123
|
-
end
|
|
201
|
+
# BEGIN SERIALIZER MACROS
|
|
124
202
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
end
|
|
203
|
+
# @example
|
|
204
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
205
|
+
# attributes :id, :name, :recent_edits
|
|
206
|
+
def self.attributes(*attrs)
|
|
207
|
+
attrs = attrs.first if attrs.first.class == Array
|
|
131
208
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
klass_name << "#{options[:namespace]}::" if options[:namespace]
|
|
135
|
-
klass_name << options[:prefix].to_s.classify if options[:prefix]
|
|
136
|
-
if resource.is_a?(String)
|
|
137
|
-
klass_name << "#{resource}Serializer"
|
|
138
|
-
else
|
|
139
|
-
klass_name << "#{resource.class.name}Serializer"
|
|
140
|
-
end
|
|
141
|
-
end
|
|
209
|
+
attrs.each do |attr|
|
|
210
|
+
attribute(attr)
|
|
142
211
|
end
|
|
212
|
+
end
|
|
143
213
|
|
|
144
|
-
|
|
145
|
-
|
|
214
|
+
# @example
|
|
215
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
216
|
+
# attributes :id, :recent_edits
|
|
217
|
+
# attribute :name, key: :title
|
|
218
|
+
#
|
|
219
|
+
# attribute :full_name do
|
|
220
|
+
# "#{object.first_name} #{object.last_name}"
|
|
221
|
+
# end
|
|
222
|
+
#
|
|
223
|
+
# def recent_edits
|
|
224
|
+
# object.edits.last(5)
|
|
225
|
+
# end
|
|
226
|
+
def self.attribute(attr, options = {}, &block)
|
|
227
|
+
key = options.fetch(:key, attr)
|
|
228
|
+
_attributes_data[key] = Attribute.new(attr, options, block)
|
|
229
|
+
end
|
|
146
230
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
231
|
+
# @param [Symbol] name of the association
|
|
232
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
233
|
+
# @return [void]
|
|
234
|
+
#
|
|
235
|
+
# @example
|
|
236
|
+
# has_many :comments, serializer: CommentSummarySerializer
|
|
237
|
+
#
|
|
238
|
+
def self.has_many(name, options = {}, &block) # rubocop:disable Style/PredicateName
|
|
239
|
+
associate(HasManyReflection.new(name, options, block))
|
|
240
|
+
end
|
|
151
241
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
242
|
+
# @param [Symbol] name of the association
|
|
243
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
244
|
+
# @return [void]
|
|
245
|
+
#
|
|
246
|
+
# @example
|
|
247
|
+
# belongs_to :author, serializer: AuthorSerializer
|
|
248
|
+
#
|
|
249
|
+
def self.belongs_to(name, options = {}, &block)
|
|
250
|
+
associate(BelongsToReflection.new(name, options, block))
|
|
155
251
|
end
|
|
156
252
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
@except = options[:except] ? Array(options[:except]) : nil
|
|
167
|
-
@key_format = options[:key_format]
|
|
168
|
-
@context = options[:context]
|
|
169
|
-
@namespace = options[:namespace]
|
|
253
|
+
# @param [Symbol] name of the association
|
|
254
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
255
|
+
# @return [void]
|
|
256
|
+
#
|
|
257
|
+
# @example
|
|
258
|
+
# has_one :author, serializer: AuthorSerializer
|
|
259
|
+
#
|
|
260
|
+
def self.has_one(name, options = {}, &block) # rubocop:disable Style/PredicateName
|
|
261
|
+
associate(HasOneReflection.new(name, options, block))
|
|
170
262
|
end
|
|
171
|
-
attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context, :polymorphic
|
|
172
263
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
264
|
+
# Add reflection and define {name} accessor.
|
|
265
|
+
# @param [ActiveModel::Serializer::Reflection] reflection
|
|
266
|
+
# @return [void]
|
|
267
|
+
#
|
|
268
|
+
# @api private
|
|
269
|
+
def self.associate(reflection)
|
|
270
|
+
key = reflection.options[:key] || reflection.name
|
|
271
|
+
self._reflections[key] = reflection
|
|
272
|
+
end
|
|
273
|
+
private_class_method :associate
|
|
274
|
+
|
|
275
|
+
# Define a link on a serializer.
|
|
276
|
+
# @example
|
|
277
|
+
# link(:self) { resource_url(object) }
|
|
278
|
+
# @example
|
|
279
|
+
# link(:self) { "http://example.com/resource/#{object.id}" }
|
|
280
|
+
# @example
|
|
281
|
+
# link :resource, "http://example.com/resource"
|
|
282
|
+
# @example
|
|
283
|
+
# link(:callback, if: :internal?), { "http://example.com/callback" }
|
|
284
|
+
#
|
|
285
|
+
def self.link(name, *args, &block)
|
|
286
|
+
options = args.extract_options!
|
|
287
|
+
# For compatibility with the use case of passing link directly as string argument
|
|
288
|
+
# without block, we are creating a wrapping block
|
|
289
|
+
_links[name] = Link.new(name, options, block || ->(_serializer) { args.first })
|
|
290
|
+
end
|
|
179
291
|
|
|
180
|
-
|
|
292
|
+
# Set the JSON API meta attribute of a serializer.
|
|
293
|
+
# @example
|
|
294
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
295
|
+
# meta { stuff: 'value' }
|
|
296
|
+
# @example
|
|
297
|
+
# meta do
|
|
298
|
+
# { comment_count: object.comments.count }
|
|
299
|
+
# end
|
|
300
|
+
def self.meta(value = nil, &block)
|
|
301
|
+
self._meta = block || value
|
|
181
302
|
end
|
|
182
303
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
304
|
+
# Set the JSON API type of a serializer.
|
|
305
|
+
# @example
|
|
306
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
307
|
+
# type 'authors'
|
|
308
|
+
def self.type(type)
|
|
309
|
+
self._type = type && type.to_s
|
|
187
310
|
end
|
|
188
311
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
end
|
|
206
|
-
hash[association.embedded_key] = serialize association, options
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
end
|
|
312
|
+
# END SERIALIZER MACROS
|
|
313
|
+
|
|
314
|
+
attr_accessor :object, :root, :scope
|
|
315
|
+
|
|
316
|
+
# `scope_name` is set as :current_user by default in the controller.
|
|
317
|
+
# If the instance does not have a method named `scope_name`, it
|
|
318
|
+
# defines the method so that it calls the +scope+.
|
|
319
|
+
def initialize(object, options = {})
|
|
320
|
+
self.object = object
|
|
321
|
+
self.instance_options = options
|
|
322
|
+
self.root = instance_options[:root]
|
|
323
|
+
self.scope = instance_options[:scope]
|
|
324
|
+
|
|
325
|
+
return if !(scope_name = instance_options[:scope_name]) || respond_to?(scope_name)
|
|
326
|
+
|
|
327
|
+
define_singleton_method scope_name, -> { scope }
|
|
210
328
|
end
|
|
211
329
|
|
|
212
|
-
def
|
|
213
|
-
|
|
214
|
-
keys & @only
|
|
215
|
-
elsif @except
|
|
216
|
-
keys - @except
|
|
217
|
-
else
|
|
218
|
-
keys
|
|
219
|
-
end
|
|
330
|
+
def success?
|
|
331
|
+
true
|
|
220
332
|
end
|
|
221
333
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
hash.merge!(association_serializer.embedded_in_root_associations) do |key, oldval, newval|
|
|
231
|
-
if oldval.respond_to?(:to_ary)
|
|
232
|
-
[oldval, newval].flatten.uniq
|
|
233
|
-
else
|
|
234
|
-
oldval.merge(newval) { |_, oldval, newval| [oldval, newval].flatten.uniq }
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
if association.embed_in_root?
|
|
239
|
-
if association.embed_in_root_key?
|
|
240
|
-
hash = hash[association.embed_in_root_key] ||= {}
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
serialized_data = association_serializer.serializable_object
|
|
244
|
-
key = association.root_key
|
|
245
|
-
if hash.has_key?(key)
|
|
246
|
-
hash[key].concat(serialized_data).uniq!
|
|
247
|
-
else
|
|
248
|
-
hash[key] = serialized_data
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
end
|
|
334
|
+
# Return the +attributes+ of +object+ as presented
|
|
335
|
+
# by the serializer.
|
|
336
|
+
def attributes(requested_attrs = nil, reload = false)
|
|
337
|
+
@attributes = nil if reload
|
|
338
|
+
@attributes ||= self.class._attributes_data.each_with_object({}) do |(key, attr), hash|
|
|
339
|
+
next if attr.excluded?(self)
|
|
340
|
+
next unless requested_attrs.nil? || requested_attrs.include?(key)
|
|
341
|
+
hash[key] = attr.value(self)
|
|
252
342
|
end
|
|
253
343
|
end
|
|
254
344
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
345
|
+
# @param [JSONAPI::IncludeDirective] include_directive (defaults to the
|
|
346
|
+
# +default_include_directive+ config value when not provided)
|
|
347
|
+
# @return [Enumerator<Association>]
|
|
348
|
+
def associations(include_directive = ActiveModelSerializers.default_include_directive, include_slice = nil)
|
|
349
|
+
include_slice ||= include_directive
|
|
350
|
+
return Enumerator.new {} unless object
|
|
259
351
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
352
|
+
Enumerator.new do |y|
|
|
353
|
+
(self.instance_reflections ||= self.class._reflections.deep_dup).each do |key, reflection|
|
|
354
|
+
next if reflection.excluded?(self)
|
|
355
|
+
next unless include_directive.key?(key)
|
|
263
356
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
357
|
+
association = reflection.build_association(self, instance_options, include_slice)
|
|
358
|
+
y.yield association
|
|
359
|
+
end
|
|
267
360
|
end
|
|
268
361
|
end
|
|
269
362
|
|
|
270
|
-
|
|
271
|
-
|
|
363
|
+
# @return [Hash] containing the attributes and first level
|
|
364
|
+
# associations, similar to how ActiveModel::Serializers::JSON is used
|
|
365
|
+
# in ActiveRecord::Base.
|
|
366
|
+
def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance)
|
|
367
|
+
adapter_options ||= {}
|
|
368
|
+
options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
|
|
369
|
+
if (fieldset = adapter_options[:fieldset])
|
|
370
|
+
options[:fields] = fieldset.fields_for(json_key)
|
|
371
|
+
end
|
|
372
|
+
resource = attributes_hash(adapter_options, options, adapter_instance)
|
|
373
|
+
relationships = associations_hash(adapter_options, options, adapter_instance)
|
|
374
|
+
resource.merge(relationships)
|
|
272
375
|
end
|
|
376
|
+
alias to_hash serializable_hash
|
|
377
|
+
alias to_h serializable_hash
|
|
273
378
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
associated_data.map { |elem| serialize_id(elem, association) }
|
|
278
|
-
else
|
|
279
|
-
serialize_id(associated_data, association) if associated_data
|
|
280
|
-
end
|
|
379
|
+
# @see #serializable_hash
|
|
380
|
+
def as_json(adapter_opts = nil)
|
|
381
|
+
serializable_hash(adapter_opts)
|
|
281
382
|
end
|
|
282
383
|
|
|
283
|
-
|
|
284
|
-
|
|
384
|
+
# Used by adapter as resource root.
|
|
385
|
+
def json_key
|
|
386
|
+
root || _type ||
|
|
387
|
+
begin
|
|
388
|
+
object.class.model_name.to_s.underscore
|
|
389
|
+
rescue ArgumentError
|
|
390
|
+
'anonymous_object'
|
|
391
|
+
end
|
|
285
392
|
end
|
|
286
393
|
|
|
287
|
-
def
|
|
288
|
-
if
|
|
289
|
-
|
|
394
|
+
def read_attribute_for_serialization(attr)
|
|
395
|
+
if respond_to?(attr)
|
|
396
|
+
send(attr)
|
|
290
397
|
else
|
|
291
|
-
|
|
398
|
+
object.read_attribute_for_serialization(attr)
|
|
292
399
|
end
|
|
293
400
|
end
|
|
294
401
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
end]
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
attr_writer :serialization_options
|
|
308
|
-
def serialization_options
|
|
309
|
-
@serialization_options || {}
|
|
402
|
+
# @api private
|
|
403
|
+
def attributes_hash(_adapter_options, options, adapter_instance)
|
|
404
|
+
if self.class.cache_enabled?
|
|
405
|
+
fetch_attributes(options[:fields], options[:cached_attributes] || {}, adapter_instance)
|
|
406
|
+
elsif self.class.fragment_cache_enabled?
|
|
407
|
+
fetch_attributes_fragment(adapter_instance, options[:cached_attributes] || {})
|
|
408
|
+
else
|
|
409
|
+
attributes(options[:fields], true)
|
|
410
|
+
end
|
|
310
411
|
end
|
|
311
412
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
413
|
+
# @api private
|
|
414
|
+
def associations_hash(adapter_options, options, adapter_instance)
|
|
415
|
+
include_directive = options.fetch(:include_directive)
|
|
416
|
+
include_slice = options[:include_slice]
|
|
417
|
+
associations(include_directive, include_slice).each_with_object({}) do |association, relationships|
|
|
418
|
+
adapter_opts = adapter_options.merge(include_directive: include_directive[association.key], adapter_instance: adapter_instance)
|
|
419
|
+
relationships[association.key] = association.serializable_hash(adapter_opts, adapter_instance)
|
|
420
|
+
end
|
|
320
421
|
end
|
|
321
|
-
alias_method :serializable_hash, :serializable_object
|
|
322
422
|
|
|
323
|
-
|
|
324
|
-
id = elem.read_attribute_for_serialization(association.embed_key)
|
|
325
|
-
association.polymorphic? ? { id: id, type: type_name(elem) } : id
|
|
326
|
-
end
|
|
423
|
+
protected
|
|
327
424
|
|
|
328
|
-
|
|
329
|
-
elem.class.to_s.demodulize.underscore.to_sym
|
|
330
|
-
end
|
|
425
|
+
attr_accessor :instance_options, :instance_reflections
|
|
331
426
|
end
|
|
332
|
-
|
|
333
427
|
end
|