active_model_serializers 0.10.0 → 0.10.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.rubocop.yml +10 -5
- data/.travis.yml +41 -21
- data/CHANGELOG.md +200 -2
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +25 -4
- data/README.md +166 -28
- data/Rakefile +5 -32
- data/active_model_serializers.gemspec +23 -25
- data/appveyor.yml +10 -6
- data/bin/rubocop +38 -0
- data/docs/README.md +2 -1
- data/docs/general/adapters.md +35 -11
- data/docs/general/caching.md +7 -1
- data/docs/general/configuration_options.md +86 -1
- data/docs/general/deserialization.md +1 -1
- data/docs/general/fields.md +31 -0
- data/docs/general/getting_started.md +1 -1
- data/docs/general/logging.md +7 -0
- data/docs/general/rendering.md +63 -25
- data/docs/general/serializers.md +137 -14
- data/docs/howto/add_pagination_links.md +16 -17
- data/docs/howto/add_relationship_links.md +140 -0
- data/docs/howto/add_root_key.md +11 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +12 -4
- data/docs/howto/passing_arbitrary_options.md +2 -2
- data/docs/howto/serialize_poro.md +46 -5
- data/docs/howto/test.md +2 -0
- data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
- data/docs/integrations/ember-and-json-api.md +67 -32
- data/docs/jsonapi/schema.md +1 -1
- data/lib/action_controller/serialization.rb +15 -3
- data/lib/active_model/serializable_resource.rb +2 -0
- data/lib/active_model/serializer/adapter/attributes.rb +2 -0
- data/lib/active_model/serializer/adapter/base.rb +4 -0
- data/lib/active_model/serializer/adapter/json.rb +2 -0
- data/lib/active_model/serializer/adapter/json_api.rb +2 -0
- data/lib/active_model/serializer/adapter/null.rb +2 -0
- data/lib/active_model/serializer/adapter.rb +2 -0
- data/lib/active_model/serializer/array_serializer.rb +10 -5
- data/lib/active_model/serializer/association.rb +64 -10
- data/lib/active_model/serializer/attribute.rb +2 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +6 -3
- data/lib/active_model/serializer/collection_serializer.rb +39 -13
- data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +87 -116
- data/lib/active_model/serializer/error_serializer.rb +13 -7
- data/lib/active_model/serializer/errors_serializer.rb +27 -20
- data/lib/active_model/serializer/field.rb +2 -0
- data/lib/active_model/serializer/fieldset.rb +2 -0
- data/lib/active_model/serializer/has_many_reflection.rb +5 -3
- data/lib/active_model/serializer/has_one_reflection.rb +3 -4
- data/lib/active_model/serializer/lazy_association.rb +99 -0
- data/lib/active_model/serializer/link.rb +23 -0
- data/lib/active_model/serializer/lint.rb +136 -130
- data/lib/active_model/serializer/null.rb +2 -0
- data/lib/active_model/serializer/reflection.rb +132 -67
- data/lib/active_model/serializer/version.rb +3 -1
- data/lib/active_model/serializer.rb +308 -82
- data/lib/active_model_serializers/adapter/attributes.rb +5 -66
- data/lib/active_model_serializers/adapter/base.rb +41 -39
- data/lib/active_model_serializers/adapter/json.rb +2 -0
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +4 -2
- data/lib/active_model_serializers/adapter/json_api/error.rb +2 -0
- data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +2 -0
- data/lib/active_model_serializers/adapter/json_api/link.rb +3 -1
- data/lib/active_model_serializers/adapter/json_api/meta.rb +2 -0
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +49 -21
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +77 -23
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +41 -10
- data/lib/active_model_serializers/adapter/json_api.rb +84 -65
- data/lib/active_model_serializers/adapter/null.rb +2 -0
- data/lib/active_model_serializers/adapter.rb +9 -1
- data/lib/active_model_serializers/callbacks.rb +2 -0
- data/lib/active_model_serializers/deprecate.rb +3 -2
- data/lib/active_model_serializers/deserialization.rb +4 -0
- data/lib/active_model_serializers/json_pointer.rb +2 -0
- data/lib/active_model_serializers/logging.rb +2 -0
- data/lib/active_model_serializers/lookup_chain.rb +82 -0
- data/lib/active_model_serializers/model.rb +111 -28
- data/lib/active_model_serializers/railtie.rb +7 -1
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +46 -31
- data/lib/active_model_serializers/serializable_resource.rb +10 -7
- data/lib/active_model_serializers/serialization_context.rb +12 -3
- data/lib/active_model_serializers/test/schema.rb +4 -2
- data/lib/active_model_serializers/test/serializer.rb +2 -0
- data/lib/active_model_serializers/test.rb +2 -0
- data/lib/active_model_serializers.rb +35 -10
- data/lib/generators/rails/resource_override.rb +3 -1
- data/lib/generators/rails/serializer_generator.rb +6 -4
- data/lib/grape/active_model_serializers.rb +9 -5
- data/lib/grape/formatters/active_model_serializers.rb +21 -2
- data/lib/grape/helpers/active_model_serializers.rb +3 -0
- data/lib/tasks/rubocop.rake +55 -0
- data/test/action_controller/adapter_selector_test.rb +16 -5
- data/test/action_controller/explicit_serializer_test.rb +7 -4
- data/test/action_controller/json/include_test.rb +108 -27
- data/test/action_controller/json_api/deserialization_test.rb +3 -1
- data/test/action_controller/json_api/errors_test.rb +10 -9
- data/test/action_controller/json_api/fields_test.rb +68 -0
- data/test/action_controller/json_api/linked_test.rb +31 -24
- data/test/action_controller/json_api/pagination_test.rb +33 -23
- data/test/action_controller/json_api/transform_test.rb +13 -3
- data/test/action_controller/lookup_proc_test.rb +51 -0
- data/test/action_controller/namespace_lookup_test.rb +234 -0
- data/test/action_controller/serialization_scope_name_test.rb +14 -6
- data/test/action_controller/serialization_test.rb +23 -12
- data/test/active_model_serializers/adapter_for_test.rb +2 -0
- data/test/active_model_serializers/json_pointer_test.rb +17 -13
- data/test/active_model_serializers/logging_test.rb +2 -0
- data/test/active_model_serializers/model_test.rb +139 -4
- data/test/active_model_serializers/railtie_test_isolated.rb +14 -7
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +163 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +25 -10
- data/test/active_model_serializers/test/schema_test.rb +5 -2
- data/test/active_model_serializers/test/serializer_test.rb +2 -0
- data/test/active_record_test.rb +2 -0
- data/test/adapter/attributes_test.rb +42 -0
- data/test/adapter/deprecation_test.rb +2 -0
- data/test/adapter/json/belongs_to_test.rb +2 -0
- data/test/adapter/json/collection_test.rb +16 -0
- data/test/adapter/json/has_many_test.rb +12 -2
- data/test/adapter/json/transform_test.rb +17 -15
- data/test/adapter/json_api/belongs_to_test.rb +2 -0
- data/test/adapter/json_api/collection_test.rb +6 -3
- data/test/adapter/json_api/errors_test.rb +19 -19
- data/test/adapter/json_api/fields_test.rb +14 -3
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +2 -0
- data/test/adapter/json_api/has_many_test.rb +51 -20
- data/test/adapter/json_api/has_one_test.rb +2 -0
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +215 -0
- data/test/adapter/json_api/json_api_test.rb +7 -7
- data/test/adapter/json_api/linked_test.rb +35 -12
- data/test/adapter/json_api/links_test.rb +22 -3
- data/test/adapter/json_api/pagination_links_test.rb +55 -13
- data/test/adapter/json_api/parse_test.rb +3 -1
- data/test/adapter/json_api/relationship_test.rb +311 -73
- data/test/adapter/json_api/resource_meta_test.rb +5 -3
- data/test/adapter/json_api/toplevel_jsonapi_test.rb +2 -0
- data/test/adapter/json_api/transform_test.rb +265 -253
- data/test/adapter/json_api/type_test.rb +170 -36
- data/test/adapter/json_test.rb +10 -7
- data/test/adapter/null_test.rb +3 -2
- data/test/adapter/polymorphic_test.rb +54 -5
- data/test/adapter_test.rb +3 -1
- data/test/array_serializer_test.rb +2 -0
- data/test/benchmark/app.rb +3 -1
- data/test/benchmark/benchmarking_support.rb +3 -1
- data/test/benchmark/bm_active_record.rb +83 -0
- data/test/benchmark/bm_adapter.rb +40 -0
- data/test/benchmark/bm_caching.rb +18 -16
- data/test/benchmark/bm_lookup_chain.rb +85 -0
- data/test/benchmark/bm_transform.rb +23 -10
- data/test/benchmark/controllers.rb +18 -17
- data/test/benchmark/fixtures.rb +74 -72
- data/test/cache_test.rb +301 -69
- data/test/collection_serializer_test.rb +33 -14
- data/test/fixtures/active_record.rb +47 -10
- data/test/fixtures/poro.rb +128 -183
- data/test/generators/scaffold_controller_generator_test.rb +2 -0
- data/test/generators/serializer_generator_test.rb +25 -5
- data/test/grape_test.rb +172 -56
- data/test/lint_test.rb +3 -1
- data/test/logger_test.rb +15 -11
- data/test/poro_test.rb +2 -0
- data/test/serializable_resource_test.rb +20 -22
- data/test/serializers/association_macros_test.rb +5 -2
- data/test/serializers/associations_test.rb +274 -49
- data/test/serializers/attribute_test.rb +7 -3
- data/test/serializers/attributes_test.rb +3 -1
- data/test/serializers/caching_configuration_test_isolated.rb +8 -6
- data/test/serializers/configuration_test.rb +2 -0
- data/test/serializers/fieldset_test.rb +3 -1
- data/test/serializers/meta_test.rb +14 -6
- data/test/serializers/options_test.rb +19 -6
- data/test/serializers/read_attribute_for_serialization_test.rb +5 -3
- data/test/serializers/reflection_test.rb +481 -0
- data/test/serializers/root_test.rb +3 -1
- data/test/serializers/serialization_test.rb +4 -2
- data/test/serializers/serializer_for_test.rb +14 -10
- data/test/serializers/serializer_for_with_namespace_test.rb +90 -0
- data/test/support/isolated_unit.rb +11 -4
- data/test/support/rails5_shims.rb +10 -2
- data/test/support/rails_app.rb +4 -9
- data/test/support/serialization_testing.rb +33 -5
- data/test/test_helper.rb +15 -0
- metadata +126 -46
- data/.rubocop_todo.yml +0 -167
- data/docs/ARCHITECTURE.md +0 -126
- data/lib/active_model/serializer/associations.rb +0 -100
- data/lib/active_model/serializer/attributes.rb +0 -82
- data/lib/active_model/serializer/collection_reflection.rb +0 -7
- data/lib/active_model/serializer/configuration.rb +0 -35
- data/lib/active_model/serializer/include_tree.rb +0 -111
- data/lib/active_model/serializer/links.rb +0 -35
- data/lib/active_model/serializer/meta.rb +0 -29
- data/lib/active_model/serializer/singular_reflection.rb +0 -7
- data/lib/active_model/serializer/type.rb +0 -25
- data/lib/active_model_serializers/key_transform.rb +0 -70
- data/test/active_model_serializers/key_transform_test.rb +0 -263
- data/test/adapter/json_api/has_many_embed_ids_test.rb +0 -43
- data/test/adapter/json_api/relationships_test.rb +0 -199
- data/test/adapter/json_api/resource_identifier_test.rb +0 -85
- data/test/include_tree/from_include_args_test.rb +0 -26
- data/test/include_tree/from_string_test.rb +0 -94
- data/test/include_tree/include_args_to_hash_test.rb +0 -64
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModelSerializers
|
|
2
4
|
module Adapter
|
|
3
5
|
class JsonApi
|
|
@@ -189,7 +191,7 @@ module ActiveModelSerializers
|
|
|
189
191
|
|
|
190
192
|
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
|
|
191
193
|
if polymorphic
|
|
192
|
-
hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'] : nil
|
|
194
|
+
hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'].classify : nil
|
|
193
195
|
end
|
|
194
196
|
|
|
195
197
|
hash
|
|
@@ -205,7 +207,7 @@ module ActiveModelSerializers
|
|
|
205
207
|
# @api private
|
|
206
208
|
def transform_keys(hash, options)
|
|
207
209
|
transform = options[:key_transform] || :underscore
|
|
208
|
-
|
|
210
|
+
CaseTransform.send(transform, hash)
|
|
209
211
|
end
|
|
210
212
|
end
|
|
211
213
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModelSerializers
|
|
2
4
|
module Adapter
|
|
3
5
|
class JsonApi
|
|
@@ -71,7 +73,7 @@ module ActiveModelSerializers
|
|
|
71
73
|
hash[:href] = @href if defined?(@href)
|
|
72
74
|
hash[:meta] = @meta if defined?(@meta)
|
|
73
75
|
|
|
74
|
-
hash
|
|
76
|
+
hash.any? ? hash : nil
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
protected
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModelSerializers
|
|
2
4
|
module Adapter
|
|
3
5
|
class JsonApi < Base
|
|
4
6
|
class PaginationLinks
|
|
7
|
+
MissingSerializationContextError = Class.new(KeyError)
|
|
5
8
|
FIRST_PAGE = 1
|
|
6
9
|
|
|
7
10
|
attr_reader :collection, :context
|
|
@@ -9,16 +12,23 @@ module ActiveModelSerializers
|
|
|
9
12
|
def initialize(collection, adapter_options)
|
|
10
13
|
@collection = collection
|
|
11
14
|
@adapter_options = adapter_options
|
|
12
|
-
@context = adapter_options.fetch(:serialization_context)
|
|
15
|
+
@context = adapter_options.fetch(:serialization_context) do
|
|
16
|
+
fail MissingSerializationContextError, <<-EOF.freeze
|
|
17
|
+
JsonApi::PaginationLinks requires a ActiveModelSerializers::SerializationContext.
|
|
18
|
+
Please pass a ':serialization_context' option or
|
|
19
|
+
override CollectionSerializer#paginated? to return 'false'.
|
|
20
|
+
EOF
|
|
21
|
+
end
|
|
13
22
|
end
|
|
14
23
|
|
|
15
24
|
def as_json
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
{
|
|
26
|
+
self: location_url,
|
|
27
|
+
first: first_page_url,
|
|
28
|
+
prev: prev_page_url,
|
|
29
|
+
next: next_page_url,
|
|
30
|
+
last: last_page_url
|
|
31
|
+
}
|
|
22
32
|
end
|
|
23
33
|
|
|
24
34
|
protected
|
|
@@ -27,25 +37,39 @@ module ActiveModelSerializers
|
|
|
27
37
|
|
|
28
38
|
private
|
|
29
39
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{}.tap do |pages|
|
|
34
|
-
pages[:self] = collection.current_page
|
|
40
|
+
def location_url
|
|
41
|
+
url_for_page(collection.current_page)
|
|
42
|
+
end
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
end
|
|
44
|
+
def first_page_url
|
|
45
|
+
url_for_page(1)
|
|
46
|
+
end
|
|
40
47
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
def last_page_url
|
|
49
|
+
if collection.total_pages == 0
|
|
50
|
+
url_for_page(FIRST_PAGE)
|
|
51
|
+
else
|
|
52
|
+
url_for_page(collection.total_pages)
|
|
45
53
|
end
|
|
46
54
|
end
|
|
47
55
|
|
|
48
|
-
def
|
|
56
|
+
def prev_page_url
|
|
57
|
+
return nil if collection.current_page == FIRST_PAGE
|
|
58
|
+
url_for_page(collection.current_page - FIRST_PAGE)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def next_page_url
|
|
62
|
+
return nil if collection.total_pages == 0 || collection.current_page == collection.total_pages
|
|
63
|
+
url_for_page(collection.next_page)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def url_for_page(number)
|
|
67
|
+
params = query_parameters.dup
|
|
68
|
+
params[:page] = { size: per_page, number: number }
|
|
69
|
+
"#{url(adapter_options)}?#{params.to_query}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def url(options = {})
|
|
49
73
|
@url ||= options.fetch(:links, {}).fetch(:self, nil) || request_url
|
|
50
74
|
end
|
|
51
75
|
|
|
@@ -56,6 +80,10 @@ module ActiveModelSerializers
|
|
|
56
80
|
def query_parameters
|
|
57
81
|
@query_parameters ||= context.query_parameters
|
|
58
82
|
end
|
|
83
|
+
|
|
84
|
+
def per_page
|
|
85
|
+
@per_page ||= collection.try(:per_page) || collection.try(:limit_value) || collection.size
|
|
86
|
+
end
|
|
59
87
|
end
|
|
60
88
|
end
|
|
61
89
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModelSerializers
|
|
2
4
|
module Adapter
|
|
3
5
|
class JsonApi
|
|
@@ -5,47 +7,99 @@ module ActiveModelSerializers
|
|
|
5
7
|
# {http://jsonapi.org/format/#document-resource-object-related-resource-links Document Resource Object Related Resource Links}
|
|
6
8
|
# {http://jsonapi.org/format/#document-links Document Links}
|
|
7
9
|
# {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, {})
|
|
10
|
+
# {http://jsonapi.org/format/#document-meta Document Meta}
|
|
11
|
+
def initialize(parent_serializer, serializable_resource_options, association)
|
|
12
|
+
@parent_serializer = parent_serializer
|
|
13
|
+
@association = association
|
|
13
14
|
@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
15
|
end
|
|
21
16
|
|
|
22
17
|
def as_json
|
|
23
18
|
hash = {}
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
|
|
20
|
+
hash[:data] = data_for(association) if association.include_data?
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
# TODO(BF): Avoid db hit on belong_to_ releationship by using foreign_key on self
|
|
39
|
+
def data_for(association)
|
|
40
|
+
if association.collection?
|
|
41
|
+
data_for_many(association)
|
|
42
|
+
else
|
|
43
|
+
data_for_one(association)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def data_for_one(association)
|
|
48
|
+
if belongs_to_id_on_self?(association)
|
|
49
|
+
id = parent_serializer.read_attribute_for_serialization(association.reflection.foreign_key)
|
|
50
|
+
type =
|
|
51
|
+
if association.polymorphic?
|
|
52
|
+
# We can't infer resource type for polymorphic relationships from the serializer.
|
|
53
|
+
# We can ONLY know a polymorphic resource type by inspecting each resource.
|
|
54
|
+
association.lazy_association.serializer.json_key
|
|
55
|
+
else
|
|
56
|
+
association.reflection.type.to_s
|
|
57
|
+
end
|
|
58
|
+
ResourceIdentifier.for_type_with_id(type, id, serializable_resource_options)
|
|
59
|
+
else
|
|
60
|
+
# TODO(BF): Process relationship without evaluating lazy_association
|
|
61
|
+
serializer = association.lazy_association.serializer
|
|
62
|
+
if (virtual_value = association.virtual_value)
|
|
63
|
+
virtual_value
|
|
64
|
+
elsif serializer && association.object
|
|
65
|
+
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
|
66
|
+
else
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def data_for_many(association)
|
|
73
|
+
# TODO(BF): Process relationship without evaluating lazy_association
|
|
74
|
+
collection_serializer = association.lazy_association.serializer
|
|
75
|
+
if collection_serializer.respond_to?(:each)
|
|
76
|
+
collection_serializer.map do |serializer|
|
|
77
|
+
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
|
78
|
+
end
|
|
79
|
+
elsif (virtual_value = association.virtual_value)
|
|
80
|
+
virtual_value
|
|
81
|
+
else
|
|
82
|
+
[]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def links_for(association)
|
|
87
|
+
association.links.each_with_object({}) do |(key, value), hash|
|
|
88
|
+
result = Link.new(parent_serializer, value).as_json
|
|
89
|
+
hash[key] = result if result
|
|
47
90
|
end
|
|
48
91
|
end
|
|
92
|
+
|
|
93
|
+
def meta_for(association)
|
|
94
|
+
meta = association.meta
|
|
95
|
+
meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def belongs_to_id_on_self?(association)
|
|
99
|
+
parent_serializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship &&
|
|
100
|
+
association.belongs_to? &&
|
|
101
|
+
parent_serializer.object.respond_to?(association.reflection.foreign_key)
|
|
102
|
+
end
|
|
49
103
|
end
|
|
50
104
|
end
|
|
51
105
|
end
|
|
@@ -1,16 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModelSerializers
|
|
2
4
|
module Adapter
|
|
3
5
|
class JsonApi
|
|
4
6
|
class ResourceIdentifier
|
|
7
|
+
def self.type_for(serializer, serializer_type = nil, transform_options = {})
|
|
8
|
+
raw_type = serializer_type ? serializer_type : raw_type_from_serializer_object(serializer.object)
|
|
9
|
+
JsonApi.send(:transform_key_casing!, raw_type, transform_options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.for_type_with_id(type, id, options)
|
|
13
|
+
type = inflect_type(type)
|
|
14
|
+
type = type_for(:no_class_needed, type, options)
|
|
15
|
+
if id.blank?
|
|
16
|
+
{ type: type }
|
|
17
|
+
else
|
|
18
|
+
{ id: id.to_s, type: type }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.raw_type_from_serializer_object(object)
|
|
23
|
+
class_name = object.class.name # should use model_name
|
|
24
|
+
raw_type = class_name.underscore
|
|
25
|
+
raw_type = inflect_type(raw_type)
|
|
26
|
+
raw_type
|
|
27
|
+
.gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator)
|
|
28
|
+
raw_type
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.inflect_type(type)
|
|
32
|
+
singularize = ActiveModelSerializers.config.jsonapi_resource_type == :singular
|
|
33
|
+
inflection = singularize ? :singularize : :pluralize
|
|
34
|
+
ActiveSupport::Inflector.public_send(inflection, type)
|
|
35
|
+
end
|
|
36
|
+
|
|
5
37
|
# {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
|
|
6
38
|
def initialize(serializer, options)
|
|
7
39
|
@id = id_for(serializer)
|
|
8
|
-
@type =
|
|
9
|
-
options)
|
|
40
|
+
@type = type_for(serializer, options)
|
|
10
41
|
end
|
|
11
42
|
|
|
12
43
|
def as_json
|
|
13
|
-
|
|
44
|
+
if id.blank?
|
|
45
|
+
{ type: type }
|
|
46
|
+
else
|
|
47
|
+
{ id: id.to_s, type: type }
|
|
48
|
+
end
|
|
14
49
|
end
|
|
15
50
|
|
|
16
51
|
protected
|
|
@@ -19,13 +54,9 @@ module ActiveModelSerializers
|
|
|
19
54
|
|
|
20
55
|
private
|
|
21
56
|
|
|
22
|
-
def type_for(serializer)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
serializer.object.class.model_name.singular
|
|
26
|
-
else
|
|
27
|
-
serializer.object.class.model_name.plural
|
|
28
|
-
end
|
|
57
|
+
def type_for(serializer, transform_options)
|
|
58
|
+
serializer_type = serializer._type
|
|
59
|
+
self.class.type_for(serializer, serializer_type, transform_options)
|
|
29
60
|
end
|
|
30
61
|
|
|
31
62
|
def id_for(serializer)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# {http://jsonapi.org/format/ JSON API specification}
|
|
2
4
|
# rubocop:disable Style/AsciiComments
|
|
3
5
|
# TODO: implement!
|
|
@@ -22,25 +24,38 @@ module ActiveModelSerializers
|
|
|
22
24
|
module Adapter
|
|
23
25
|
class JsonApi < Base
|
|
24
26
|
extend ActiveSupport::Autoload
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def initialize(serializer, options = {})
|
|
35
|
-
super
|
|
36
|
-
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include])
|
|
37
|
-
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
|
|
27
|
+
eager_autoload do
|
|
28
|
+
autoload :Jsonapi
|
|
29
|
+
autoload :ResourceIdentifier
|
|
30
|
+
autoload :Link
|
|
31
|
+
autoload :PaginationLinks
|
|
32
|
+
autoload :Meta
|
|
33
|
+
autoload :Error
|
|
34
|
+
autoload :Deserialization
|
|
35
|
+
autoload :Relationship
|
|
38
36
|
end
|
|
39
37
|
|
|
40
38
|
def self.default_key_transform
|
|
41
39
|
:dash
|
|
42
40
|
end
|
|
43
41
|
|
|
42
|
+
def self.fragment_cache(cached_hash, non_cached_hash, root = true)
|
|
43
|
+
core_cached = cached_hash.first
|
|
44
|
+
core_non_cached = non_cached_hash.first
|
|
45
|
+
no_root_cache = cached_hash.delete_if { |key, _value| key == core_cached[0] }
|
|
46
|
+
no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] }
|
|
47
|
+
cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
|
|
48
|
+
hash = root ? { root => cached_resource } : cached_resource
|
|
49
|
+
|
|
50
|
+
hash.deep_merge no_root_non_cache.deep_merge no_root_cache
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def initialize(serializer, options = {})
|
|
54
|
+
super
|
|
55
|
+
@include_directive = JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
|
|
56
|
+
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
|
|
57
|
+
end
|
|
58
|
+
|
|
44
59
|
# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
|
|
45
60
|
# {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}
|
|
46
61
|
def serializable_hash(*)
|
|
@@ -52,6 +67,11 @@ module ActiveModelSerializers
|
|
|
52
67
|
self.class.transform_key_casing!(document, instance_options)
|
|
53
68
|
end
|
|
54
69
|
|
|
70
|
+
def fragment_cache(cached_hash, non_cached_hash)
|
|
71
|
+
root = !instance_options.include?(:include)
|
|
72
|
+
self.class.fragment_cache(cached_hash, non_cached_hash, root)
|
|
73
|
+
end
|
|
74
|
+
|
|
55
75
|
# {http://jsonapi.org/format/#document-top-level Primary data}
|
|
56
76
|
# definition:
|
|
57
77
|
# ☐ toplevel_data (required)
|
|
@@ -174,18 +194,6 @@ module ActiveModelSerializers
|
|
|
174
194
|
hash
|
|
175
195
|
end
|
|
176
196
|
|
|
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
197
|
protected
|
|
190
198
|
|
|
191
199
|
attr_reader :fieldset
|
|
@@ -231,17 +239,17 @@ module ActiveModelSerializers
|
|
|
231
239
|
@primary = []
|
|
232
240
|
@included = []
|
|
233
241
|
@resource_identifiers = Set.new
|
|
234
|
-
serializers.each { |serializer| process_resource(serializer, true) }
|
|
235
|
-
serializers.each { |serializer| process_relationships(serializer, @
|
|
242
|
+
serializers.each { |serializer| process_resource(serializer, true, @include_directive) }
|
|
243
|
+
serializers.each { |serializer| process_relationships(serializer, @include_directive) }
|
|
236
244
|
|
|
237
245
|
[@primary, @included]
|
|
238
246
|
end
|
|
239
247
|
|
|
240
|
-
def process_resource(serializer, primary)
|
|
248
|
+
def process_resource(serializer, primary, include_slice = {})
|
|
241
249
|
resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json
|
|
242
250
|
return false unless @resource_identifiers.add?(resource_identifier)
|
|
243
251
|
|
|
244
|
-
resource_object = resource_object_for(serializer)
|
|
252
|
+
resource_object = resource_object_for(serializer, include_slice)
|
|
245
253
|
if primary
|
|
246
254
|
@primary << resource_object
|
|
247
255
|
else
|
|
@@ -251,21 +259,22 @@ module ActiveModelSerializers
|
|
|
251
259
|
true
|
|
252
260
|
end
|
|
253
261
|
|
|
254
|
-
def process_relationships(serializer,
|
|
255
|
-
serializer.associations(
|
|
256
|
-
|
|
262
|
+
def process_relationships(serializer, include_slice)
|
|
263
|
+
serializer.associations(include_slice).each do |association|
|
|
264
|
+
# TODO(BF): Process relationship without evaluating lazy_association
|
|
265
|
+
process_relationship(association.lazy_association.serializer, include_slice[association.key])
|
|
257
266
|
end
|
|
258
267
|
end
|
|
259
268
|
|
|
260
|
-
def process_relationship(serializer,
|
|
269
|
+
def process_relationship(serializer, include_slice)
|
|
261
270
|
if serializer.respond_to?(:each)
|
|
262
|
-
serializer.each { |s| process_relationship(s,
|
|
271
|
+
serializer.each { |s| process_relationship(s, include_slice) }
|
|
263
272
|
return
|
|
264
273
|
end
|
|
265
274
|
return unless serializer && serializer.object
|
|
266
|
-
return unless process_resource(serializer, false)
|
|
275
|
+
return unless process_resource(serializer, false, include_slice)
|
|
267
276
|
|
|
268
|
-
process_relationships(serializer,
|
|
277
|
+
process_relationships(serializer, include_slice)
|
|
269
278
|
end
|
|
270
279
|
|
|
271
280
|
# {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
|
|
@@ -289,21 +298,9 @@ module ActiveModelSerializers
|
|
|
289
298
|
end
|
|
290
299
|
|
|
291
300
|
# {http://jsonapi.org/format/#document-resource-objects Document Resource Objects}
|
|
292
|
-
def resource_object_for(serializer)
|
|
293
|
-
resource_object =
|
|
294
|
-
resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
|
|
295
|
-
|
|
296
|
-
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
|
|
297
|
-
attributes = attributes_for(serializer, requested_fields)
|
|
298
|
-
resource_object[:attributes] = attributes if attributes.any?
|
|
299
|
-
resource_object
|
|
300
|
-
end
|
|
301
|
+
def resource_object_for(serializer, include_slice = {})
|
|
302
|
+
resource_object = data_for(serializer, include_slice)
|
|
301
303
|
|
|
302
|
-
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
|
|
303
|
-
relationships = relationships_for(serializer, requested_associations)
|
|
304
|
-
resource_object[:relationships] = relationships if relationships.any?
|
|
305
|
-
|
|
306
|
-
links = links_for(serializer)
|
|
307
304
|
# toplevel_links
|
|
308
305
|
# definition:
|
|
309
306
|
# allOf
|
|
@@ -317,7 +314,10 @@ module ActiveModelSerializers
|
|
|
317
314
|
# prs:
|
|
318
315
|
# https://github.com/rails-api/active_model_serializers/pull/1247
|
|
319
316
|
# https://github.com/rails-api/active_model_serializers/pull/1018
|
|
320
|
-
|
|
317
|
+
if (links = links_for(serializer)).any?
|
|
318
|
+
resource_object ||= {}
|
|
319
|
+
resource_object[:links] = links
|
|
320
|
+
end
|
|
321
321
|
|
|
322
322
|
# toplevel_meta
|
|
323
323
|
# alias meta
|
|
@@ -327,12 +327,33 @@ module ActiveModelSerializers
|
|
|
327
327
|
# {
|
|
328
328
|
# :'git-ref' => 'abc123'
|
|
329
329
|
# }
|
|
330
|
-
meta = meta_for(serializer)
|
|
331
|
-
|
|
330
|
+
if (meta = meta_for(serializer)).present?
|
|
331
|
+
resource_object ||= {}
|
|
332
|
+
resource_object[:meta] = meta
|
|
333
|
+
end
|
|
332
334
|
|
|
333
335
|
resource_object
|
|
334
336
|
end
|
|
335
337
|
|
|
338
|
+
def data_for(serializer, include_slice)
|
|
339
|
+
data = serializer.fetch(self) do
|
|
340
|
+
resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
|
|
341
|
+
break nil if resource_object.nil?
|
|
342
|
+
|
|
343
|
+
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
|
|
344
|
+
attributes = attributes_for(serializer, requested_fields)
|
|
345
|
+
resource_object[:attributes] = attributes if attributes.any?
|
|
346
|
+
resource_object
|
|
347
|
+
end
|
|
348
|
+
data.tap do |resource_object|
|
|
349
|
+
next if resource_object.nil?
|
|
350
|
+
# NOTE(BF): the attributes are cached above, separately from the relationships, below.
|
|
351
|
+
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
|
|
352
|
+
relationships = relationships_for(serializer, requested_associations, include_slice)
|
|
353
|
+
resource_object[:relationships] = relationships if relationships.any?
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
336
357
|
# {http://jsonapi.org/format/#document-resource-object-relationships Document Resource Object Relationship}
|
|
337
358
|
# relationships
|
|
338
359
|
# definition:
|
|
@@ -428,17 +449,13 @@ module ActiveModelSerializers
|
|
|
428
449
|
# id: 'required-id',
|
|
429
450
|
# meta: meta
|
|
430
451
|
# }.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
|
|
452
|
+
def relationships_for(serializer, requested_associations, include_slice)
|
|
453
|
+
include_directive = JSONAPI::IncludeDirective.new(
|
|
454
|
+
requested_associations,
|
|
455
|
+
allow_wildcard: true
|
|
456
|
+
)
|
|
457
|
+
serializer.associations(include_directive, include_slice).each_with_object({}) do |association, hash|
|
|
458
|
+
hash[association.key] = Relationship.new(serializer, instance_options, association).as_json
|
|
442
459
|
end
|
|
443
460
|
end
|
|
444
461
|
|
|
@@ -467,7 +484,9 @@ module ActiveModelSerializers
|
|
|
467
484
|
# }.reject! {|_,v| v.nil? }
|
|
468
485
|
def links_for(serializer)
|
|
469
486
|
serializer._links.each_with_object({}) do |(name, value), hash|
|
|
470
|
-
|
|
487
|
+
next if value.excluded?(serializer)
|
|
488
|
+
result = Link.new(serializer, value.block).as_json
|
|
489
|
+
hash[name] = result if result
|
|
471
490
|
end
|
|
472
491
|
end
|
|
473
492
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveModelSerializers
|
|
2
4
|
module Adapter
|
|
3
5
|
UnknownAdapterError = Class.new(ArgumentError)
|
|
@@ -5,11 +7,13 @@ module ActiveModelSerializers
|
|
|
5
7
|
private_constant :ADAPTER_MAP if defined?(private_constant)
|
|
6
8
|
|
|
7
9
|
class << self # All methods are class functions
|
|
10
|
+
# :nocov:
|
|
8
11
|
def new(*args)
|
|
9
12
|
fail ArgumentError, 'Adapters inherit from Adapter::Base.' \
|
|
10
13
|
"Adapter.new called with args: '#{args.inspect}', from" \
|
|
11
14
|
"'caller[0]'."
|
|
12
15
|
end
|
|
16
|
+
# :nocov:
|
|
13
17
|
|
|
14
18
|
def configured_adapter
|
|
15
19
|
lookup(ActiveModelSerializers.config.adapter)
|
|
@@ -33,7 +37,7 @@ module ActiveModelSerializers
|
|
|
33
37
|
|
|
34
38
|
# @return [Array<Symbol>] list of adapter names
|
|
35
39
|
def adapters
|
|
36
|
-
adapter_map.keys.sort
|
|
40
|
+
adapter_map.keys.sort!
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
# Adds an adapter 'klass' with 'name' to the 'adapter_map'
|
|
@@ -51,6 +55,10 @@ module ActiveModelSerializers
|
|
|
51
55
|
self
|
|
52
56
|
end
|
|
53
57
|
|
|
58
|
+
def registered_name(adapter_class)
|
|
59
|
+
ADAPTER_MAP.key adapter_class
|
|
60
|
+
end
|
|
61
|
+
|
|
54
62
|
# @param adapter [String, Symbol, Class] name to fetch adapter by
|
|
55
63
|
# @return [ActiveModelSerializers::Adapter] subclass of Adapter
|
|
56
64
|
# @raise [UnknownAdapterError]
|