mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 22:06:50 +00:00
Merge pull request #1426 from brigade/default-include
Add a default_include_tree config variable to ActiveModel::Serializer
This commit is contained in:
commit
7d7329bbcf
@ -5,6 +5,7 @@
|
|||||||
Breaking changes:
|
Breaking changes:
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
- [#1426](https://github.com/rails-api/active_model_serializers/pull/1426) Add ActiveModelSerializers.config.default_includes (@empact)
|
||||||
|
|
||||||
Fixes:
|
Fixes:
|
||||||
- [#1710](https://github.com/rails-api/active_model_serializers/pull/1710) Prevent association loading when `include_data` option
|
- [#1710](https://github.com/rails-api/active_model_serializers/pull/1710) Prevent association loading when `include_data` option
|
||||||
|
|||||||
@ -52,10 +52,12 @@ Each adapter has a default key transform configured:
|
|||||||
`config.key_transform` is a global override of the adapter default. Adapters
|
`config.key_transform` is a global override of the adapter default. Adapters
|
||||||
still prefer the render option `:key_transform` over this setting.
|
still prefer the render option `:key_transform` over this setting.
|
||||||
|
|
||||||
|
##### default_includes
|
||||||
|
What relationships to serialize by default. Default: `'*'`, which includes one level of related
|
||||||
|
objects. See [includes](adapters.md#included) for more info.
|
||||||
|
|
||||||
## JSON API
|
## JSON API
|
||||||
|
|
||||||
|
|
||||||
##### jsonapi_resource_type
|
##### jsonapi_resource_type
|
||||||
|
|
||||||
Sets whether the [type](http://jsonapi.org/format/#document-resource-identifier-objects)
|
Sets whether the [type](http://jsonapi.org/format/#document-resource-identifier-objects)
|
||||||
|
|||||||
@ -10,8 +10,6 @@ module ActiveModel
|
|||||||
module Associations
|
module Associations
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
DEFAULT_INCLUDE_TREE = ActiveModel::Serializer::IncludeTree.from_string('*')
|
|
||||||
|
|
||||||
included do
|
included do
|
||||||
with_options instance_writer: false, instance_reader: true do |serializer|
|
with_options instance_writer: false, instance_reader: true do |serializer|
|
||||||
serializer.class_attribute :_reflections
|
serializer.class_attribute :_reflections
|
||||||
@ -80,10 +78,11 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param [IncludeTree] include_tree (defaults to all associations when not provided)
|
# @param [IncludeTree] include_tree (defaults to the
|
||||||
|
# default_includes config value when not provided)
|
||||||
# @return [Enumerator<Association>]
|
# @return [Enumerator<Association>]
|
||||||
#
|
#
|
||||||
def associations(include_tree = DEFAULT_INCLUDE_TREE)
|
def associations(include_tree = ActiveModelSerializers.default_include_tree)
|
||||||
return unless object
|
return unless object
|
||||||
|
|
||||||
Enumerator.new do |y|
|
Enumerator.new do |y|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ module ActiveModel
|
|||||||
collection_serializer
|
collection_serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config.default_includes = '*'
|
||||||
config.adapter = :attributes
|
config.adapter = :attributes
|
||||||
config.jsonapi_resource_type = :plural
|
config.jsonapi_resource_type = :plural
|
||||||
config.jsonapi_version = '1.0'
|
config.jsonapi_version = '1.0'
|
||||||
|
|||||||
@ -31,6 +31,13 @@ module ActiveModelSerializers
|
|||||||
[file, lineno]
|
[file, lineno]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Memoized default include tree
|
||||||
|
# @return [ActiveModel::Serializer::IncludeTree]
|
||||||
|
def self.default_include_tree
|
||||||
|
@default_include_tree ||= ActiveModel::Serializer::IncludeTree
|
||||||
|
.from_include_args(config.default_includes)
|
||||||
|
end
|
||||||
|
|
||||||
require 'active_model/serializer/version'
|
require 'active_model/serializer/version'
|
||||||
require 'active_model/serializer'
|
require 'active_model/serializer'
|
||||||
require 'active_model/serializable_resource'
|
require 'active_model/serializable_resource'
|
||||||
|
|||||||
@ -3,8 +3,13 @@ module ActiveModelSerializers
|
|||||||
class Attributes < Base
|
class Attributes < Base
|
||||||
def initialize(serializer, options = {})
|
def initialize(serializer, options = {})
|
||||||
super
|
super
|
||||||
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include] || '*')
|
|
||||||
@cached_attributes = options[:cache_attributes] || {}
|
@cached_attributes = options[:cache_attributes] || {}
|
||||||
|
@include_tree =
|
||||||
|
if options[:include]
|
||||||
|
ActiveModel::Serializer::IncludeTree.from_include_args(options[:include])
|
||||||
|
else
|
||||||
|
ActiveModelSerializers.default_include_tree
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def serializable_hash(options = nil)
|
def serializable_hash(options = nil)
|
||||||
|
|||||||
@ -4,6 +4,10 @@ module ActionController
|
|||||||
module Serialization
|
module Serialization
|
||||||
class Json
|
class Json
|
||||||
class IncludeTest < ActionController::TestCase
|
class IncludeTest < ActionController::TestCase
|
||||||
|
INCLUDE_STRING = 'posts.comments'.freeze
|
||||||
|
INCLUDE_HASH = { posts: :comments }.freeze
|
||||||
|
DEEP_INCLUDE = 'posts.comments.author'.freeze
|
||||||
|
|
||||||
class IncludeTestController < ActionController::Base
|
class IncludeTestController < ActionController::Base
|
||||||
def setup_data
|
def setup_data
|
||||||
ActionController::Base.cache_store.clear
|
ActionController::Base.cache_store.clear
|
||||||
@ -38,17 +42,28 @@ module ActionController
|
|||||||
|
|
||||||
def render_resource_with_include_hash
|
def render_resource_with_include_hash
|
||||||
setup_data
|
setup_data
|
||||||
render json: @author, include: { posts: :comments }, adapter: :json
|
render json: @author, include: INCLUDE_HASH, adapter: :json
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_resource_with_include_string
|
def render_resource_with_include_string
|
||||||
setup_data
|
setup_data
|
||||||
render json: @author, include: 'posts.comments', adapter: :json
|
render json: @author, include: INCLUDE_STRING, adapter: :json
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_resource_with_deep_include
|
def render_resource_with_deep_include
|
||||||
setup_data
|
setup_data
|
||||||
render json: @author, include: 'posts.comments.author', adapter: :json
|
render json: @author, include: DEEP_INCLUDE, adapter: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_without_recursive_relationships
|
||||||
|
# testing recursive includes ('**') can't have any cycles in the
|
||||||
|
# relationships, or we enter an infinite loop.
|
||||||
|
author = Author.new(id: 11, name: 'Jane Doe')
|
||||||
|
post = Post.new(id: 12, title: 'Hello World', body: 'My first post')
|
||||||
|
comment = Comment.new(id: 13, body: 'Commentary')
|
||||||
|
author.posts = [post]
|
||||||
|
post.comments = [comment]
|
||||||
|
render json: author
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -77,34 +92,90 @@ module ActionController
|
|||||||
def test_render_resource_with_include_hash
|
def test_render_resource_with_include_hash
|
||||||
get :render_resource_with_include_hash
|
get :render_resource_with_include_hash
|
||||||
response = JSON.parse(@response.body)
|
response = JSON.parse(@response.body)
|
||||||
expected = {
|
|
||||||
'author' => {
|
|
||||||
'id' => 1,
|
|
||||||
'name' => 'Steve K.',
|
|
||||||
'posts' => [
|
|
||||||
{
|
|
||||||
'id' => 42, 'title' => 'New Post', 'body' => 'Body',
|
|
||||||
'comments' => [
|
|
||||||
{
|
|
||||||
'id' => 1, 'body' => 'ZOMG A COMMENT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'id' => 2, 'body' => 'ZOMG ANOTHER COMMENT'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal(expected, response)
|
assert_equal(expected_include_response, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_resource_with_include_string
|
def test_render_resource_with_include_string
|
||||||
get :render_resource_with_include_string
|
get :render_resource_with_include_string
|
||||||
|
|
||||||
response = JSON.parse(@response.body)
|
response = JSON.parse(@response.body)
|
||||||
expected = {
|
|
||||||
|
assert_equal(expected_include_response, response)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_resource_with_deep_include
|
||||||
|
get :render_resource_with_deep_include
|
||||||
|
|
||||||
|
response = JSON.parse(@response.body)
|
||||||
|
|
||||||
|
assert_equal(expected_deep_include_response, response)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_with_empty_default_includes
|
||||||
|
with_default_includes '' do
|
||||||
|
get :render_without_include
|
||||||
|
response = JSON.parse(@response.body)
|
||||||
|
expected = {
|
||||||
|
'author' => {
|
||||||
|
'id' => 1,
|
||||||
|
'name' => 'Steve K.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_equal(expected, response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_with_recursive_default_includes
|
||||||
|
with_default_includes '**' do
|
||||||
|
get :render_without_recursive_relationships
|
||||||
|
response = JSON.parse(@response.body)
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
'id' => 11,
|
||||||
|
'name' => 'Jane Doe',
|
||||||
|
'roles' => nil,
|
||||||
|
'bio' => nil,
|
||||||
|
'posts' => [
|
||||||
|
{
|
||||||
|
'id' => 12,
|
||||||
|
'title' => 'Hello World',
|
||||||
|
'body' => 'My first post',
|
||||||
|
'comments' => [
|
||||||
|
{
|
||||||
|
'id' => 13,
|
||||||
|
'body' => 'Commentary',
|
||||||
|
'post' => nil, # not set to avoid infinite recursion
|
||||||
|
'author' => nil, # not set to avoid infinite recursion
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'blog' => {
|
||||||
|
'id' => 999,
|
||||||
|
'name' => 'Custom blog',
|
||||||
|
'writer' => nil,
|
||||||
|
'articles' => nil
|
||||||
|
},
|
||||||
|
'author' => nil # not set to avoid infinite recursion
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
assert_equal(expected, response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_with_includes_overrides_default_includes
|
||||||
|
with_default_includes '' do
|
||||||
|
get :render_resource_with_include_hash
|
||||||
|
response = JSON.parse(@response.body)
|
||||||
|
|
||||||
|
assert_equal(expected_include_response, response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def expected_include_response
|
||||||
|
{
|
||||||
'author' => {
|
'author' => {
|
||||||
'id' => 1,
|
'id' => 1,
|
||||||
'name' => 'Steve K.',
|
'name' => 'Steve K.',
|
||||||
@ -123,15 +194,10 @@ module ActionController
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal(expected, response)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_resource_with_deep_include
|
def expected_deep_include_response
|
||||||
get :render_resource_with_deep_include
|
{
|
||||||
|
|
||||||
response = JSON.parse(@response.body)
|
|
||||||
expected = {
|
|
||||||
'author' => {
|
'author' => {
|
||||||
'id' => 1,
|
'id' => 1,
|
||||||
'name' => 'Steve K.',
|
'name' => 'Steve K.',
|
||||||
@ -158,8 +224,21 @@ module ActionController
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
end
|
||||||
|
|
||||||
assert_equal(expected, response)
|
def with_default_includes(include_tree)
|
||||||
|
original = ActiveModelSerializers.config.default_includes
|
||||||
|
ActiveModelSerializers.config.default_includes = include_tree
|
||||||
|
clear_include_tree_cache
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
ActiveModelSerializers.config.default_includes = original
|
||||||
|
clear_include_tree_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear_include_tree_cache
|
||||||
|
ActiveModelSerializers
|
||||||
|
.instance_variable_set(:@default_include_tree, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user