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,15 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# A Grape response formatter that can be used as 'formatter :json, Grape::Formatters::ActiveModelSerializers'
|
|
2
4
|
#
|
|
3
5
|
# Serializer options can be passed as a hash from your Grape endpoint using env[:active_model_serializer_options],
|
|
4
6
|
# or better yet user the render helper in Grape::Helpers::ActiveModelSerializers
|
|
7
|
+
|
|
8
|
+
require 'active_model_serializers/serialization_context'
|
|
9
|
+
|
|
5
10
|
module Grape
|
|
6
11
|
module Formatters
|
|
7
12
|
module ActiveModelSerializers
|
|
8
13
|
def self.call(resource, env)
|
|
9
|
-
serializer_options =
|
|
10
|
-
serializer_options.merge!(env[:active_model_serializer_options]) if env[:active_model_serializer_options]
|
|
14
|
+
serializer_options = build_serializer_options(env)
|
|
11
15
|
::ActiveModelSerializers::SerializableResource.new(resource, serializer_options).to_json
|
|
12
16
|
end
|
|
17
|
+
|
|
18
|
+
def self.build_serializer_options(env)
|
|
19
|
+
ams_options = env[:active_model_serializer_options] || {}
|
|
20
|
+
|
|
21
|
+
# Add serialization context
|
|
22
|
+
ams_options.fetch(:serialization_context) do
|
|
23
|
+
request = env['grape.request']
|
|
24
|
+
ams_options[:serialization_context] = ::ActiveModelSerializers::SerializationContext.new(
|
|
25
|
+
request_url: request.url[/\A[^?]+/],
|
|
26
|
+
query_parameters: request.params
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
ams_options
|
|
31
|
+
end
|
|
13
32
|
end
|
|
14
33
|
end
|
|
15
34
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'rubocop'
|
|
5
|
+
require 'rubocop/rake_task'
|
|
6
|
+
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
|
7
|
+
else
|
|
8
|
+
require 'rbconfig'
|
|
9
|
+
# https://github.com/bundler/bundler/blob/1b3eb2465a/lib/bundler/constants.rb#L2
|
|
10
|
+
windows_platforms = /(msdos|mswin|djgpp|mingw)/
|
|
11
|
+
if RbConfig::CONFIG['host_os'] =~ windows_platforms
|
|
12
|
+
desc 'No-op rubocop on Windows-- unsupported platform'
|
|
13
|
+
task :rubocop do
|
|
14
|
+
puts 'Skipping rubocop on Windows'
|
|
15
|
+
end
|
|
16
|
+
elsif defined?(::Rubinius)
|
|
17
|
+
desc 'No-op rubocop to avoid rbx segfault'
|
|
18
|
+
task :rubocop do
|
|
19
|
+
puts 'Skipping rubocop on rbx due to segfault'
|
|
20
|
+
puts 'https://github.com/rubinius/rubinius/issues/3499'
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop)
|
|
24
|
+
patterns = [
|
|
25
|
+
'Gemfile',
|
|
26
|
+
'Rakefile',
|
|
27
|
+
'lib/**/*.{rb,rake}',
|
|
28
|
+
'config/**/*.rb',
|
|
29
|
+
'app/**/*.rb',
|
|
30
|
+
'test/**/*.rb'
|
|
31
|
+
]
|
|
32
|
+
desc 'Execute rubocop'
|
|
33
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
|
34
|
+
task.options = ['--rails', '--display-cop-names', '--display-style-guide']
|
|
35
|
+
task.formatters = ['progress']
|
|
36
|
+
task.patterns = patterns
|
|
37
|
+
task.fail_on_error = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
namespace :rubocop do
|
|
41
|
+
desc 'Auto-gen rubocop config'
|
|
42
|
+
task :auto_gen_config do
|
|
43
|
+
options = ['--auto-gen-config'].concat patterns
|
|
44
|
+
require 'benchmark'
|
|
45
|
+
result = 0
|
|
46
|
+
cli = RuboCop::CLI.new
|
|
47
|
+
time = Benchmark.realtime do
|
|
48
|
+
result = cli.run(options)
|
|
49
|
+
end
|
|
50
|
+
puts "Finished in #{time} seconds" if cli.options[:debug]
|
|
51
|
+
abort('RuboCop failed!') if result.nonzero?
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -1,21 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'test_helper'
|
|
2
4
|
|
|
3
5
|
module ActionController
|
|
4
6
|
module Serialization
|
|
5
7
|
class AdapterSelectorTest < ActionController::TestCase
|
|
8
|
+
class Profile < Model
|
|
9
|
+
attributes :id, :name, :description
|
|
10
|
+
associations :comments
|
|
11
|
+
end
|
|
12
|
+
class ProfileSerializer < ActiveModel::Serializer
|
|
13
|
+
type 'profiles'
|
|
14
|
+
attributes :name, :description
|
|
15
|
+
end
|
|
16
|
+
|
|
6
17
|
class AdapterSelectorTestController < ActionController::Base
|
|
7
18
|
def render_using_default_adapter
|
|
8
|
-
@profile = Profile.new(
|
|
19
|
+
@profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
|
9
20
|
render json: @profile
|
|
10
21
|
end
|
|
11
22
|
|
|
12
23
|
def render_using_adapter_override
|
|
13
|
-
@profile = Profile.new(
|
|
24
|
+
@profile = Profile.new(id: 'render_using_adapter_override', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
|
14
25
|
render json: @profile, adapter: :json_api
|
|
15
26
|
end
|
|
16
27
|
|
|
17
28
|
def render_skipping_adapter
|
|
18
|
-
@profile = Profile.new(
|
|
29
|
+
@profile = Profile.new(id: 'render_skipping_adapter_id', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
|
19
30
|
render json: @profile, adapter: false
|
|
20
31
|
end
|
|
21
32
|
end
|
|
@@ -32,7 +43,7 @@ module ActionController
|
|
|
32
43
|
|
|
33
44
|
expected = {
|
|
34
45
|
data: {
|
|
35
|
-
id:
|
|
46
|
+
id: 'render_using_adapter_override',
|
|
36
47
|
type: 'profiles',
|
|
37
48
|
attributes: {
|
|
38
49
|
name: 'Name 1',
|
|
@@ -46,7 +57,7 @@ module ActionController
|
|
|
46
57
|
|
|
47
58
|
def test_render_skipping_adapter
|
|
48
59
|
get :render_skipping_adapter
|
|
49
|
-
assert_equal '{"
|
|
60
|
+
assert_equal '{"id":"render_skipping_adapter_id","name":"Name 1","description":"Description 1"}', response.body
|
|
50
61
|
end
|
|
51
62
|
end
|
|
52
63
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'test_helper'
|
|
2
4
|
|
|
3
5
|
module ActionController
|
|
@@ -100,11 +102,12 @@ module ActionController
|
|
|
100
102
|
get :render_array_using_explicit_serializer_and_custom_serializers
|
|
101
103
|
|
|
102
104
|
expected = [
|
|
103
|
-
{
|
|
105
|
+
{
|
|
106
|
+
'title' => 'New Post',
|
|
104
107
|
'body' => 'Body',
|
|
105
|
-
'id' =>
|
|
108
|
+
'id' => @controller.instance_variable_get(:@post).id,
|
|
106
109
|
'comments' => [{ 'id' => 1 }, { 'id' => 2 }],
|
|
107
|
-
'author' => { 'id' =>
|
|
110
|
+
'author' => { 'id' => @controller.instance_variable_get(:@author).id }
|
|
108
111
|
}
|
|
109
112
|
]
|
|
110
113
|
|
|
@@ -122,7 +125,7 @@ module ActionController
|
|
|
122
125
|
id: 42,
|
|
123
126
|
lat: '-23.550520',
|
|
124
127
|
lng: '-46.633309',
|
|
125
|
-
|
|
128
|
+
address: 'Nowhere' # is a virtual attribute on LocationSerializer
|
|
126
129
|
}
|
|
127
130
|
]
|
|
128
131
|
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'test_helper'
|
|
2
4
|
|
|
3
5
|
module ActionController
|
|
4
6
|
module Serialization
|
|
5
7
|
class Json
|
|
6
8
|
class IncludeTest < ActionController::TestCase
|
|
9
|
+
INCLUDE_STRING = 'posts.comments'.freeze
|
|
10
|
+
INCLUDE_HASH = { posts: :comments }.freeze
|
|
11
|
+
DEEP_INCLUDE = 'posts.comments.author'.freeze
|
|
12
|
+
|
|
7
13
|
class IncludeTestController < ActionController::Base
|
|
8
14
|
def setup_data
|
|
9
15
|
ActionController::Base.cache_store.clear
|
|
@@ -38,17 +44,28 @@ module ActionController
|
|
|
38
44
|
|
|
39
45
|
def render_resource_with_include_hash
|
|
40
46
|
setup_data
|
|
41
|
-
render json: @author, include:
|
|
47
|
+
render json: @author, include: INCLUDE_HASH, adapter: :json
|
|
42
48
|
end
|
|
43
49
|
|
|
44
50
|
def render_resource_with_include_string
|
|
45
51
|
setup_data
|
|
46
|
-
render json: @author, include:
|
|
52
|
+
render json: @author, include: INCLUDE_STRING, adapter: :json
|
|
47
53
|
end
|
|
48
54
|
|
|
49
55
|
def render_resource_with_deep_include
|
|
50
56
|
setup_data
|
|
51
|
-
render json: @author, include:
|
|
57
|
+
render json: @author, include: DEEP_INCLUDE, adapter: :json
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def render_without_recursive_relationships
|
|
61
|
+
# testing recursive includes ('**') can't have any cycles in the
|
|
62
|
+
# relationships, or we enter an infinite loop.
|
|
63
|
+
author = Author.new(id: 11, name: 'Jane Doe')
|
|
64
|
+
post = Post.new(id: 12, title: 'Hello World', body: 'My first post')
|
|
65
|
+
comment = Comment.new(id: 13, body: 'Commentary')
|
|
66
|
+
author.posts = [post]
|
|
67
|
+
post.comments = [comment]
|
|
68
|
+
render json: author
|
|
52
69
|
end
|
|
53
70
|
end
|
|
54
71
|
|
|
@@ -77,34 +94,90 @@ module ActionController
|
|
|
77
94
|
def test_render_resource_with_include_hash
|
|
78
95
|
get :render_resource_with_include_hash
|
|
79
96
|
response = JSON.parse(@response.body)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
97
|
+
|
|
98
|
+
assert_equal(expected_include_response, response)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_render_resource_with_include_string
|
|
102
|
+
get :render_resource_with_include_string
|
|
103
|
+
|
|
104
|
+
response = JSON.parse(@response.body)
|
|
105
|
+
|
|
106
|
+
assert_equal(expected_include_response, response)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_render_resource_with_deep_include
|
|
110
|
+
get :render_resource_with_deep_include
|
|
111
|
+
|
|
112
|
+
response = JSON.parse(@response.body)
|
|
113
|
+
|
|
114
|
+
assert_equal(expected_deep_include_response, response)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_render_with_empty_default_includes
|
|
118
|
+
with_default_includes '' do
|
|
119
|
+
get :render_without_include
|
|
120
|
+
response = JSON.parse(@response.body)
|
|
121
|
+
expected = {
|
|
122
|
+
'author' => {
|
|
123
|
+
'id' => 1,
|
|
124
|
+
'name' => 'Steve K.'
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
assert_equal(expected, response)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_render_with_recursive_default_includes
|
|
132
|
+
with_default_includes '**' do
|
|
133
|
+
get :render_without_recursive_relationships
|
|
134
|
+
response = JSON.parse(@response.body)
|
|
135
|
+
|
|
136
|
+
expected = {
|
|
137
|
+
'id' => 11,
|
|
138
|
+
'name' => 'Jane Doe',
|
|
139
|
+
'roles' => nil,
|
|
140
|
+
'bio' => nil,
|
|
84
141
|
'posts' => [
|
|
85
142
|
{
|
|
86
|
-
'id' =>
|
|
143
|
+
'id' => 12,
|
|
144
|
+
'title' => 'Hello World',
|
|
145
|
+
'body' => 'My first post',
|
|
87
146
|
'comments' => [
|
|
88
147
|
{
|
|
89
|
-
'id' =>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
'
|
|
148
|
+
'id' => 13,
|
|
149
|
+
'body' => 'Commentary',
|
|
150
|
+
'post' => nil, # not set to avoid infinite recursion
|
|
151
|
+
'author' => nil, # not set to avoid infinite recursion
|
|
93
152
|
}
|
|
94
|
-
]
|
|
153
|
+
],
|
|
154
|
+
'blog' => {
|
|
155
|
+
'id' => 999,
|
|
156
|
+
'name' => 'Custom blog',
|
|
157
|
+
'writer' => nil,
|
|
158
|
+
'articles' => nil
|
|
159
|
+
},
|
|
160
|
+
'author' => nil # not set to avoid infinite recursion
|
|
95
161
|
}
|
|
96
162
|
]
|
|
97
163
|
}
|
|
98
|
-
|
|
164
|
+
assert_equal(expected, response)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
99
167
|
|
|
100
|
-
|
|
168
|
+
def test_render_with_includes_overrides_default_includes
|
|
169
|
+
with_default_includes '' do
|
|
170
|
+
get :render_resource_with_include_hash
|
|
171
|
+
response = JSON.parse(@response.body)
|
|
172
|
+
|
|
173
|
+
assert_equal(expected_include_response, response)
|
|
174
|
+
end
|
|
101
175
|
end
|
|
102
176
|
|
|
103
|
-
|
|
104
|
-
get :render_resource_with_include_string
|
|
177
|
+
private
|
|
105
178
|
|
|
106
|
-
|
|
107
|
-
|
|
179
|
+
def expected_include_response
|
|
180
|
+
{
|
|
108
181
|
'author' => {
|
|
109
182
|
'id' => 1,
|
|
110
183
|
'name' => 'Steve K.',
|
|
@@ -123,15 +196,10 @@ module ActionController
|
|
|
123
196
|
]
|
|
124
197
|
}
|
|
125
198
|
}
|
|
126
|
-
|
|
127
|
-
assert_equal(expected, response)
|
|
128
199
|
end
|
|
129
200
|
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
response = JSON.parse(@response.body)
|
|
134
|
-
expected = {
|
|
201
|
+
def expected_deep_include_response
|
|
202
|
+
{
|
|
135
203
|
'author' => {
|
|
136
204
|
'id' => 1,
|
|
137
205
|
'name' => 'Steve K.',
|
|
@@ -158,8 +226,21 @@ module ActionController
|
|
|
158
226
|
]
|
|
159
227
|
}
|
|
160
228
|
}
|
|
229
|
+
end
|
|
161
230
|
|
|
162
|
-
|
|
231
|
+
def with_default_includes(include_directive)
|
|
232
|
+
original = ActiveModelSerializers.config.default_includes
|
|
233
|
+
ActiveModelSerializers.config.default_includes = include_directive
|
|
234
|
+
clear_include_directive_cache
|
|
235
|
+
yield
|
|
236
|
+
ensure
|
|
237
|
+
ActiveModelSerializers.config.default_includes = original
|
|
238
|
+
clear_include_directive_cache
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def clear_include_directive_cache
|
|
242
|
+
ActiveModelSerializers
|
|
243
|
+
.instance_variable_set(:@default_include_directive, nil)
|
|
163
244
|
end
|
|
164
245
|
end
|
|
165
246
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'test_helper'
|
|
2
4
|
|
|
3
5
|
module ActionController
|
|
@@ -45,7 +47,7 @@ module ActionController
|
|
|
45
47
|
response = JSON.parse(@response.body)
|
|
46
48
|
expected = {
|
|
47
49
|
'restriction_for_id' => '67',
|
|
48
|
-
'restriction_for_type' => '
|
|
50
|
+
'restriction_for_type' => 'Discount',
|
|
49
51
|
'restricted_to_id' => nil,
|
|
50
52
|
'restricted_to_type' => nil
|
|
51
53
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'test_helper'
|
|
2
4
|
|
|
3
5
|
module ActionController
|
|
@@ -7,18 +9,17 @@ module ActionController
|
|
|
7
9
|
def test_active_model_with_multiple_errors
|
|
8
10
|
get :render_resource_with_errors
|
|
9
11
|
|
|
10
|
-
expected_errors_object =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
]
|
|
12
|
+
expected_errors_object = {
|
|
13
|
+
errors: [
|
|
14
|
+
{ source: { pointer: '/data/attributes/name' }, detail: 'cannot be nil' },
|
|
15
|
+
{ source: { pointer: '/data/attributes/name' }, detail: 'must be longer' },
|
|
16
|
+
{ source: { pointer: '/data/attributes/id' }, detail: 'must be a uuid' }
|
|
17
|
+
]
|
|
17
18
|
}.to_json
|
|
18
|
-
assert_equal
|
|
19
|
+
assert_equal json_response_body.to_json, expected_errors_object
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
def
|
|
22
|
+
def json_response_body
|
|
22
23
|
JSON.load(@response.body)
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
module ActionController
|
|
6
|
+
module Serialization
|
|
7
|
+
class JsonApi
|
|
8
|
+
class FieldsTest < ActionController::TestCase
|
|
9
|
+
class FieldsTestController < ActionController::Base
|
|
10
|
+
class AuthorWithName < Author
|
|
11
|
+
attributes :first_name, :last_name
|
|
12
|
+
end
|
|
13
|
+
class AuthorWithNameSerializer < AuthorSerializer
|
|
14
|
+
type 'authors'
|
|
15
|
+
end
|
|
16
|
+
class PostWithPublishAt < Post
|
|
17
|
+
attributes :publish_at
|
|
18
|
+
end
|
|
19
|
+
class PostWithPublishAtSerializer < ActiveModel::Serializer
|
|
20
|
+
type 'posts'
|
|
21
|
+
attributes :title, :body, :publish_at
|
|
22
|
+
belongs_to :author
|
|
23
|
+
has_many :comments
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def setup_post
|
|
27
|
+
ActionController::Base.cache_store.clear
|
|
28
|
+
@author = AuthorWithName.new(id: 1, first_name: 'Bob', last_name: 'Jones')
|
|
29
|
+
@comment1 = Comment.new(id: 7, body: 'cool', author: @author)
|
|
30
|
+
@comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
|
|
31
|
+
@post = PostWithPublishAt.new(id: 1337, title: 'Title 1', body: 'Body 1',
|
|
32
|
+
author: @author, comments: [@comment1, @comment2],
|
|
33
|
+
publish_at: '2020-03-16T03:55:25.291Z')
|
|
34
|
+
@comment1.post = @post
|
|
35
|
+
@comment2.post = @post
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def render_fields_works_on_relationships
|
|
39
|
+
setup_post
|
|
40
|
+
render json: @post, serializer: PostWithPublishAtSerializer, adapter: :json_api, fields: { posts: [:author] }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
tests FieldsTestController
|
|
45
|
+
|
|
46
|
+
test 'fields works on relationships' do
|
|
47
|
+
get :render_fields_works_on_relationships
|
|
48
|
+
response = JSON.parse(@response.body)
|
|
49
|
+
expected = {
|
|
50
|
+
'data' => {
|
|
51
|
+
'id' => '1337',
|
|
52
|
+
'type' => 'posts',
|
|
53
|
+
'relationships' => {
|
|
54
|
+
'author' => {
|
|
55
|
+
'data' => {
|
|
56
|
+
'id' => '1',
|
|
57
|
+
'type' => 'authors'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
assert_equal expected, response
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'test_helper'
|
|
2
4
|
|
|
3
5
|
module ActionController
|
|
4
6
|
module Serialization
|
|
5
7
|
class JsonApi
|
|
6
|
-
class LinkedTest <
|
|
8
|
+
class LinkedTest < ActionDispatch::IntegrationTest
|
|
7
9
|
class LinkedTestController < ActionController::Base
|
|
8
|
-
require 'active_model_serializers/register_jsonapi_renderer'
|
|
9
10
|
def setup_post
|
|
10
11
|
ActionController::Base.cache_store.clear
|
|
11
12
|
@role1 = Role.new(id: 1, name: 'admin')
|
|
@@ -39,62 +40,68 @@ module ActionController
|
|
|
39
40
|
|
|
40
41
|
def render_resource_without_include
|
|
41
42
|
setup_post
|
|
42
|
-
render
|
|
43
|
+
render json: @post
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def render_resource_with_include
|
|
46
47
|
setup_post
|
|
47
|
-
render
|
|
48
|
+
render json: @post, adapter: :json_api, include: [:author]
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
def render_resource_with_include_of_custom_key_by_original
|
|
51
52
|
setup_post
|
|
52
|
-
render
|
|
53
|
+
render json: @post, adapter: :json_api, include: [:reviews], serializer: PostWithCustomKeysSerializer
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def render_resource_with_nested_include
|
|
56
57
|
setup_post
|
|
57
|
-
render
|
|
58
|
+
render json: @post, adapter: :json_api, include: [comments: [:author]]
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def render_resource_with_nested_has_many_include_wildcard
|
|
61
62
|
setup_post
|
|
62
|
-
render
|
|
63
|
+
render json: @post, adapter: :json_api, include: 'author.*'
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
def render_resource_with_missing_nested_has_many_include
|
|
66
67
|
setup_post
|
|
67
68
|
@post.author = @author2 # author2 has no roles.
|
|
68
|
-
render
|
|
69
|
+
render json: @post, adapter: :json_api, include: [author: [:roles]]
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
def render_collection_with_missing_nested_has_many_include
|
|
72
73
|
setup_post
|
|
73
74
|
@post.author = @author2
|
|
74
|
-
render
|
|
75
|
+
render json: [@post, @post2], adapter: :json_api, include: [author: [:roles]]
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
def render_collection_without_include
|
|
78
79
|
setup_post
|
|
79
|
-
render
|
|
80
|
+
render json: [@post], adapter: :json_api
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
def render_collection_with_include
|
|
83
84
|
setup_post
|
|
84
|
-
render
|
|
85
|
+
render json: [@post], adapter: :json_api, include: 'author, comments'
|
|
85
86
|
end
|
|
86
87
|
end
|
|
87
88
|
|
|
88
|
-
|
|
89
|
+
setup do
|
|
90
|
+
@routes = Rails.application.routes.draw do
|
|
91
|
+
ActiveSupport::Deprecation.silence do
|
|
92
|
+
match ':action', to: LinkedTestController, via: [:get, :post]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
89
96
|
|
|
90
97
|
def test_render_resource_without_include
|
|
91
|
-
get
|
|
98
|
+
get '/render_resource_without_include'
|
|
92
99
|
response = JSON.parse(@response.body)
|
|
93
100
|
refute response.key? 'included'
|
|
94
101
|
end
|
|
95
102
|
|
|
96
103
|
def test_render_resource_with_include
|
|
97
|
-
get
|
|
104
|
+
get '/render_resource_with_include'
|
|
98
105
|
response = JSON.parse(@response.body)
|
|
99
106
|
assert response.key? 'included'
|
|
100
107
|
assert_equal 1, response['included'].size
|
|
@@ -102,7 +109,7 @@ module ActionController
|
|
|
102
109
|
end
|
|
103
110
|
|
|
104
111
|
def test_render_resource_with_nested_has_many_include
|
|
105
|
-
get
|
|
112
|
+
get '/render_resource_with_nested_has_many_include_wildcard'
|
|
106
113
|
response = JSON.parse(@response.body)
|
|
107
114
|
expected_linked = [
|
|
108
115
|
{
|
|
@@ -144,7 +151,7 @@ module ActionController
|
|
|
144
151
|
end
|
|
145
152
|
|
|
146
153
|
def test_render_resource_with_include_of_custom_key_by_original
|
|
147
|
-
get
|
|
154
|
+
get '/render_resource_with_include_of_custom_key_by_original'
|
|
148
155
|
response = JSON.parse(@response.body)
|
|
149
156
|
assert response.key? 'included'
|
|
150
157
|
|
|
@@ -156,39 +163,39 @@ module ActionController
|
|
|
156
163
|
end
|
|
157
164
|
|
|
158
165
|
def test_render_resource_with_nested_include
|
|
159
|
-
get
|
|
166
|
+
get '/render_resource_with_nested_include'
|
|
160
167
|
response = JSON.parse(@response.body)
|
|
161
168
|
assert response.key? 'included'
|
|
162
169
|
assert_equal 3, response['included'].size
|
|
163
170
|
end
|
|
164
171
|
|
|
165
172
|
def test_render_collection_without_include
|
|
166
|
-
get
|
|
173
|
+
get '/render_collection_without_include'
|
|
167
174
|
response = JSON.parse(@response.body)
|
|
168
175
|
refute response.key? 'included'
|
|
169
176
|
end
|
|
170
177
|
|
|
171
178
|
def test_render_collection_with_include
|
|
172
|
-
get
|
|
179
|
+
get '/render_collection_with_include'
|
|
173
180
|
response = JSON.parse(@response.body)
|
|
174
181
|
assert response.key? 'included'
|
|
175
182
|
end
|
|
176
183
|
|
|
177
184
|
def test_render_resource_with_nested_attributes_even_when_missing_associations
|
|
178
|
-
get
|
|
185
|
+
get '/render_resource_with_missing_nested_has_many_include'
|
|
179
186
|
response = JSON.parse(@response.body)
|
|
180
187
|
assert response.key? 'included'
|
|
181
|
-
refute
|
|
188
|
+
refute include_type?(response['included'], 'roles')
|
|
182
189
|
end
|
|
183
190
|
|
|
184
191
|
def test_render_collection_with_missing_nested_has_many_include
|
|
185
|
-
get
|
|
192
|
+
get '/render_collection_with_missing_nested_has_many_include'
|
|
186
193
|
response = JSON.parse(@response.body)
|
|
187
194
|
assert response.key? 'included'
|
|
188
|
-
assert
|
|
195
|
+
assert include_type?(response['included'], 'roles')
|
|
189
196
|
end
|
|
190
197
|
|
|
191
|
-
def
|
|
198
|
+
def include_type?(collection, value)
|
|
192
199
|
collection.detect { |i| i['type'] == value }
|
|
193
200
|
end
|
|
194
201
|
end
|