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