active_model_serializers 0.10.0 → 0.10.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +239 -2
- data/README.md +171 -34
- data/lib/action_controller/serialization.rb +23 -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 +48 -13
- data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +89 -117
- 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 +3 -1
- 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 +130 -65
- data/lib/active_model/serializer/version.rb +3 -1
- data/lib/active_model/serializer.rb +321 -86
- data/lib/active_model_serializers/adapter/attributes.rb +17 -57
- 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 +52 -20
- 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/caching.rb +26 -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
- metadata +104 -296
- data/.github/ISSUE_TEMPLATE.md +0 -29
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -15
- data/.gitignore +0 -35
- data/.rubocop.yml +0 -104
- data/.rubocop_todo.yml +0 -167
- data/.simplecov +0 -110
- data/.travis.yml +0 -43
- data/CONTRIBUTING.md +0 -105
- data/Gemfile +0 -53
- data/Rakefile +0 -103
- data/active_model_serializers.gemspec +0 -66
- data/appveyor.yml +0 -24
- data/bin/bench +0 -171
- data/bin/bench_regression +0 -316
- data/bin/serve_benchmark +0 -39
- data/docs/ARCHITECTURE.md +0 -126
- data/docs/README.md +0 -40
- data/docs/STYLE.md +0 -58
- data/docs/general/adapters.md +0 -245
- data/docs/general/caching.md +0 -52
- data/docs/general/configuration_options.md +0 -100
- data/docs/general/deserialization.md +0 -100
- data/docs/general/getting_started.md +0 -133
- data/docs/general/instrumentation.md +0 -40
- data/docs/general/key_transforms.md +0 -40
- data/docs/general/logging.md +0 -14
- data/docs/general/rendering.md +0 -255
- data/docs/general/serializers.md +0 -372
- data/docs/how-open-source-maintained.jpg +0 -0
- data/docs/howto/add_pagination_links.md +0 -139
- data/docs/howto/add_root_key.md +0 -51
- data/docs/howto/outside_controller_use.md +0 -58
- data/docs/howto/passing_arbitrary_options.md +0 -27
- data/docs/howto/serialize_poro.md +0 -32
- data/docs/howto/test.md +0 -152
- data/docs/integrations/ember-and-json-api.md +0 -112
- data/docs/integrations/grape.md +0 -19
- data/docs/jsonapi/errors.md +0 -56
- data/docs/jsonapi/schema/schema.json +0 -366
- data/docs/jsonapi/schema.md +0 -151
- data/docs/rfcs/0000-namespace.md +0 -106
- data/docs/rfcs/template.md +0 -15
- 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/action_controller/adapter_selector_test.rb +0 -53
- data/test/action_controller/explicit_serializer_test.rb +0 -134
- data/test/action_controller/json/include_test.rb +0 -167
- data/test/action_controller/json_api/deserialization_test.rb +0 -112
- data/test/action_controller/json_api/errors_test.rb +0 -41
- data/test/action_controller/json_api/linked_test.rb +0 -197
- data/test/action_controller/json_api/pagination_test.rb +0 -116
- data/test/action_controller/json_api/transform_test.rb +0 -181
- data/test/action_controller/serialization_scope_name_test.rb +0 -229
- data/test/action_controller/serialization_test.rb +0 -469
- data/test/active_model_serializers/adapter_for_test.rb +0 -208
- data/test/active_model_serializers/json_pointer_test.rb +0 -20
- data/test/active_model_serializers/key_transform_test.rb +0 -263
- data/test/active_model_serializers/logging_test.rb +0 -77
- data/test/active_model_serializers/model_test.rb +0 -9
- data/test/active_model_serializers/railtie_test_isolated.rb +0 -63
- data/test/active_model_serializers/serialization_context_test_isolated.rb +0 -58
- data/test/active_model_serializers/test/schema_test.rb +0 -130
- data/test/active_model_serializers/test/serializer_test.rb +0 -62
- data/test/active_record_test.rb +0 -9
- data/test/adapter/deprecation_test.rb +0 -100
- data/test/adapter/json/belongs_to_test.rb +0 -45
- data/test/adapter/json/collection_test.rb +0 -90
- data/test/adapter/json/has_many_test.rb +0 -45
- data/test/adapter/json/transform_test.rb +0 -93
- data/test/adapter/json_api/belongs_to_test.rb +0 -155
- data/test/adapter/json_api/collection_test.rb +0 -95
- data/test/adapter/json_api/errors_test.rb +0 -78
- data/test/adapter/json_api/fields_test.rb +0 -87
- data/test/adapter/json_api/has_many_embed_ids_test.rb +0 -43
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +0 -96
- data/test/adapter/json_api/has_many_test.rb +0 -144
- data/test/adapter/json_api/has_one_test.rb +0 -80
- data/test/adapter/json_api/json_api_test.rb +0 -35
- data/test/adapter/json_api/linked_test.rb +0 -392
- data/test/adapter/json_api/links_test.rb +0 -93
- data/test/adapter/json_api/pagination_links_test.rb +0 -166
- data/test/adapter/json_api/parse_test.rb +0 -137
- data/test/adapter/json_api/relationship_test.rb +0 -161
- data/test/adapter/json_api/relationships_test.rb +0 -199
- data/test/adapter/json_api/resource_identifier_test.rb +0 -85
- data/test/adapter/json_api/resource_meta_test.rb +0 -100
- data/test/adapter/json_api/toplevel_jsonapi_test.rb +0 -82
- data/test/adapter/json_api/transform_test.rb +0 -502
- data/test/adapter/json_api/type_test.rb +0 -61
- data/test/adapter/json_test.rb +0 -45
- data/test/adapter/null_test.rb +0 -23
- data/test/adapter/polymorphic_test.rb +0 -171
- data/test/adapter_test.rb +0 -67
- data/test/array_serializer_test.rb +0 -22
- data/test/benchmark/app.rb +0 -65
- data/test/benchmark/benchmarking_support.rb +0 -67
- data/test/benchmark/bm_caching.rb +0 -119
- data/test/benchmark/bm_transform.rb +0 -34
- data/test/benchmark/config.ru +0 -3
- data/test/benchmark/controllers.rb +0 -84
- data/test/benchmark/fixtures.rb +0 -219
- data/test/cache_test.rb +0 -485
- data/test/collection_serializer_test.rb +0 -110
- data/test/fixtures/active_record.rb +0 -78
- data/test/fixtures/poro.rb +0 -282
- data/test/generators/scaffold_controller_generator_test.rb +0 -24
- data/test/generators/serializer_generator_test.rb +0 -57
- data/test/grape_test.rb +0 -82
- 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
- data/test/lint_test.rb +0 -49
- data/test/logger_test.rb +0 -18
- data/test/poro_test.rb +0 -9
- data/test/serializable_resource_test.rb +0 -83
- data/test/serializers/association_macros_test.rb +0 -36
- data/test/serializers/associations_test.rb +0 -295
- data/test/serializers/attribute_test.rb +0 -151
- data/test/serializers/attributes_test.rb +0 -52
- data/test/serializers/caching_configuration_test_isolated.rb +0 -170
- data/test/serializers/configuration_test.rb +0 -32
- data/test/serializers/fieldset_test.rb +0 -14
- data/test/serializers/meta_test.rb +0 -196
- data/test/serializers/options_test.rb +0 -21
- data/test/serializers/read_attribute_for_serialization_test.rb +0 -79
- data/test/serializers/root_test.rb +0 -21
- data/test/serializers/serialization_test.rb +0 -55
- data/test/serializers/serializer_for_test.rb +0 -134
- data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +0 -6
- data/test/support/isolated_unit.rb +0 -79
- data/test/support/rails5_shims.rb +0 -47
- data/test/support/rails_app.rb +0 -45
- data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +0 -6
- data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +0 -6
- data/test/support/schemas/custom/show.json +0 -7
- data/test/support/schemas/hyper_schema.json +0 -93
- data/test/support/schemas/render_using_json_api.json +0 -43
- data/test/support/schemas/simple_json_pointers.json +0 -10
- data/test/support/serialization_testing.rb +0 -53
- data/test/test_helper.rb +0 -57
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_model/serializer/collection_serializer'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
|
|
5
|
+
module ActiveModel
|
|
6
|
+
class Serializer
|
|
7
|
+
class ArraySerializer < CollectionSerializer
|
|
8
|
+
class << self
|
|
9
|
+
extend ActiveModelSerializers::Deprecate
|
|
10
|
+
deprecate :new, 'ActiveModel::Serializer::CollectionSerializer.'
|
|
11
|
+
end
|
|
7
12
|
end
|
|
8
13
|
end
|
|
9
14
|
end
|
|
@@ -1,19 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_model/serializer/lazy_association'
|
|
4
|
+
|
|
1
5
|
module ActiveModel
|
|
2
6
|
class Serializer
|
|
3
|
-
# This class
|
|
7
|
+
# This class holds all information about serializer's association.
|
|
4
8
|
#
|
|
5
|
-
# @
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
# @api private
|
|
10
|
+
Association = Struct.new(:reflection, :association_options) do
|
|
11
|
+
attr_reader :lazy_association
|
|
12
|
+
delegate :object, :include_data?, :virtual_value, :collection?, to: :lazy_association
|
|
13
|
+
|
|
14
|
+
def initialize(*)
|
|
15
|
+
super
|
|
16
|
+
@lazy_association = LazyAssociation.new(reflection, association_options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [Symbol]
|
|
20
|
+
delegate :name, to: :reflection
|
|
21
|
+
|
|
13
22
|
# @return [Symbol]
|
|
14
23
|
def key
|
|
15
|
-
|
|
24
|
+
reflection_options.fetch(:key, name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [True,False]
|
|
28
|
+
def key?
|
|
29
|
+
reflection_options.key?(:key)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [Hash]
|
|
33
|
+
def links
|
|
34
|
+
reflection_options.fetch(:links) || {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [Hash, nil]
|
|
38
|
+
# This gets mutated, so cannot use the cached reflection_options
|
|
39
|
+
def meta
|
|
40
|
+
reflection.options[:meta]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def belongs_to?
|
|
44
|
+
reflection.foreign_key_on == :self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def polymorphic?
|
|
48
|
+
true == reflection_options[:polymorphic]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @api private
|
|
52
|
+
def serializable_hash(adapter_options, adapter_instance)
|
|
53
|
+
association_serializer = lazy_association.serializer
|
|
54
|
+
return virtual_value if virtual_value
|
|
55
|
+
association_object = association_serializer && association_serializer.object
|
|
56
|
+
return unless association_object
|
|
57
|
+
|
|
58
|
+
serialization = association_serializer.serializable_hash(adapter_options, {}, adapter_instance)
|
|
59
|
+
|
|
60
|
+
if polymorphic? && serialization
|
|
61
|
+
polymorphic_type = association_object.class.name.underscore
|
|
62
|
+
serialization = { type: polymorphic_type, polymorphic_type.to_sym => serialization }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
serialization
|
|
16
66
|
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
delegate :reflection_options, to: :lazy_association
|
|
17
71
|
end
|
|
18
72
|
end
|
|
19
73
|
end
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModel
|
|
2
4
|
class Serializer
|
|
3
5
|
# @api private
|
|
4
|
-
class BelongsToReflection <
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
class BelongsToReflection < Reflection
|
|
7
|
+
# @api private
|
|
8
|
+
def foreign_key_on
|
|
9
|
+
:self
|
|
7
10
|
end
|
|
8
11
|
end
|
|
9
12
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModel
|
|
2
4
|
class Serializer
|
|
3
5
|
class CollectionSerializer
|
|
4
|
-
NoSerializerError = Class.new(StandardError)
|
|
5
6
|
include Enumerable
|
|
6
7
|
delegate :each, to: :@serializers
|
|
7
8
|
|
|
@@ -11,22 +12,22 @@ module ActiveModel
|
|
|
11
12
|
@object = resources
|
|
12
13
|
@options = options
|
|
13
14
|
@root = options[:root]
|
|
14
|
-
|
|
15
|
-
@serializers = resources.map do |resource|
|
|
16
|
-
serializer_class = options.fetch(:serializer) { serializer_context_class.serializer_for(resource) }
|
|
17
|
-
|
|
18
|
-
if serializer_class.nil? # rubocop:disable Style/GuardClause
|
|
19
|
-
fail NoSerializerError, "No serializer found for resource: #{resource.inspect}"
|
|
20
|
-
else
|
|
21
|
-
serializer_class.new(resource, options.except(:serializer))
|
|
22
|
-
end
|
|
23
|
-
end
|
|
15
|
+
@serializers = serializers_from_resources
|
|
24
16
|
end
|
|
25
17
|
|
|
26
18
|
def success?
|
|
27
19
|
true
|
|
28
20
|
end
|
|
29
21
|
|
|
22
|
+
# @api private
|
|
23
|
+
def serializable_hash(adapter_options, options, adapter_instance)
|
|
24
|
+
options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
|
|
25
|
+
options[:cached_attributes] ||= ActiveModel::Serializer.cache_read_multi(self, adapter_instance, options[:include_directive])
|
|
26
|
+
serializers.map do |serializer|
|
|
27
|
+
serializer.serializable_hash(adapter_options, options, adapter_instance)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
30
31
|
# TODO: unify naming of root, json_key, and _type. Right now, a serializer's
|
|
31
32
|
# json_key comes from the root option or the object's model name, by default.
|
|
32
33
|
# But, if a dev defines a custom `json_key` method with an explicit value,
|
|
@@ -46,19 +47,53 @@ module ActiveModel
|
|
|
46
47
|
# 3. get from collection name, if a named collection
|
|
47
48
|
key ||= object.respond_to?(:name) ? object.name && object.name.underscore : nil
|
|
48
49
|
# 4. key may be nil for empty collection and no serializer option
|
|
49
|
-
key
|
|
50
|
+
key &&= key.pluralize
|
|
51
|
+
if raise_cannot_infer_root_key_error?
|
|
52
|
+
# 5. fail if the key cannot be determined
|
|
53
|
+
key || fail(CannotInferRootKeyError, 'Cannot infer root key from collection type. Please specify the root or each_serializer option, or render a JSON String')
|
|
54
|
+
end
|
|
55
|
+
key
|
|
50
56
|
end
|
|
51
57
|
# rubocop:enable Metrics/CyclomaticComplexity
|
|
52
58
|
|
|
53
59
|
def paginated?
|
|
54
|
-
|
|
60
|
+
ActiveModelSerializers.config.jsonapi_pagination_links_enabled &&
|
|
61
|
+
object.respond_to?(:current_page) &&
|
|
55
62
|
object.respond_to?(:total_pages) &&
|
|
56
63
|
object.respond_to?(:size)
|
|
57
64
|
end
|
|
58
65
|
|
|
66
|
+
class CannotInferRootKeyError < StandardError; end
|
|
67
|
+
|
|
59
68
|
protected
|
|
60
69
|
|
|
61
70
|
attr_reader :serializers, :options
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def raise_cannot_infer_root_key_error?
|
|
75
|
+
ActiveModelSerializers.config.raise_cannot_infer_root_key_error
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def serializers_from_resources
|
|
79
|
+
serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
|
|
80
|
+
object.map do |resource|
|
|
81
|
+
serializer_from_resource(resource, serializer_context_class, options)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def serializer_from_resource(resource, serializer_context_class, options)
|
|
86
|
+
serializer_class = options.fetch(:serializer) do
|
|
87
|
+
serializer_context_class.serializer_for(resource, namespace: options[:namespace])
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
if serializer_class.nil?
|
|
91
|
+
ActiveModelSerializers.logger.debug "No serializer found for resource: #{resource.inspect}"
|
|
92
|
+
throw :no_serializer
|
|
93
|
+
else
|
|
94
|
+
serializer_class.new(resource, options.except(:serializer))
|
|
95
|
+
end
|
|
96
|
+
end
|
|
62
97
|
end
|
|
63
98
|
end
|
|
64
99
|
end
|
|
@@ -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
|
|
@@ -55,7 +56,8 @@ module ActiveModel
|
|
|
55
56
|
def digest_caller_file(caller_line)
|
|
56
57
|
serializer_file_path = caller_line[CALLER_FILE]
|
|
57
58
|
serializer_file_contents = IO.read(serializer_file_path)
|
|
58
|
-
Digest::MD5
|
|
59
|
+
algorithm = ActiveModelSerializers.config.use_sha1_digests ? Digest::SHA1 : Digest::MD5
|
|
60
|
+
algorithm.hexdigest(serializer_file_contents)
|
|
59
61
|
rescue TypeError, Errno::ENOENT
|
|
60
62
|
warn <<-EOF.strip_heredoc
|
|
61
63
|
Cannot digest non-existent file: '#{caller_line}'.
|
|
@@ -69,19 +71,27 @@ module ActiveModel
|
|
|
69
71
|
_cache_options && _cache_options[:skip_digest]
|
|
70
72
|
end
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def
|
|
77
|
-
|
|
74
|
+
# @api private
|
|
75
|
+
# maps attribute value to explicit key name
|
|
76
|
+
# @see Serializer::attribute
|
|
77
|
+
# @see Serializer::fragmented_attributes
|
|
78
|
+
def _attributes_keys
|
|
79
|
+
_attributes_data
|
|
80
|
+
.each_with_object({}) do |(key, attr), hash|
|
|
81
|
+
next if key == attr.name
|
|
82
|
+
hash[attr.name] = { key: key }
|
|
83
|
+
end
|
|
78
84
|
end
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
def fragmented_attributes
|
|
87
|
+
cached = _cache_only ? _cache_only : _attributes - _cache_except
|
|
88
|
+
cached = cached.map! { |field| _attributes_keys.fetch(field, field) }
|
|
89
|
+
non_cached = _attributes - cached
|
|
90
|
+
non_cached = non_cached.map! { |field| _attributes_keys.fetch(field, field) }
|
|
91
|
+
{
|
|
92
|
+
cached: cached,
|
|
93
|
+
non_cached: non_cached
|
|
94
|
+
}
|
|
85
95
|
end
|
|
86
96
|
|
|
87
97
|
# Enables a serializer to be automatically cached
|
|
@@ -163,10 +173,11 @@ module ActiveModel
|
|
|
163
173
|
|
|
164
174
|
# Read cache from cache_store
|
|
165
175
|
# @return [Hash]
|
|
166
|
-
|
|
176
|
+
# Used in CollectionSerializer to set :cached_attributes
|
|
177
|
+
def cache_read_multi(collection_serializer, adapter_instance, include_directive)
|
|
167
178
|
return {} if ActiveModelSerializers.config.cache_store.blank?
|
|
168
179
|
|
|
169
|
-
keys = object_cache_keys(collection_serializer, adapter_instance,
|
|
180
|
+
keys = object_cache_keys(collection_serializer, adapter_instance, include_directive)
|
|
170
181
|
|
|
171
182
|
return {} if keys.blank?
|
|
172
183
|
|
|
@@ -176,21 +187,23 @@ module ActiveModel
|
|
|
176
187
|
# Find all cache_key for the collection_serializer
|
|
177
188
|
# @param serializers [ActiveModel::Serializer::CollectionSerializer]
|
|
178
189
|
# @param adapter_instance [ActiveModelSerializers::Adapter::Base]
|
|
179
|
-
# @param
|
|
190
|
+
# @param include_directive [JSONAPI::IncludeDirective]
|
|
180
191
|
# @return [Array] all cache_key of collection_serializer
|
|
181
|
-
def object_cache_keys(collection_serializer, adapter_instance,
|
|
192
|
+
def object_cache_keys(collection_serializer, adapter_instance, include_directive)
|
|
182
193
|
cache_keys = []
|
|
183
194
|
|
|
184
195
|
collection_serializer.each do |serializer|
|
|
185
196
|
cache_keys << object_cache_key(serializer, adapter_instance)
|
|
186
197
|
|
|
187
|
-
serializer.associations(
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
serializer.associations(include_directive).each do |association|
|
|
199
|
+
# TODO(BF): Process relationship without evaluating lazy_association
|
|
200
|
+
association_serializer = association.lazy_association.serializer
|
|
201
|
+
if association_serializer.respond_to?(:each)
|
|
202
|
+
association_serializer.each do |sub_serializer|
|
|
190
203
|
cache_keys << object_cache_key(sub_serializer, adapter_instance)
|
|
191
204
|
end
|
|
192
205
|
else
|
|
193
|
-
cache_keys << object_cache_key(
|
|
206
|
+
cache_keys << object_cache_key(association_serializer, adapter_instance)
|
|
194
207
|
end
|
|
195
208
|
end
|
|
196
209
|
end
|
|
@@ -202,132 +215,91 @@ module ActiveModel
|
|
|
202
215
|
def object_cache_key(serializer, adapter_instance)
|
|
203
216
|
return unless serializer.present? && serializer.object.present?
|
|
204
217
|
|
|
205
|
-
serializer.class.cache_enabled? ? serializer.cache_key(adapter_instance) : nil
|
|
218
|
+
(serializer.class.cache_enabled? || serializer.class.fragment_cache_enabled?) ? serializer.cache_key(adapter_instance) : nil
|
|
206
219
|
end
|
|
207
220
|
end
|
|
208
221
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
222
|
+
### INSTANCE METHODS
|
|
223
|
+
def fetch_attributes(fields, cached_attributes, adapter_instance)
|
|
224
|
+
key = cache_key(adapter_instance)
|
|
225
|
+
cached_attributes.fetch(key) do
|
|
226
|
+
fetch(adapter_instance, serializer_class._cache_options, key) do
|
|
227
|
+
attributes(fields, true)
|
|
228
|
+
end
|
|
215
229
|
end
|
|
216
230
|
end
|
|
217
231
|
|
|
218
|
-
def
|
|
219
|
-
if
|
|
220
|
-
|
|
232
|
+
def fetch(adapter_instance, cache_options = serializer_class._cache_options, key = nil)
|
|
233
|
+
if serializer_class.cache_store
|
|
234
|
+
key ||= cache_key(adapter_instance)
|
|
235
|
+
serializer_class.cache_store.fetch(key, cache_options) do
|
|
221
236
|
yield
|
|
222
237
|
end
|
|
223
|
-
elsif self.class.fragment_cache_enabled?
|
|
224
|
-
fetch_fragment_cache(adapter_instance)
|
|
225
238
|
else
|
|
226
239
|
yield
|
|
227
240
|
end
|
|
228
241
|
end
|
|
229
242
|
|
|
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
|
-
|
|
243
|
+
# 1. Determine cached fields from serializer class options
|
|
244
|
+
# 2. Get non_cached_fields and fetch cache_fields
|
|
245
|
+
# 3. Merge the two hashes using adapter_instance#fragment_cache
|
|
246
|
+
def fetch_attributes_fragment(adapter_instance, cached_attributes = {})
|
|
247
|
+
serializer_class._cache_options ||= {}
|
|
248
|
+
serializer_class._cache_options[:key] = serializer_class._cache_key if serializer_class._cache_key
|
|
249
|
+
fields = serializer_class.fragmented_attributes
|
|
250
|
+
|
|
251
|
+
non_cached_fields = fields[:non_cached].dup
|
|
252
|
+
non_cached_hash = attributes(non_cached_fields, true)
|
|
253
|
+
include_directive = JSONAPI::IncludeDirective.new(non_cached_fields - non_cached_hash.keys)
|
|
254
|
+
non_cached_hash.merge! associations_hash({}, { include_directive: include_directive }, adapter_instance)
|
|
255
|
+
|
|
256
|
+
cached_fields = fields[:cached].dup
|
|
257
|
+
key = cache_key(adapter_instance)
|
|
258
|
+
cached_hash =
|
|
259
|
+
cached_attributes.fetch(key) do
|
|
260
|
+
fetch(adapter_instance, serializer_class._cache_options, key) do
|
|
261
|
+
hash = attributes(cached_fields, true)
|
|
262
|
+
include_directive = JSONAPI::IncludeDirective.new(cached_fields - hash.keys)
|
|
263
|
+
hash.merge! associations_hash({}, { include_directive: include_directive }, adapter_instance)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
276
266
|
# Merge both results
|
|
277
267
|
adapter_instance.fragment_cache(cached_hash, non_cached_hash)
|
|
278
268
|
end
|
|
279
269
|
|
|
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
270
|
def cache_key(adapter_instance)
|
|
309
271
|
return @cache_key if defined?(@cache_key)
|
|
310
272
|
|
|
311
273
|
parts = []
|
|
312
274
|
parts << object_cache_key
|
|
313
|
-
parts << adapter_instance.
|
|
314
|
-
parts <<
|
|
315
|
-
@cache_key = parts
|
|
275
|
+
parts << adapter_instance.cache_key
|
|
276
|
+
parts << serializer_class._cache_digest unless serializer_class._skip_digest?
|
|
277
|
+
@cache_key = expand_cache_key(parts)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def expand_cache_key(parts)
|
|
281
|
+
ActiveSupport::Cache.expand_cache_key(parts)
|
|
316
282
|
end
|
|
317
283
|
|
|
318
284
|
# Use object's cache_key if available, else derive a key from the object
|
|
319
285
|
# Pass the `key` option to the `cache` declaration or override this method to customize the cache key
|
|
320
286
|
def object_cache_key
|
|
321
|
-
if object.respond_to?(:
|
|
287
|
+
if object.respond_to?(:cache_key_with_version)
|
|
288
|
+
object.cache_key_with_version
|
|
289
|
+
elsif object.respond_to?(:cache_key)
|
|
322
290
|
object.cache_key
|
|
323
|
-
elsif (serializer_cache_key = (
|
|
291
|
+
elsif (serializer_cache_key = (serializer_class._cache_key || serializer_class._cache_options[:key]))
|
|
324
292
|
object_time_safe = object.updated_at
|
|
325
293
|
object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
|
|
326
294
|
"#{serializer_cache_key}/#{object.id}-#{object_time_safe}"
|
|
327
295
|
else
|
|
328
|
-
fail UndefinedCacheKey, "#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{
|
|
296
|
+
fail UndefinedCacheKey, "#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{serializer_class}.cache'"
|
|
329
297
|
end
|
|
330
298
|
end
|
|
299
|
+
|
|
300
|
+
def serializer_class
|
|
301
|
+
@serializer_class ||= self.class
|
|
302
|
+
end
|
|
331
303
|
end
|
|
332
304
|
end
|
|
333
305
|
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
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModel
|
|
2
4
|
class Serializer
|
|
3
5
|
class Fieldset
|
|
@@ -10,7 +12,7 @@ module ActiveModel
|
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def fields_for(type)
|
|
13
|
-
fields[type.singularize.to_sym] || fields[type.pluralize.to_sym]
|
|
15
|
+
fields[type.to_s.singularize.to_sym] || fields[type.to_s.pluralize.to_sym]
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
protected
|