active_model_serializers 0.10.0 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -4
- data/.travis.yml +9 -1
- data/CHANGELOG.md +81 -2
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +5 -2
- data/README.md +24 -24
- data/Rakefile +3 -3
- data/active_model_serializers.gemspec +20 -24
- data/docs/ARCHITECTURE.md +6 -7
- data/docs/README.md +2 -0
- data/docs/general/adapters.md +4 -2
- data/docs/general/caching.md +7 -1
- data/docs/general/configuration_options.md +70 -1
- data/docs/general/deserialization.md +1 -1
- data/docs/general/fields.md +31 -0
- data/docs/general/rendering.md +42 -3
- data/docs/general/serializers.md +97 -8
- data/docs/howto/add_pagination_links.md +4 -5
- data/docs/howto/add_relationship_links.md +137 -0
- data/docs/howto/add_root_key.md +4 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +9 -2
- data/docs/howto/passing_arbitrary_options.md +2 -2
- 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 +64 -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 +19 -4
- data/lib/active_model/serializer/belongs_to_reflection.rb +0 -3
- data/lib/active_model/serializer/collection_serializer.rb +35 -12
- data/lib/active_model/serializer/{associations.rb → concerns/associations.rb} +13 -11
- data/lib/active_model/serializer/{attributes.rb → concerns/attributes.rb} +0 -0
- data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +72 -113
- data/lib/active_model/serializer/{configuration.rb → concerns/configuration.rb} +25 -1
- data/lib/active_model/serializer/{links.rb → concerns/links.rb} +0 -0
- data/lib/active_model/serializer/{meta.rb → concerns/meta.rb} +0 -0
- data/lib/active_model/serializer/{type.rb → concerns/type.rb} +0 -0
- 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 +0 -3
- data/lib/active_model/serializer/has_one_reflection.rb +0 -3
- data/lib/active_model/serializer/lint.rb +134 -130
- data/lib/active_model/serializer/reflection.rb +37 -21
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model/serializer.rb +76 -37
- data/lib/active_model_serializers/adapter/attributes.rb +3 -66
- data/lib/active_model_serializers/adapter/base.rb +38 -38
- data/lib/active_model_serializers/adapter/json_api/link.rb +1 -1
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +8 -1
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +30 -19
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +23 -9
- data/lib/active_model_serializers/adapter/json_api.rb +44 -43
- 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/key_transform.rb +4 -0
- data/lib/active_model_serializers/lookup_chain.rb +80 -0
- data/lib/active_model_serializers/model.rb +4 -2
- 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.rb +7 -0
- 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/test/action_controller/adapter_selector_test.rb +4 -4
- 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/errors_test.rb +6 -7
- data/test/action_controller/json_api/fields_test.rb +57 -0
- data/test/action_controller/json_api/linked_test.rb +29 -24
- data/test/action_controller/json_api/pagination_test.rb +19 -19
- data/test/action_controller/json_api/transform_test.rb +3 -3
- data/test/action_controller/lookup_proc_test.rb +49 -0
- data/test/action_controller/namespace_lookup_test.rb +226 -0
- data/test/action_controller/serialization_test.rb +10 -7
- data/test/active_model_serializers/json_pointer_test.rb +15 -13
- data/test/active_model_serializers/key_transform_test.rb +286 -252
- data/test/active_model_serializers/model_test.rb +17 -4
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +143 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +23 -10
- data/test/adapter/attributes_test.rb +43 -0
- data/test/adapter/json/collection_test.rb +14 -0
- 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 +4 -3
- data/test/adapter/json_api/has_many_test.rb +39 -18
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +166 -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 +35 -8
- data/test/adapter/json_api/relationship_test.rb +309 -73
- data/test/adapter/json_api/resource_identifier_test.rb +27 -2
- data/test/adapter/json_api/resource_meta_test.rb +3 -3
- data/test/adapter/json_api/transform_test.rb +255 -253
- data/test/adapter/json_api/type_test.rb +1 -1
- data/test/adapter/json_test.rb +8 -7
- data/test/adapter/null_test.rb +1 -2
- data/test/adapter/polymorphic_test.rb +5 -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 +16 -5
- data/test/benchmark/controllers.rb +16 -17
- data/test/benchmark/fixtures.rb +72 -72
- data/test/cache_test.rb +143 -49
- data/test/collection_serializer_test.rb +3 -3
- data/test/fixtures/poro.rb +52 -48
- data/test/generators/serializer_generator_test.rb +22 -5
- data/test/grape_test.rb +152 -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 +107 -32
- data/test/serializers/attribute_test.rb +2 -2
- data/test/serializers/attributes_test.rb +1 -1
- data/test/serializers/fieldset_test.rb +1 -1
- data/test/serializers/meta_test.rb +12 -6
- data/test/serializers/root_test.rb +1 -1
- data/test/serializers/serializer_for_test.rb +6 -4
- data/test/serializers/serializer_for_with_namespace_test.rb +87 -0
- data/test/support/isolated_unit.rb +5 -2
- data/test/support/rails5_shims.rb +8 -2
- data/test/support/rails_app.rb +0 -9
- data/test/support/serialization_testing.rb +23 -5
- data/test/test_helper.rb +1 -0
- metadata +85 -34
- data/.rubocop_todo.yml +0 -167
- data/lib/active_model/serializer/include_tree.rb +0 -111
- data/test/adapter/json_api/relationships_test.rb +0 -199
- 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,75 +1,12 @@
|
|
1
1
|
module ActiveModelSerializers
|
2
2
|
module Adapter
|
3
3
|
class Attributes < Base
|
4
|
-
def initialize(serializer, options = {})
|
5
|
-
super
|
6
|
-
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include] || '*')
|
7
|
-
@cached_attributes = options[:cache_attributes] || {}
|
8
|
-
end
|
9
|
-
|
10
4
|
def serializable_hash(options = nil)
|
11
5
|
options = serialization_options(options)
|
6
|
+
options[:fields] ||= instance_options[:fields]
|
7
|
+
serialized_hash = serializer.serializable_hash(instance_options, options, self)
|
12
8
|
|
13
|
-
|
14
|
-
serializable_hash_for_collection(options)
|
15
|
-
else
|
16
|
-
serializable_hash_for_single_resource(options)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def serializable_hash_for_collection(options)
|
23
|
-
cache_attributes
|
24
|
-
|
25
|
-
serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) }
|
26
|
-
end
|
27
|
-
|
28
|
-
def serializable_hash_for_single_resource(options)
|
29
|
-
resource = resource_object_for(options)
|
30
|
-
relationships = resource_relationships(options)
|
31
|
-
resource.merge(relationships)
|
32
|
-
end
|
33
|
-
|
34
|
-
def resource_relationships(options)
|
35
|
-
relationships = {}
|
36
|
-
serializer.associations(@include_tree).each do |association|
|
37
|
-
relationships[association.key] ||= relationship_value_for(association, options)
|
38
|
-
end
|
39
|
-
|
40
|
-
relationships
|
41
|
-
end
|
42
|
-
|
43
|
-
def relationship_value_for(association, options)
|
44
|
-
return association.options[:virtual_value] if association.options[:virtual_value]
|
45
|
-
return unless association.serializer && association.serializer.object
|
46
|
-
|
47
|
-
opts = instance_options.merge(include: @include_tree[association.key])
|
48
|
-
relationship_value = Attributes.new(association.serializer, opts).serializable_hash(options)
|
49
|
-
|
50
|
-
if association.options[:polymorphic] && relationship_value
|
51
|
-
polymorphic_type = association.serializer.object.class.name.underscore
|
52
|
-
relationship_value = { type: polymorphic_type, polymorphic_type.to_sym => relationship_value }
|
53
|
-
end
|
54
|
-
|
55
|
-
relationship_value
|
56
|
-
end
|
57
|
-
|
58
|
-
# Set @cached_attributes
|
59
|
-
def cache_attributes
|
60
|
-
return if @cached_attributes.present?
|
61
|
-
|
62
|
-
@cached_attributes = ActiveModel::Serializer.cache_read_multi(serializer, self, @include_tree)
|
63
|
-
end
|
64
|
-
|
65
|
-
def resource_object_for(options)
|
66
|
-
if serializer.class.cache_enabled?
|
67
|
-
@cached_attributes.fetch(serializer.cache_key(self)) do
|
68
|
-
serializer.cached_fields(options[:fields], self)
|
69
|
-
end
|
70
|
-
else
|
71
|
-
serializer.cached_fields(options[:fields], self)
|
72
|
-
end
|
9
|
+
self.class.transform_key_casing!(serialized_hash, instance_options)
|
73
10
|
end
|
74
11
|
end
|
75
12
|
end
|
@@ -8,6 +8,40 @@ module ActiveModelSerializers
|
|
8
8
|
ActiveModelSerializers::Adapter.register(subclass)
|
9
9
|
end
|
10
10
|
|
11
|
+
# Sets the default transform for the adapter.
|
12
|
+
#
|
13
|
+
# @return [Symbol] the default transform for the adapter
|
14
|
+
def self.default_key_transform
|
15
|
+
:unaltered
|
16
|
+
end
|
17
|
+
|
18
|
+
# Determines the transform to use in order of precedence:
|
19
|
+
# adapter option, global config, adapter default.
|
20
|
+
#
|
21
|
+
# @param options [Object]
|
22
|
+
# @return [Symbol] the transform to use
|
23
|
+
def self.transform(options)
|
24
|
+
return options[:key_transform] if options && options[:key_transform]
|
25
|
+
ActiveModelSerializers.config.key_transform || default_key_transform
|
26
|
+
end
|
27
|
+
|
28
|
+
# Transforms the casing of the supplied value.
|
29
|
+
#
|
30
|
+
# @param value [Object] the value to be transformed
|
31
|
+
# @param options [Object] serializable resource options
|
32
|
+
# @return [Symbol] the default transform for the adapter
|
33
|
+
def self.transform_key_casing!(value, options)
|
34
|
+
KeyTransform.send(transform(options), value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.cache_key
|
38
|
+
@cache_key ||= ActiveModelSerializers::Adapter.registered_name(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.fragment_cache(cached_hash, non_cached_hash)
|
42
|
+
non_cached_hash.merge cached_hash
|
43
|
+
end
|
44
|
+
|
11
45
|
attr_reader :serializer, :instance_options
|
12
46
|
|
13
47
|
def initialize(serializer, options = {})
|
@@ -15,10 +49,6 @@ module ActiveModelSerializers
|
|
15
49
|
@instance_options = options
|
16
50
|
end
|
17
51
|
|
18
|
-
def cached_name
|
19
|
-
@cached_name ||= self.class.name.demodulize.underscore
|
20
|
-
end
|
21
|
-
|
22
52
|
# Subclasses that implement this method must first call
|
23
53
|
# options = serialization_options(options)
|
24
54
|
def serializable_hash(_options = nil)
|
@@ -29,14 +59,12 @@ module ActiveModelSerializers
|
|
29
59
|
serializable_hash(options)
|
30
60
|
end
|
31
61
|
|
32
|
-
def
|
33
|
-
|
62
|
+
def cache_key
|
63
|
+
self.class.cache_key
|
34
64
|
end
|
35
65
|
|
36
|
-
def
|
37
|
-
|
38
|
-
yield
|
39
|
-
end
|
66
|
+
def fragment_cache(cached_hash, non_cached_hash)
|
67
|
+
self.class.fragment_cache(cached_hash, non_cached_hash)
|
40
68
|
end
|
41
69
|
|
42
70
|
private
|
@@ -50,34 +78,6 @@ module ActiveModelSerializers
|
|
50
78
|
def root
|
51
79
|
serializer.json_key.to_sym if serializer.json_key
|
52
80
|
end
|
53
|
-
|
54
|
-
class << self
|
55
|
-
# Sets the default transform for the adapter.
|
56
|
-
#
|
57
|
-
# @return [Symbol] the default transform for the adapter
|
58
|
-
def default_key_transform
|
59
|
-
:unaltered
|
60
|
-
end
|
61
|
-
|
62
|
-
# Determines the transform to use in order of precedence:
|
63
|
-
# adapter option, global config, adapter default.
|
64
|
-
#
|
65
|
-
# @param options [Object]
|
66
|
-
# @return [Symbol] the transform to use
|
67
|
-
def transform(options)
|
68
|
-
return options[:key_transform] if options && options[:key_transform]
|
69
|
-
ActiveModelSerializers.config.key_transform || default_key_transform
|
70
|
-
end
|
71
|
-
|
72
|
-
# Transforms the casing of the supplied value.
|
73
|
-
#
|
74
|
-
# @param value [Object] the value to be transformed
|
75
|
-
# @param options [Object] serializable resource options
|
76
|
-
# @return [Symbol] the default transform for the adapter
|
77
|
-
def transform_key_casing!(value, options)
|
78
|
-
KeyTransform.send(transform(options), value)
|
79
|
-
end
|
80
|
-
end
|
81
81
|
end
|
82
82
|
end
|
83
83
|
end
|
@@ -2,6 +2,7 @@ module ActiveModelSerializers
|
|
2
2
|
module Adapter
|
3
3
|
class JsonApi < Base
|
4
4
|
class PaginationLinks
|
5
|
+
MissingSerializationContextError = Class.new(KeyError)
|
5
6
|
FIRST_PAGE = 1
|
6
7
|
|
7
8
|
attr_reader :collection, :context
|
@@ -9,7 +10,13 @@ module ActiveModelSerializers
|
|
9
10
|
def initialize(collection, adapter_options)
|
10
11
|
@collection = collection
|
11
12
|
@adapter_options = adapter_options
|
12
|
-
@context = adapter_options.fetch(:serialization_context)
|
13
|
+
@context = adapter_options.fetch(:serialization_context) do
|
14
|
+
fail MissingSerializationContextError, <<-EOF.freeze
|
15
|
+
JsonApi::PaginationLinks requires a ActiveModelSerializers::SerializationContext.
|
16
|
+
Please pass a ':serialization_context' option or
|
17
|
+
override CollectionSerializer#paginated? to return 'false'.
|
18
|
+
EOF
|
19
|
+
end
|
13
20
|
end
|
14
21
|
|
15
22
|
def as_json
|
@@ -5,47 +5,58 @@ module ActiveModelSerializers
|
|
5
5
|
# {http://jsonapi.org/format/#document-resource-object-related-resource-links Document Resource Object Related Resource Links}
|
6
6
|
# {http://jsonapi.org/format/#document-links Document Links}
|
7
7
|
# {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage}
|
8
|
-
# {http://jsonapi.org/format/#document-meta
|
9
|
-
def initialize(parent_serializer,
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@association_options = args.fetch(:options, {})
|
8
|
+
# {http://jsonapi.org/format/#document-meta Document Meta}
|
9
|
+
def initialize(parent_serializer, serializable_resource_options, association)
|
10
|
+
@parent_serializer = parent_serializer
|
11
|
+
@association = association
|
13
12
|
@serializable_resource_options = serializable_resource_options
|
14
|
-
@data = data_for(serializer)
|
15
|
-
@links = args.fetch(:links, {}).each_with_object({}) do |(key, value), hash|
|
16
|
-
hash[key] = ActiveModelSerializers::Adapter::JsonApi::Link.new(parent_serializer, value).as_json
|
17
|
-
end
|
18
|
-
meta = args.fetch(:meta, nil)
|
19
|
-
@meta = meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
|
20
13
|
end
|
21
14
|
|
22
15
|
def as_json
|
23
16
|
hash = {}
|
24
|
-
|
25
|
-
|
17
|
+
|
18
|
+
if association.options[:include_data]
|
19
|
+
hash[:data] = data_for(association)
|
20
|
+
end
|
21
|
+
|
22
|
+
links = links_for(association)
|
26
23
|
hash[:links] = links if links.any?
|
27
|
-
|
24
|
+
|
25
|
+
meta = meta_for(association)
|
28
26
|
hash[:meta] = meta if meta
|
27
|
+
hash[:meta] = {} if hash.empty?
|
29
28
|
|
30
29
|
hash
|
31
30
|
end
|
32
31
|
|
33
32
|
protected
|
34
33
|
|
35
|
-
attr_reader :
|
36
|
-
:association_options, :links, :meta
|
34
|
+
attr_reader :parent_serializer, :serializable_resource_options, :association
|
37
35
|
|
38
36
|
private
|
39
37
|
|
40
|
-
def data_for(
|
38
|
+
def data_for(association)
|
39
|
+
serializer = association.serializer
|
41
40
|
if serializer.respond_to?(:each)
|
42
41
|
serializer.map { |s| ResourceIdentifier.new(s, serializable_resource_options).as_json }
|
43
|
-
elsif
|
44
|
-
|
42
|
+
elsif (virtual_value = association.options[:virtual_value])
|
43
|
+
virtual_value
|
45
44
|
elsif serializer && serializer.object
|
46
45
|
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
47
46
|
end
|
48
47
|
end
|
48
|
+
|
49
|
+
def links_for(association)
|
50
|
+
association.links.each_with_object({}) do |(key, value), hash|
|
51
|
+
result = Link.new(parent_serializer, value).as_json
|
52
|
+
hash[key] = result if result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def meta_for(association)
|
57
|
+
meta = association.meta
|
58
|
+
meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
|
59
|
+
end
|
49
60
|
end
|
50
61
|
end
|
51
62
|
end
|
@@ -2,11 +2,30 @@ module ActiveModelSerializers
|
|
2
2
|
module Adapter
|
3
3
|
class JsonApi
|
4
4
|
class ResourceIdentifier
|
5
|
+
def self.type_for(class_name, serializer_type = nil, transform_options = {})
|
6
|
+
if serializer_type
|
7
|
+
raw_type = serializer_type
|
8
|
+
else
|
9
|
+
inflection =
|
10
|
+
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
|
11
|
+
:singularize
|
12
|
+
else
|
13
|
+
:pluralize
|
14
|
+
end
|
15
|
+
|
16
|
+
raw_type = class_name.underscore
|
17
|
+
raw_type = ActiveSupport::Inflector.public_send(inflection, raw_type)
|
18
|
+
raw_type
|
19
|
+
.gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator)
|
20
|
+
raw_type
|
21
|
+
end
|
22
|
+
JsonApi.send(:transform_key_casing!, raw_type, transform_options)
|
23
|
+
end
|
24
|
+
|
5
25
|
# {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
|
6
26
|
def initialize(serializer, options)
|
7
27
|
@id = id_for(serializer)
|
8
|
-
@type =
|
9
|
-
options)
|
28
|
+
@type = type_for(serializer, options)
|
10
29
|
end
|
11
30
|
|
12
31
|
def as_json
|
@@ -19,13 +38,8 @@ module ActiveModelSerializers
|
|
19
38
|
|
20
39
|
private
|
21
40
|
|
22
|
-
def type_for(serializer)
|
23
|
-
|
24
|
-
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
|
25
|
-
serializer.object.class.model_name.singular
|
26
|
-
else
|
27
|
-
serializer.object.class.model_name.plural
|
28
|
-
end
|
41
|
+
def type_for(serializer, transform_options)
|
42
|
+
self.class.type_for(serializer.object.class.name, serializer._type, transform_options)
|
29
43
|
end
|
30
44
|
|
31
45
|
def id_for(serializer)
|
@@ -31,16 +31,27 @@ module ActiveModelSerializers
|
|
31
31
|
autoload :Error
|
32
32
|
autoload :Deserialization
|
33
33
|
|
34
|
+
def self.default_key_transform
|
35
|
+
:dash
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.fragment_cache(cached_hash, non_cached_hash, root = true)
|
39
|
+
core_cached = cached_hash.first
|
40
|
+
core_non_cached = non_cached_hash.first
|
41
|
+
no_root_cache = cached_hash.delete_if { |key, _value| key == core_cached[0] }
|
42
|
+
no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] }
|
43
|
+
cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
|
44
|
+
hash = root ? { root => cached_resource } : cached_resource
|
45
|
+
|
46
|
+
hash.deep_merge no_root_non_cache.deep_merge no_root_cache
|
47
|
+
end
|
48
|
+
|
34
49
|
def initialize(serializer, options = {})
|
35
50
|
super
|
36
|
-
@
|
51
|
+
@include_directive = JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
|
37
52
|
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
|
38
53
|
end
|
39
54
|
|
40
|
-
def self.default_key_transform
|
41
|
-
:dash
|
42
|
-
end
|
43
|
-
|
44
55
|
# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
|
45
56
|
# {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}
|
46
57
|
def serializable_hash(*)
|
@@ -52,6 +63,11 @@ module ActiveModelSerializers
|
|
52
63
|
self.class.transform_key_casing!(document, instance_options)
|
53
64
|
end
|
54
65
|
|
66
|
+
def fragment_cache(cached_hash, non_cached_hash)
|
67
|
+
root = !instance_options.include?(:include)
|
68
|
+
self.class.fragment_cache(cached_hash, non_cached_hash, root)
|
69
|
+
end
|
70
|
+
|
55
71
|
# {http://jsonapi.org/format/#document-top-level Primary data}
|
56
72
|
# definition:
|
57
73
|
# ☐ toplevel_data (required)
|
@@ -174,18 +190,6 @@ module ActiveModelSerializers
|
|
174
190
|
hash
|
175
191
|
end
|
176
192
|
|
177
|
-
def fragment_cache(cached_hash, non_cached_hash)
|
178
|
-
root = false if instance_options.include?(:include)
|
179
|
-
core_cached = cached_hash.first
|
180
|
-
core_non_cached = non_cached_hash.first
|
181
|
-
no_root_cache = cached_hash.delete_if { |key, _value| key == core_cached[0] }
|
182
|
-
no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] }
|
183
|
-
cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
|
184
|
-
hash = root ? { root => cached_resource } : cached_resource
|
185
|
-
|
186
|
-
hash.deep_merge no_root_non_cache.deep_merge no_root_cache
|
187
|
-
end
|
188
|
-
|
189
193
|
protected
|
190
194
|
|
191
195
|
attr_reader :fieldset
|
@@ -231,17 +235,17 @@ module ActiveModelSerializers
|
|
231
235
|
@primary = []
|
232
236
|
@included = []
|
233
237
|
@resource_identifiers = Set.new
|
234
|
-
serializers.each { |serializer| process_resource(serializer, true) }
|
235
|
-
serializers.each { |serializer| process_relationships(serializer, @
|
238
|
+
serializers.each { |serializer| process_resource(serializer, true, @include_directive) }
|
239
|
+
serializers.each { |serializer| process_relationships(serializer, @include_directive) }
|
236
240
|
|
237
241
|
[@primary, @included]
|
238
242
|
end
|
239
243
|
|
240
|
-
def process_resource(serializer, primary)
|
244
|
+
def process_resource(serializer, primary, include_slice = {})
|
241
245
|
resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json
|
242
246
|
return false unless @resource_identifiers.add?(resource_identifier)
|
243
247
|
|
244
|
-
resource_object = resource_object_for(serializer)
|
248
|
+
resource_object = resource_object_for(serializer, include_slice)
|
245
249
|
if primary
|
246
250
|
@primary << resource_object
|
247
251
|
else
|
@@ -251,21 +255,21 @@ module ActiveModelSerializers
|
|
251
255
|
true
|
252
256
|
end
|
253
257
|
|
254
|
-
def process_relationships(serializer,
|
255
|
-
serializer.associations(
|
256
|
-
process_relationship(association.serializer,
|
258
|
+
def process_relationships(serializer, include_slice)
|
259
|
+
serializer.associations(include_slice).each do |association|
|
260
|
+
process_relationship(association.serializer, include_slice[association.key])
|
257
261
|
end
|
258
262
|
end
|
259
263
|
|
260
|
-
def process_relationship(serializer,
|
264
|
+
def process_relationship(serializer, include_slice)
|
261
265
|
if serializer.respond_to?(:each)
|
262
|
-
serializer.each { |s| process_relationship(s,
|
266
|
+
serializer.each { |s| process_relationship(s, include_slice) }
|
263
267
|
return
|
264
268
|
end
|
265
269
|
return unless serializer && serializer.object
|
266
|
-
return unless process_resource(serializer, false)
|
270
|
+
return unless process_resource(serializer, false, include_slice)
|
267
271
|
|
268
|
-
process_relationships(serializer,
|
272
|
+
process_relationships(serializer, include_slice)
|
269
273
|
end
|
270
274
|
|
271
275
|
# {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
|
@@ -289,8 +293,8 @@ module ActiveModelSerializers
|
|
289
293
|
end
|
290
294
|
|
291
295
|
# {http://jsonapi.org/format/#document-resource-objects Document Resource Objects}
|
292
|
-
def resource_object_for(serializer)
|
293
|
-
resource_object =
|
296
|
+
def resource_object_for(serializer, include_slice = {})
|
297
|
+
resource_object = serializer.fetch(self) do
|
294
298
|
resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
|
295
299
|
|
296
300
|
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
|
@@ -300,7 +304,7 @@ module ActiveModelSerializers
|
|
300
304
|
end
|
301
305
|
|
302
306
|
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
|
303
|
-
relationships = relationships_for(serializer, requested_associations)
|
307
|
+
relationships = relationships_for(serializer, requested_associations, include_slice)
|
304
308
|
resource_object[:relationships] = relationships if relationships.any?
|
305
309
|
|
306
310
|
links = links_for(serializer)
|
@@ -428,17 +432,13 @@ module ActiveModelSerializers
|
|
428
432
|
# id: 'required-id',
|
429
433
|
# meta: meta
|
430
434
|
# }.reject! {|_,v| v.nil? }
|
431
|
-
def relationships_for(serializer, requested_associations)
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
options: association.options,
|
439
|
-
links: association.links,
|
440
|
-
meta: association.meta
|
441
|
-
).as_json
|
435
|
+
def relationships_for(serializer, requested_associations, include_slice)
|
436
|
+
include_directive = JSONAPI::IncludeDirective.new(
|
437
|
+
requested_associations,
|
438
|
+
allow_wildcard: true
|
439
|
+
)
|
440
|
+
serializer.associations(include_directive, include_slice).each_with_object({}) do |association, hash|
|
441
|
+
hash[association.key] = Relationship.new(serializer, instance_options, association).as_json
|
442
442
|
end
|
443
443
|
end
|
444
444
|
|
@@ -467,7 +467,8 @@ module ActiveModelSerializers
|
|
467
467
|
# }.reject! {|_,v| v.nil? }
|
468
468
|
def links_for(serializer)
|
469
469
|
serializer._links.each_with_object({}) do |(name, value), hash|
|
470
|
-
|
470
|
+
result = Link.new(serializer, value).as_json
|
471
|
+
hash[name] = result if result
|
471
472
|
end
|
472
473
|
end
|
473
474
|
|
@@ -5,11 +5,13 @@ module ActiveModelSerializers
|
|
5
5
|
private_constant :ADAPTER_MAP if defined?(private_constant)
|
6
6
|
|
7
7
|
class << self # All methods are class functions
|
8
|
+
# :nocov:
|
8
9
|
def new(*args)
|
9
10
|
fail ArgumentError, 'Adapters inherit from Adapter::Base.' \
|
10
11
|
"Adapter.new called with args: '#{args.inspect}', from" \
|
11
12
|
"'caller[0]'."
|
12
13
|
end
|
14
|
+
# :nocov:
|
13
15
|
|
14
16
|
def configured_adapter
|
15
17
|
lookup(ActiveModelSerializers.config.adapter)
|
@@ -51,6 +53,10 @@ module ActiveModelSerializers
|
|
51
53
|
self
|
52
54
|
end
|
53
55
|
|
56
|
+
def registered_name(adapter_class)
|
57
|
+
ADAPTER_MAP.key adapter_class
|
58
|
+
end
|
59
|
+
|
54
60
|
# @param adapter [String, Symbol, Class] name to fetch adapter by
|
55
61
|
# @return [ActiveModelSerializers::Adapter] subclass of Adapter
|
56
62
|
# @raise [UnknownAdapterError]
|
@@ -36,8 +36,7 @@ module ActiveModelSerializers
|
|
36
36
|
target = is_a?(Module) ? "#{self}." : "#{self.class}#"
|
37
37
|
msg = ["NOTE: #{target}#{name} is deprecated",
|
38
38
|
replacement == :none ? ' with no replacement' : "; use #{replacement} instead",
|
39
|
-
"\n#{target}#{name} called from #{ActiveModelSerializers.location_of_caller.join(
|
40
|
-
]
|
39
|
+
"\n#{target}#{name} called from #{ActiveModelSerializers.location_of_caller.join(':')}"]
|
41
40
|
warn "#{msg.join}."
|
42
41
|
send old, *args, &block
|
43
42
|
end
|
@@ -11,6 +11,7 @@ module ActiveModelSerializers
|
|
11
11
|
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
|
12
12
|
def camel(value)
|
13
13
|
case value
|
14
|
+
when Array then value.map { |item| camel(item) }
|
14
15
|
when Hash then value.deep_transform_keys! { |key| camel(key) }
|
15
16
|
when Symbol then camel(value.to_s).to_sym
|
16
17
|
when String then value.underscore.camelize
|
@@ -25,6 +26,7 @@ module ActiveModelSerializers
|
|
25
26
|
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
|
26
27
|
def camel_lower(value)
|
27
28
|
case value
|
29
|
+
when Array then value.map { |item| camel_lower(item) }
|
28
30
|
when Hash then value.deep_transform_keys! { |key| camel_lower(key) }
|
29
31
|
when Symbol then camel_lower(value.to_s).to_sym
|
30
32
|
when String then value.underscore.camelize(:lower)
|
@@ -40,6 +42,7 @@ module ActiveModelSerializers
|
|
40
42
|
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize}
|
41
43
|
def dash(value)
|
42
44
|
case value
|
45
|
+
when Array then value.map { |item| dash(item) }
|
43
46
|
when Hash then value.deep_transform_keys! { |key| dash(key) }
|
44
47
|
when Symbol then dash(value.to_s).to_sym
|
45
48
|
when String then value.underscore.dasherize
|
@@ -55,6 +58,7 @@ module ActiveModelSerializers
|
|
55
58
|
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore}
|
56
59
|
def underscore(value)
|
57
60
|
case value
|
61
|
+
when Array then value.map { |item| underscore(item) }
|
58
62
|
when Hash then value.deep_transform_keys! { |key| underscore(key) }
|
59
63
|
when Symbol then underscore(value.to_s).to_sym
|
60
64
|
when String then value.underscore
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module ActiveModelSerializers
|
2
|
+
module LookupChain
|
3
|
+
# Standard appending of Serializer to the resource name.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
# Author => AuthorSerializer
|
7
|
+
BY_RESOURCE = lambda do |resource_class, _serializer_class, _namespace|
|
8
|
+
serializer_from(resource_class)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Uses the namespace of the resource to find the serializer
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# British::Author => British::AuthorSerializer
|
15
|
+
BY_RESOURCE_NAMESPACE = lambda do |resource_class, _serializer_class, _namespace|
|
16
|
+
resource_namespace = namespace_for(resource_class)
|
17
|
+
serializer_name = serializer_from(resource_class)
|
18
|
+
|
19
|
+
"#{resource_namespace}::#{serializer_name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Uses the controller namespace of the resource to find the serializer
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
# Api::V3::AuthorsController => Api::V3::AuthorSerializer
|
26
|
+
BY_NAMESPACE = lambda do |resource_class, _serializer_class, namespace|
|
27
|
+
resource_name = resource_class_name(resource_class)
|
28
|
+
namespace ? "#{namespace}::#{resource_name}Serializer" : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# Allows for serializers to be defined in parent serializers
|
32
|
+
# - useful if a relationship only needs a different set of attributes
|
33
|
+
# than if it were rendered independently.
|
34
|
+
#
|
35
|
+
# Example:
|
36
|
+
# class BlogSerializer < ActiveModel::Serializer
|
37
|
+
# class AuthorSerialier < ActiveModel::Serializer
|
38
|
+
# ...
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# belongs_to :author
|
42
|
+
# ...
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# The belongs_to relationship would be rendered with
|
46
|
+
# BlogSerializer::AuthorSerialier
|
47
|
+
BY_PARENT_SERIALIZER = lambda do |resource_class, serializer_class, _namespace|
|
48
|
+
return if serializer_class == ActiveModel::Serializer
|
49
|
+
|
50
|
+
serializer_name = serializer_from(resource_class)
|
51
|
+
"#{serializer_class}::#{serializer_name}"
|
52
|
+
end
|
53
|
+
|
54
|
+
DEFAULT = [
|
55
|
+
BY_PARENT_SERIALIZER,
|
56
|
+
BY_NAMESPACE,
|
57
|
+
BY_RESOURCE_NAMESPACE,
|
58
|
+
BY_RESOURCE
|
59
|
+
].freeze
|
60
|
+
|
61
|
+
module_function
|
62
|
+
|
63
|
+
def namespace_for(klass)
|
64
|
+
klass.name.deconstantize
|
65
|
+
end
|
66
|
+
|
67
|
+
def resource_class_name(klass)
|
68
|
+
klass.name.demodulize
|
69
|
+
end
|
70
|
+
|
71
|
+
def serializer_from_resource_name(name)
|
72
|
+
"#{name}Serializer"
|
73
|
+
end
|
74
|
+
|
75
|
+
def serializer_from(klass)
|
76
|
+
name = resource_class_name(klass)
|
77
|
+
serializer_from_resource_name(name)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|