active_model_serializers 0.10.0 → 0.10.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|