active_model_serializers 0.10.0 → 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 +5 -5
- data/.rubocop.yml +6 -5
- data/.travis.yml +30 -21
- data/CHANGELOG.md +172 -2
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +23 -4
- data/README.md +166 -28
- data/Rakefile +3 -32
- data/active_model_serializers.gemspec +22 -25
- data/appveyor.yml +10 -6
- data/bin/rubocop +38 -0
- data/docs/README.md +2 -1
- data/docs/general/adapters.md +35 -11
- data/docs/general/caching.md +7 -1
- data/docs/general/configuration_options.md +86 -1
- data/docs/general/deserialization.md +1 -1
- data/docs/general/fields.md +31 -0
- data/docs/general/getting_started.md +1 -1
- data/docs/general/logging.md +7 -0
- data/docs/general/rendering.md +63 -25
- data/docs/general/serializers.md +125 -14
- data/docs/howto/add_pagination_links.md +16 -17
- data/docs/howto/add_relationship_links.md +140 -0
- data/docs/howto/add_root_key.md +11 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +12 -4
- data/docs/howto/passing_arbitrary_options.md +2 -2
- data/docs/howto/serialize_poro.md +46 -5
- data/docs/howto/test.md +2 -0
- data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
- data/docs/integrations/ember-and-json-api.md +67 -32
- data/docs/jsonapi/schema.md +1 -1
- data/lib/action_controller/serialization.rb +13 -3
- data/lib/active_model/serializer/adapter/base.rb +2 -0
- data/lib/active_model/serializer/array_serializer.rb +8 -5
- data/lib/active_model/serializer/association.rb +62 -10
- data/lib/active_model/serializer/belongs_to_reflection.rb +4 -3
- data/lib/active_model/serializer/collection_serializer.rb +39 -13
- data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +82 -115
- data/lib/active_model/serializer/error_serializer.rb +11 -7
- data/lib/active_model/serializer/errors_serializer.rb +25 -20
- data/lib/active_model/serializer/has_many_reflection.rb +3 -3
- data/lib/active_model/serializer/has_one_reflection.rb +1 -4
- data/lib/active_model/serializer/lazy_association.rb +95 -0
- data/lib/active_model/serializer/lint.rb +134 -130
- data/lib/active_model/serializer/reflection.rb +127 -67
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model/serializer.rb +297 -79
- data/lib/active_model_serializers/adapter/attributes.rb +3 -66
- data/lib/active_model_serializers/adapter/base.rb +39 -39
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +2 -2
- data/lib/active_model_serializers/adapter/json_api/link.rb +1 -1
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +47 -21
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +75 -23
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +39 -10
- data/lib/active_model_serializers/adapter/json_api.rb +71 -57
- data/lib/active_model_serializers/adapter.rb +6 -0
- data/lib/active_model_serializers/deprecate.rb +1 -2
- data/lib/active_model_serializers/deserialization.rb +2 -0
- data/lib/active_model_serializers/lookup_chain.rb +80 -0
- data/lib/active_model_serializers/model.rb +109 -28
- data/lib/active_model_serializers/railtie.rb +3 -1
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +44 -31
- data/lib/active_model_serializers/serializable_resource.rb +6 -5
- data/lib/active_model_serializers/serialization_context.rb +10 -3
- data/lib/active_model_serializers/test/schema.rb +2 -2
- data/lib/active_model_serializers.rb +16 -1
- data/lib/generators/rails/resource_override.rb +1 -1
- data/lib/generators/rails/serializer_generator.rb +4 -4
- data/lib/grape/active_model_serializers.rb +7 -5
- data/lib/grape/formatters/active_model_serializers.rb +19 -2
- data/lib/grape/helpers/active_model_serializers.rb +1 -0
- data/lib/tasks/rubocop.rake +53 -0
- data/test/action_controller/adapter_selector_test.rb +14 -5
- data/test/action_controller/explicit_serializer_test.rb +5 -4
- data/test/action_controller/json/include_test.rb +106 -27
- data/test/action_controller/json_api/deserialization_test.rb +1 -1
- data/test/action_controller/json_api/errors_test.rb +8 -9
- data/test/action_controller/json_api/fields_test.rb +66 -0
- data/test/action_controller/json_api/linked_test.rb +29 -24
- data/test/action_controller/json_api/pagination_test.rb +31 -23
- data/test/action_controller/json_api/transform_test.rb +11 -3
- 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 +12 -6
- data/test/action_controller/serialization_test.rb +12 -9
- data/test/active_model_serializers/json_pointer_test.rb +15 -13
- data/test/active_model_serializers/model_test.rb +137 -4
- data/test/active_model_serializers/railtie_test_isolated.rb +12 -7
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +23 -10
- data/test/active_model_serializers/test/schema_test.rb +3 -2
- data/test/adapter/attributes_test.rb +40 -0
- data/test/adapter/json/collection_test.rb +14 -0
- data/test/adapter/json/has_many_test.rb +10 -2
- data/test/adapter/json/transform_test.rb +15 -15
- data/test/adapter/json_api/collection_test.rb +4 -3
- data/test/adapter/json_api/errors_test.rb +17 -19
- data/test/adapter/json_api/fields_test.rb +12 -3
- data/test/adapter/json_api/has_many_test.rb +49 -20
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +213 -0
- data/test/adapter/json_api/json_api_test.rb +5 -7
- data/test/adapter/json_api/linked_test.rb +33 -12
- data/test/adapter/json_api/links_test.rb +4 -2
- data/test/adapter/json_api/pagination_links_test.rb +53 -13
- data/test/adapter/json_api/parse_test.rb +1 -1
- data/test/adapter/json_api/relationship_test.rb +309 -73
- data/test/adapter/json_api/resource_meta_test.rb +3 -3
- data/test/adapter/json_api/transform_test.rb +263 -253
- data/test/adapter/json_api/type_test.rb +168 -36
- data/test/adapter/json_test.rb +8 -7
- data/test/adapter/null_test.rb +1 -2
- data/test/adapter/polymorphic_test.rb +52 -5
- data/test/adapter_test.rb +1 -1
- data/test/benchmark/app.rb +1 -1
- data/test/benchmark/benchmarking_support.rb +1 -1
- data/test/benchmark/bm_active_record.rb +81 -0
- data/test/benchmark/bm_adapter.rb +38 -0
- data/test/benchmark/bm_caching.rb +16 -16
- data/test/benchmark/bm_lookup_chain.rb +83 -0
- data/test/benchmark/bm_transform.rb +21 -10
- data/test/benchmark/controllers.rb +16 -17
- data/test/benchmark/fixtures.rb +72 -72
- data/test/cache_test.rb +235 -69
- data/test/collection_serializer_test.rb +31 -14
- data/test/fixtures/active_record.rb +45 -10
- data/test/fixtures/poro.rb +124 -181
- data/test/generators/serializer_generator_test.rb +23 -5
- data/test/grape_test.rb +170 -56
- data/test/lint_test.rb +1 -1
- data/test/logger_test.rb +13 -11
- data/test/serializable_resource_test.rb +18 -22
- data/test/serializers/association_macros_test.rb +3 -2
- data/test/serializers/associations_test.rb +222 -49
- data/test/serializers/attribute_test.rb +5 -3
- data/test/serializers/attributes_test.rb +1 -1
- data/test/serializers/caching_configuration_test_isolated.rb +6 -6
- data/test/serializers/fieldset_test.rb +1 -1
- data/test/serializers/meta_test.rb +12 -6
- data/test/serializers/options_test.rb +17 -6
- data/test/serializers/read_attribute_for_serialization_test.rb +3 -3
- data/test/serializers/reflection_test.rb +427 -0
- data/test/serializers/root_test.rb +1 -1
- data/test/serializers/serialization_test.rb +2 -2
- data/test/serializers/serializer_for_test.rb +12 -10
- data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
- data/test/support/isolated_unit.rb +9 -4
- data/test/support/rails5_shims.rb +8 -2
- data/test/support/rails_app.rb +2 -9
- data/test/support/serialization_testing.rb +31 -5
- data/test/test_helper.rb +13 -0
- metadata +130 -71
- data/.rubocop_todo.yml +0 -167
- data/docs/ARCHITECTURE.md +0 -126
- data/lib/active_model/serializer/associations.rb +0 -100
- data/lib/active_model/serializer/attributes.rb +0 -82
- data/lib/active_model/serializer/collection_reflection.rb +0 -7
- data/lib/active_model/serializer/configuration.rb +0 -35
- data/lib/active_model/serializer/include_tree.rb +0 -111
- data/lib/active_model/serializer/links.rb +0 -35
- data/lib/active_model/serializer/meta.rb +0 -29
- data/lib/active_model/serializer/singular_reflection.rb +0 -7
- data/lib/active_model/serializer/type.rb +0 -25
- data/lib/active_model_serializers/key_transform.rb +0 -70
- data/test/active_model_serializers/key_transform_test.rb +0 -263
- data/test/adapter/json_api/has_many_embed_ids_test.rb +0 -43
- data/test/adapter/json_api/relationships_test.rb +0 -199
- data/test/adapter/json_api/resource_identifier_test.rb +0 -85
- data/test/include_tree/from_include_args_test.rb +0 -26
- data/test/include_tree/from_string_test.rb +0 -94
- data/test/include_tree/include_args_to_hash_test.rb +0 -64
|
@@ -7,10 +7,9 @@ module ActiveModel
|
|
|
7
7
|
included do
|
|
8
8
|
with_options instance_writer: false, instance_reader: false do |serializer|
|
|
9
9
|
serializer.class_attribute :_cache # @api private : the cache store
|
|
10
|
-
serializer.class_attribute :_fragmented # @api private : @see ::fragmented
|
|
11
10
|
serializer.class_attribute :_cache_key # @api private : when present, is first item in cache_key. Ignored if the serializable object defines #cache_key.
|
|
12
|
-
serializer.class_attribute :_cache_only # @api private : when fragment caching, whitelists
|
|
13
|
-
serializer.class_attribute :_cache_except # @api private : when fragment caching, blacklists
|
|
11
|
+
serializer.class_attribute :_cache_only # @api private : when fragment caching, whitelists fetch_attributes. Cannot combine with except
|
|
12
|
+
serializer.class_attribute :_cache_except # @api private : when fragment caching, blacklists fetch_attributes. Cannot combine with only
|
|
14
13
|
serializer.class_attribute :_cache_options # @api private : used by CachedSerializer, passed to _cache.fetch
|
|
15
14
|
# _cache_options include:
|
|
16
15
|
# expires_in
|
|
@@ -19,7 +18,7 @@ module ActiveModel
|
|
|
19
18
|
# race_condition_ttl
|
|
20
19
|
# Passed to ::_cache as
|
|
21
20
|
# serializer.cache_store.fetch(cache_key, @klass._cache_options)
|
|
22
|
-
# Passed as second argument to serializer.cache_store.fetch(cache_key,
|
|
21
|
+
# Passed as second argument to serializer.cache_store.fetch(cache_key, serializer_class._cache_options)
|
|
23
22
|
serializer.class_attribute :_cache_digest_file_path # @api private : Derived at inheritance
|
|
24
23
|
end
|
|
25
24
|
end
|
|
@@ -41,9 +40,9 @@ module ActiveModel
|
|
|
41
40
|
|
|
42
41
|
module ClassMethods
|
|
43
42
|
def inherited(base)
|
|
44
|
-
super
|
|
45
43
|
caller_line = caller[1]
|
|
46
44
|
base._cache_digest_file_path = caller_line
|
|
45
|
+
super
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
def _cache_digest
|
|
@@ -69,19 +68,27 @@ module ActiveModel
|
|
|
69
68
|
_cache_options && _cache_options[:skip_digest]
|
|
70
69
|
end
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def
|
|
77
|
-
|
|
71
|
+
# @api private
|
|
72
|
+
# maps attribute value to explicit key name
|
|
73
|
+
# @see Serializer::attribute
|
|
74
|
+
# @see Serializer::fragmented_attributes
|
|
75
|
+
def _attributes_keys
|
|
76
|
+
_attributes_data
|
|
77
|
+
.each_with_object({}) do |(key, attr), hash|
|
|
78
|
+
next if key == attr.name
|
|
79
|
+
hash[attr.name] = { key: key }
|
|
80
|
+
end
|
|
78
81
|
end
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
def fragmented_attributes
|
|
84
|
+
cached = _cache_only ? _cache_only : _attributes - _cache_except
|
|
85
|
+
cached = cached.map! { |field| _attributes_keys.fetch(field, field) }
|
|
86
|
+
non_cached = _attributes - cached
|
|
87
|
+
non_cached = non_cached.map! { |field| _attributes_keys.fetch(field, field) }
|
|
88
|
+
{
|
|
89
|
+
cached: cached,
|
|
90
|
+
non_cached: non_cached
|
|
91
|
+
}
|
|
85
92
|
end
|
|
86
93
|
|
|
87
94
|
# Enables a serializer to be automatically cached
|
|
@@ -163,10 +170,11 @@ module ActiveModel
|
|
|
163
170
|
|
|
164
171
|
# Read cache from cache_store
|
|
165
172
|
# @return [Hash]
|
|
166
|
-
|
|
173
|
+
# Used in CollectionSerializer to set :cached_attributes
|
|
174
|
+
def cache_read_multi(collection_serializer, adapter_instance, include_directive)
|
|
167
175
|
return {} if ActiveModelSerializers.config.cache_store.blank?
|
|
168
176
|
|
|
169
|
-
keys = object_cache_keys(collection_serializer, adapter_instance,
|
|
177
|
+
keys = object_cache_keys(collection_serializer, adapter_instance, include_directive)
|
|
170
178
|
|
|
171
179
|
return {} if keys.blank?
|
|
172
180
|
|
|
@@ -176,21 +184,23 @@ module ActiveModel
|
|
|
176
184
|
# Find all cache_key for the collection_serializer
|
|
177
185
|
# @param serializers [ActiveModel::Serializer::CollectionSerializer]
|
|
178
186
|
# @param adapter_instance [ActiveModelSerializers::Adapter::Base]
|
|
179
|
-
# @param
|
|
187
|
+
# @param include_directive [JSONAPI::IncludeDirective]
|
|
180
188
|
# @return [Array] all cache_key of collection_serializer
|
|
181
|
-
def object_cache_keys(collection_serializer, adapter_instance,
|
|
189
|
+
def object_cache_keys(collection_serializer, adapter_instance, include_directive)
|
|
182
190
|
cache_keys = []
|
|
183
191
|
|
|
184
192
|
collection_serializer.each do |serializer|
|
|
185
193
|
cache_keys << object_cache_key(serializer, adapter_instance)
|
|
186
194
|
|
|
187
|
-
serializer.associations(
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
serializer.associations(include_directive).each do |association|
|
|
196
|
+
# TODO(BF): Process relationship without evaluating lazy_association
|
|
197
|
+
association_serializer = association.lazy_association.serializer
|
|
198
|
+
if association_serializer.respond_to?(:each)
|
|
199
|
+
association_serializer.each do |sub_serializer|
|
|
190
200
|
cache_keys << object_cache_key(sub_serializer, adapter_instance)
|
|
191
201
|
end
|
|
192
202
|
else
|
|
193
|
-
cache_keys << object_cache_key(
|
|
203
|
+
cache_keys << object_cache_key(association_serializer, adapter_instance)
|
|
194
204
|
end
|
|
195
205
|
end
|
|
196
206
|
end
|
|
@@ -202,117 +212,70 @@ module ActiveModel
|
|
|
202
212
|
def object_cache_key(serializer, adapter_instance)
|
|
203
213
|
return unless serializer.present? && serializer.object.present?
|
|
204
214
|
|
|
205
|
-
serializer.class.cache_enabled? ? serializer.cache_key(adapter_instance) : nil
|
|
215
|
+
(serializer.class.cache_enabled? || serializer.class.fragment_cache_enabled?) ? serializer.cache_key(adapter_instance) : nil
|
|
206
216
|
end
|
|
207
217
|
end
|
|
208
218
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
219
|
+
### INSTANCE METHODS
|
|
220
|
+
def fetch_attributes(fields, cached_attributes, adapter_instance)
|
|
221
|
+
key = cache_key(adapter_instance)
|
|
222
|
+
cached_attributes.fetch(key) do
|
|
223
|
+
fetch(adapter_instance, serializer_class._cache_options, key) do
|
|
224
|
+
attributes(fields, true)
|
|
225
|
+
end
|
|
215
226
|
end
|
|
216
227
|
end
|
|
217
228
|
|
|
218
|
-
def
|
|
219
|
-
if
|
|
220
|
-
|
|
229
|
+
def fetch(adapter_instance, cache_options = serializer_class._cache_options, key = nil)
|
|
230
|
+
if serializer_class.cache_store
|
|
231
|
+
key ||= cache_key(adapter_instance)
|
|
232
|
+
serializer_class.cache_store.fetch(key, cache_options) do
|
|
221
233
|
yield
|
|
222
234
|
end
|
|
223
|
-
elsif self.class.fragment_cache_enabled?
|
|
224
|
-
fetch_fragment_cache(adapter_instance)
|
|
225
235
|
else
|
|
226
236
|
yield
|
|
227
237
|
end
|
|
228
238
|
end
|
|
229
239
|
|
|
230
|
-
# 1.
|
|
231
|
-
# 2.
|
|
232
|
-
# 3.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
# Given a hash of its cached and non-cached serializers
|
|
254
|
-
# 1. Determine cached attributes from serializer class options
|
|
255
|
-
# 2. Add cached attributes to cached Serializer
|
|
256
|
-
# 3. Add non-cached attributes to non-cached Serializer
|
|
257
|
-
def fetch_fragment_cache(adapter_instance)
|
|
258
|
-
serializer_class_name = self.class.name.gsub('::'.freeze, '_'.freeze)
|
|
259
|
-
self.class._cache_options ||= {}
|
|
260
|
-
self.class._cache_options[:key] = self.class._cache_key if self.class._cache_key
|
|
261
|
-
|
|
262
|
-
cached_serializer = _get_or_create_fragment_cached_serializer(serializer_class_name)
|
|
263
|
-
cached_hash = ActiveModelSerializers::SerializableResource.new(
|
|
264
|
-
object,
|
|
265
|
-
serializer: cached_serializer,
|
|
266
|
-
adapter: adapter_instance.class
|
|
267
|
-
).serializable_hash
|
|
268
|
-
|
|
269
|
-
non_cached_serializer = _get_or_create_fragment_non_cached_serializer(serializer_class_name)
|
|
270
|
-
non_cached_hash = ActiveModelSerializers::SerializableResource.new(
|
|
271
|
-
object,
|
|
272
|
-
serializer: non_cached_serializer,
|
|
273
|
-
adapter: adapter_instance.class
|
|
274
|
-
).serializable_hash
|
|
275
|
-
|
|
240
|
+
# 1. Determine cached fields from serializer class options
|
|
241
|
+
# 2. Get non_cached_fields and fetch cache_fields
|
|
242
|
+
# 3. Merge the two hashes using adapter_instance#fragment_cache
|
|
243
|
+
def fetch_attributes_fragment(adapter_instance, cached_attributes = {})
|
|
244
|
+
serializer_class._cache_options ||= {}
|
|
245
|
+
serializer_class._cache_options[:key] = serializer_class._cache_key if serializer_class._cache_key
|
|
246
|
+
fields = serializer_class.fragmented_attributes
|
|
247
|
+
|
|
248
|
+
non_cached_fields = fields[:non_cached].dup
|
|
249
|
+
non_cached_hash = attributes(non_cached_fields, true)
|
|
250
|
+
include_directive = JSONAPI::IncludeDirective.new(non_cached_fields - non_cached_hash.keys)
|
|
251
|
+
non_cached_hash.merge! associations_hash({}, { include_directive: include_directive }, adapter_instance)
|
|
252
|
+
|
|
253
|
+
cached_fields = fields[:cached].dup
|
|
254
|
+
key = cache_key(adapter_instance)
|
|
255
|
+
cached_hash =
|
|
256
|
+
cached_attributes.fetch(key) do
|
|
257
|
+
fetch(adapter_instance, serializer_class._cache_options, key) do
|
|
258
|
+
hash = attributes(cached_fields, true)
|
|
259
|
+
include_directive = JSONAPI::IncludeDirective.new(cached_fields - hash.keys)
|
|
260
|
+
hash.merge! associations_hash({}, { include_directive: include_directive }, adapter_instance)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
276
263
|
# Merge both results
|
|
277
264
|
adapter_instance.fragment_cache(cached_hash, non_cached_hash)
|
|
278
265
|
end
|
|
279
266
|
|
|
280
|
-
def _get_or_create_fragment_cached_serializer(serializer_class_name)
|
|
281
|
-
cached_serializer = _get_or_create_fragment_serializer "Cached#{serializer_class_name}"
|
|
282
|
-
cached_serializer.cache(self.class._cache_options)
|
|
283
|
-
cached_serializer.type(self.class._type)
|
|
284
|
-
cached_serializer.fragmented(self)
|
|
285
|
-
self.class.cached_attributes.each do |attribute|
|
|
286
|
-
options = self.class._attributes_keys[attribute] || {}
|
|
287
|
-
cached_serializer.attribute(attribute, options)
|
|
288
|
-
end
|
|
289
|
-
cached_serializer
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
def _get_or_create_fragment_non_cached_serializer(serializer_class_name)
|
|
293
|
-
non_cached_serializer = _get_or_create_fragment_serializer "NonCached#{serializer_class_name}"
|
|
294
|
-
non_cached_serializer.type(self.class._type)
|
|
295
|
-
non_cached_serializer.fragmented(self)
|
|
296
|
-
self.class.non_cached_attributes.each do |attribute|
|
|
297
|
-
options = self.class._attributes_keys[attribute] || {}
|
|
298
|
-
non_cached_serializer.attribute(attribute, options)
|
|
299
|
-
end
|
|
300
|
-
non_cached_serializer
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
def _get_or_create_fragment_serializer(name)
|
|
304
|
-
return Object.const_get(name) if Object.const_defined?(name)
|
|
305
|
-
Object.const_set(name, Class.new(ActiveModel::Serializer))
|
|
306
|
-
end
|
|
307
|
-
|
|
308
267
|
def cache_key(adapter_instance)
|
|
309
268
|
return @cache_key if defined?(@cache_key)
|
|
310
269
|
|
|
311
270
|
parts = []
|
|
312
271
|
parts << object_cache_key
|
|
313
|
-
parts << adapter_instance.
|
|
314
|
-
parts <<
|
|
315
|
-
@cache_key = parts
|
|
272
|
+
parts << adapter_instance.cache_key
|
|
273
|
+
parts << serializer_class._cache_digest unless serializer_class._skip_digest?
|
|
274
|
+
@cache_key = expand_cache_key(parts)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def expand_cache_key(parts)
|
|
278
|
+
ActiveSupport::Cache.expand_cache_key(parts)
|
|
316
279
|
end
|
|
317
280
|
|
|
318
281
|
# Use object's cache_key if available, else derive a key from the object
|
|
@@ -320,14 +283,18 @@ module ActiveModel
|
|
|
320
283
|
def object_cache_key
|
|
321
284
|
if object.respond_to?(:cache_key)
|
|
322
285
|
object.cache_key
|
|
323
|
-
elsif (serializer_cache_key = (
|
|
286
|
+
elsif (serializer_cache_key = (serializer_class._cache_key || serializer_class._cache_options[:key]))
|
|
324
287
|
object_time_safe = object.updated_at
|
|
325
288
|
object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
|
|
326
289
|
"#{serializer_cache_key}/#{object.id}-#{object_time_safe}"
|
|
327
290
|
else
|
|
328
|
-
fail UndefinedCacheKey, "#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{
|
|
291
|
+
fail UndefinedCacheKey, "#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{serializer_class}.cache'"
|
|
329
292
|
end
|
|
330
293
|
end
|
|
294
|
+
|
|
295
|
+
def serializer_class
|
|
296
|
+
@serializer_class ||= self.class
|
|
297
|
+
end
|
|
331
298
|
end
|
|
332
299
|
end
|
|
333
300
|
end
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
module ActiveModel
|
|
2
|
+
class Serializer
|
|
3
|
+
class ErrorSerializer < ActiveModel::Serializer
|
|
4
|
+
# @return [Hash<field_name,Array<error_message>>]
|
|
5
|
+
def as_json
|
|
6
|
+
object.errors.messages
|
|
7
|
+
end
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
def success?
|
|
10
|
+
false
|
|
11
|
+
end
|
|
12
|
+
end
|
|
9
13
|
end
|
|
10
14
|
end
|
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
require 'active_model/serializer/error_serializer'
|
|
2
|
-
class ActiveModel::Serializer::ErrorsSerializer
|
|
3
|
-
include Enumerable
|
|
4
|
-
delegate :each, to: :@serializers
|
|
5
|
-
attr_reader :object, :root
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
end
|
|
14
|
-
end
|
|
3
|
+
module ActiveModel
|
|
4
|
+
class Serializer
|
|
5
|
+
class ErrorsSerializer
|
|
6
|
+
include Enumerable
|
|
7
|
+
delegate :each, to: :@serializers
|
|
8
|
+
attr_reader :object, :root
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
def initialize(resources, options = {})
|
|
11
|
+
@root = options[:root]
|
|
12
|
+
@object = resources
|
|
13
|
+
@serializers = resources.map do |resource|
|
|
14
|
+
serializer_class = options.fetch(:serializer) { ActiveModel::Serializer::ErrorSerializer }
|
|
15
|
+
serializer_class.new(resource, options.except(:serializer))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
def success?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
def json_key
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
attr_reader :serializers
|
|
30
|
+
end
|
|
31
|
+
end
|
|
27
32
|
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module ActiveModel
|
|
2
|
+
class Serializer
|
|
3
|
+
# @api private
|
|
4
|
+
LazyAssociation = Struct.new(:reflection, :association_options) do
|
|
5
|
+
REFLECTION_OPTIONS = %i(key links polymorphic meta serializer virtual_value namespace).freeze
|
|
6
|
+
|
|
7
|
+
delegate :collection?, to: :reflection
|
|
8
|
+
|
|
9
|
+
def reflection_options
|
|
10
|
+
@reflection_options ||= reflection.options.dup.reject { |k, _| !REFLECTION_OPTIONS.include?(k) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def object
|
|
14
|
+
@object ||= reflection.value(
|
|
15
|
+
association_options.fetch(:parent_serializer),
|
|
16
|
+
association_options.fetch(:include_slice)
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
alias_method :eval_reflection_block, :object
|
|
20
|
+
|
|
21
|
+
def include_data?
|
|
22
|
+
eval_reflection_block if reflection.block
|
|
23
|
+
reflection.include_data?(
|
|
24
|
+
association_options.fetch(:include_slice)
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [ActiveModel::Serializer, nil]
|
|
29
|
+
def serializer
|
|
30
|
+
return @serializer if defined?(@serializer)
|
|
31
|
+
if serializer_class
|
|
32
|
+
serialize_object!(object)
|
|
33
|
+
elsif !object.nil? && !object.instance_of?(Object)
|
|
34
|
+
cached_result[:virtual_value] = object
|
|
35
|
+
end
|
|
36
|
+
@serializer = cached_result[:serializer]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def virtual_value
|
|
40
|
+
cached_result[:virtual_value] || reflection_options[:virtual_value]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def serializer_class
|
|
44
|
+
return @serializer_class if defined?(@serializer_class)
|
|
45
|
+
serializer_for_options = { namespace: namespace }
|
|
46
|
+
serializer_for_options[:serializer] = reflection_options[:serializer] if reflection_options.key?(:serializer)
|
|
47
|
+
@serializer_class = association_options.fetch(:parent_serializer).class.serializer_for(object, serializer_for_options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def cached_result
|
|
53
|
+
@cached_result ||= {}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def serialize_object!(object)
|
|
57
|
+
if collection?
|
|
58
|
+
if (serializer = instantiate_collection_serializer(object)).nil?
|
|
59
|
+
# BUG: per #2027, JSON API resource relationships are only id and type, and hence either
|
|
60
|
+
# *require* a serializer or we need to be a little clever about figuring out the id/type.
|
|
61
|
+
# In either case, returning the raw virtual value will almost always be incorrect.
|
|
62
|
+
#
|
|
63
|
+
# Should be reflection_options[:virtual_value] or adapter needs to figure out what to do
|
|
64
|
+
# with an object that is non-nil and has no defined serializer.
|
|
65
|
+
cached_result[:virtual_value] = object.try(:as_json) || object
|
|
66
|
+
else
|
|
67
|
+
cached_result[:serializer] = serializer
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
cached_result[:serializer] = instantiate_serializer(object)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def instantiate_serializer(object)
|
|
75
|
+
serializer_options = association_options.fetch(:parent_serializer_options).except(:serializer)
|
|
76
|
+
serializer_options[:serializer_context_class] = association_options.fetch(:parent_serializer).class
|
|
77
|
+
serializer = reflection_options.fetch(:serializer, nil)
|
|
78
|
+
serializer_options[:serializer] = serializer if serializer
|
|
79
|
+
serializer_class.new(object, serializer_options)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def instantiate_collection_serializer(object)
|
|
83
|
+
serializer = catch(:no_serializer) do
|
|
84
|
+
instantiate_serializer(object)
|
|
85
|
+
end
|
|
86
|
+
serializer
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def namespace
|
|
90
|
+
reflection_options[:namespace] ||
|
|
91
|
+
association_options.fetch(:parent_serializer_options)[:namespace]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|