Merge pull request #1251 from bf4/collection_serializer

Rename ArraySerializer to CollectionSerializer for clarity
This commit is contained in:
Benjamin Fleischer 2015-10-22 10:29:02 -05:00
commit 8e1245a5b2
19 changed files with 213 additions and 148 deletions

View File

@ -71,7 +71,7 @@ Style/BracesAroundHashParameters:
- 'test/adapter/json_api/pagination_links_test.rb' - 'test/adapter/json_api/pagination_links_test.rb'
- 'test/adapter/null_test.rb' - 'test/adapter/null_test.rb'
- 'test/adapter_test.rb' - 'test/adapter_test.rb'
- 'test/array_serializer_test.rb' - 'test/collection_serializer_test.rb'
- 'test/serializable_resource_test.rb' - 'test/serializable_resource_test.rb'
- 'test/serializers/associations_test.rb' - 'test/serializers/associations_test.rb'
- 'test/serializers/attribute_test.rb' - 'test/serializers/attribute_test.rb'

View File

@ -20,6 +20,8 @@ Features:
- [#1127](https://github.com/rails-api/active_model_serializers/pull/1127) Add support for nested - [#1127](https://github.com/rails-api/active_model_serializers/pull/1127) Add support for nested
associations for JSON and Attributes adapters via the `include` option (@NullVoxPopuli, @beauby). associations for JSON and Attributes adapters via the `include` option (@NullVoxPopuli, @beauby).
- [#1050](https://github.com/rails-api/active_model_serializers/pull/1050) Add support for toplevel jsonapi member (@beauby, @bf4) - [#1050](https://github.com/rails-api/active_model_serializers/pull/1050) Add support for toplevel jsonapi member (@beauby, @bf4)
- [#tbd](https://github.com/rails-api/active_model_serializers/pull/tbd) Rename ArraySerializer to
CollectionSerializer for clarity, add ActiveModelSerializers.config.collection_serializer (@bf4)
Fixes: Fixes:
- [#1239](https://github.com/rails-api/active_model_serializers/pull/1239) Fix duplicates in JSON API compound documents (@beauby) - [#1239](https://github.com/rails-api/active_model_serializers/pull/1239) Fix duplicates in JSON API compound documents (@beauby)

View File

@ -120,7 +120,7 @@ If you wish to use a serializer other than the default, you can explicitly pass
#### 2. For an array resource: #### 2. For an array resource:
```ruby ```ruby
# Use the default `ArraySerializer`, which will use `each_serializer` to # Use the default `CollectionSerializer`, which will use `each_serializer` to
# serialize each element # serialize each element
render json: @posts, each_serializer: PostPreviewSerializer render json: @posts, each_serializer: PostPreviewSerializer

View File

@ -75,7 +75,7 @@ render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPrevi
And then, you could do something like the following class. And then, you could do something like the following class.
```ruby ```ruby
class PaginatedSerializer < ActiveModel::Serializer::ArraySerializer class PaginatedSerializer < ActiveModel::Serializer::CollectionSerializer
def initialize(object, options={}) def initialize(object, options={})
meta_key = options[:meta_key] || :meta meta_key = options[:meta_key] || :meta
options[meta_key] ||= {} options[meta_key] ||= {}

View File

@ -31,7 +31,7 @@ module ActionController
serializable_resource.serialization_scope_name = _serialization_scope serializable_resource.serialization_scope_name = _serialization_scope
begin begin
serializable_resource.adapter serializable_resource.adapter
rescue ActiveModel::Serializer::ArraySerializer::NoSerializerError rescue ActiveModel::Serializer::CollectionSerializer::NoSerializerError
resource resource
end end
else else

View File

@ -1,4 +1,5 @@
require 'thread_safe' require 'thread_safe'
require 'active_model/serializer/collection_serializer'
require 'active_model/serializer/array_serializer' require 'active_model/serializer/array_serializer'
require 'active_model/serializer/include_tree' require 'active_model/serializer/include_tree'
require 'active_model/serializer/associations' require 'active_model/serializer/associations'
@ -105,7 +106,7 @@ module ActiveModel
if resource.respond_to?(:serializer_class) if resource.respond_to?(:serializer_class)
resource.serializer_class resource.serializer_class
elsif resource.respond_to?(:to_ary) elsif resource.respond_to?(:to_ary)
config.array_serializer config.collection_serializer
else else
options.fetch(:serializer) { get_serializer_for(resource.class) } options.fetch(:serializer) { get_serializer_for(resource.class) }
end end

View File

@ -1,41 +1,9 @@
module ActiveModel require 'active_model/serializer/collection_serializer'
class Serializer class ActiveModel::Serializer
class ArraySerializer class ArraySerializer < CollectionSerializer
NoSerializerError = Class.new(StandardError) def initialize(*)
include Enumerable warn "Calling deprecated ArraySerializer in #{caller[0]}. Please use CollectionSerializer"
delegate :each, to: :@serializers super
attr_reader :object, :root
def initialize(resources, options = {})
@root = options[:root]
@object = resources
@serializers = resources.map do |resource|
serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
serializer_class = options.fetch(:serializer) { serializer_context_class.serializer_for(resource) }
if serializer_class.nil?
fail NoSerializerError, "No serializer found for resource: #{resource.inspect}"
else
serializer_class.new(resource, options.except(:serializer))
end
end
end
def json_key
key = root || serializers.first.try(:json_key) || object.try(:name).try(:underscore)
key.try(:pluralize)
end
def paginated?
object.respond_to?(:current_page) &&
object.respond_to?(:total_pages) &&
object.respond_to?(:size)
end
protected
attr_reader :serializers
end end
end end
end end

View File

@ -0,0 +1,41 @@
module ActiveModel
class Serializer
class CollectionSerializer
NoSerializerError = Class.new(StandardError)
include Enumerable
delegate :each, to: :@serializers
attr_reader :object, :root
def initialize(resources, options = {})
@root = options[:root]
@object = resources
@serializers = resources.map do |resource|
serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
serializer_class = options.fetch(:serializer) { serializer_context_class.serializer_for(resource) }
if serializer_class.nil?
fail NoSerializerError, "No serializer found for resource: #{resource.inspect}"
else
serializer_class.new(resource, options.except(:serializer))
end
end
end
def json_key
key = root || serializers.first.try(:json_key) || object.try(:name).try(:underscore)
key.try(:pluralize)
end
def paginated?
object.respond_to?(:current_page) &&
object.respond_to?(:total_pages) &&
object.respond_to?(:size)
end
protected
attr_reader :serializers
end
end
end

View File

@ -7,9 +7,19 @@ module ActiveModel
# Configuration options may also be set in # Configuration options may also be set in
# Serializers and Adapters # Serializers and Adapters
included do |base| included do |base|
base.config.array_serializer = ActiveModel::Serializer::ArraySerializer config = base.config
base.config.adapter = :attributes config.collection_serializer = ActiveModel::Serializer::CollectionSerializer
base.config.jsonapi_resource_type = :plural
def config.array_serializer=(collection_serializer)
self.collection_serializer = collection_serializer
end
def config.array_serializer
collection_serializer
end
config.adapter = :attributes
config.jsonapi_resource_type = :plural
end end
end end
end end

View File

@ -50,7 +50,7 @@ module ActiveModel
association_value, association_value,
serializer_options(subject, parent_serializer_options, reflection_options) serializer_options(subject, parent_serializer_options, reflection_options)
) )
rescue ActiveModel::Serializer::ArraySerializer::NoSerializerError rescue ActiveModel::Serializer::CollectionSerializer::NoSerializerError
reflection_options[:virtual_value] = association_value.try(:as_json) || association_value reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
end end
elsif !association_value.nil? && !association_value.instance_of?(Object) elsif !association_value.nil? && !association_value.instance_of?(Object)

View File

@ -23,7 +23,7 @@ module ActiveModel
def test_with_serializer_option def test_with_serializer_option
@blog.special_attribute = 'Special' @blog.special_attribute = 'Special'
@blog.articles = [@first_post, @second_post] @blog.articles = [@first_post, @second_post]
serializer = ArraySerializer.new([@blog], serializer: CustomBlogSerializer) serializer = CollectionSerializer.new([@blog], serializer: CustomBlogSerializer)
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
expected = { blogs: [{ expected = { blogs: [{
@ -35,7 +35,7 @@ module ActiveModel
end end
def test_include_multiple_posts def test_include_multiple_posts
serializer = ArraySerializer.new([@first_post, @second_post]) serializer = CollectionSerializer.new([@first_post, @second_post])
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
expected = { posts: [{ expected = { posts: [{
@ -70,14 +70,14 @@ module ActiveModel
def test_root_is_underscored def test_root_is_underscored
virtual_value = VirtualValue.new(id: 1) virtual_value = VirtualValue.new(id: 1)
serializer = ArraySerializer.new([virtual_value]) serializer = CollectionSerializer.new([virtual_value])
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
assert_equal 1, adapter.serializable_hash[:virtual_values].length assert_equal 1, adapter.serializable_hash[:virtual_values].length
end end
def test_include_option def test_include_option
serializer = ArraySerializer.new([@first_post, @second_post]) serializer = CollectionSerializer.new([@first_post, @second_post])
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer, include: '') adapter = ActiveModel::Serializer::Adapter::Json.new(serializer, include: '')
actual = adapter.serializable_hash actual = adapter.serializable_hash
expected = { posts: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, expected = { posts: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' },

View File

@ -19,7 +19,7 @@ module ActiveModel
@second_post.author = @author @second_post.author = @author
@author.posts = [@first_post, @second_post] @author.posts = [@first_post, @second_post]
@serializer = ArraySerializer.new([@first_post, @second_post]) @serializer = CollectionSerializer.new([@first_post, @second_post])
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
ActionController::Base.cache_store.clear ActionController::Base.cache_store.clear
end end

View File

@ -45,7 +45,7 @@ module ActiveModel
end end
def test_include_multiple_posts_and_linked_array def test_include_multiple_posts_and_linked_array
serializer = ArraySerializer.new([@first_post, @second_post]) serializer = CollectionSerializer.new([@first_post, @second_post])
adapter = ActiveModel::Serializer::Adapter::JsonApi.new( adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
serializer, serializer,
include: [:comments, author: [:bio]] include: [:comments, author: [:bio]]
@ -226,7 +226,7 @@ module ActiveModel
end end
def test_multiple_references_to_same_resource def test_multiple_references_to_same_resource
serializer = ArraySerializer.new([@first_comment, @second_comment]) serializer = CollectionSerializer.new([@first_comment, @second_comment])
adapter = ActiveModel::Serializer::Adapter::JsonApi.new( adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
serializer, serializer,
include: [:post] include: [:post]

View File

@ -1,92 +1,21 @@
require 'test_helper' require 'test_helper'
require_relative 'collection_serializer_test'
module ActiveModel module ActiveModel
class Serializer class Serializer
class ArraySerializerTest < Minitest::Test class ArraySerializerTest < CollectionSerializerTest
def setup extend ActiveSupport::Testing::Stream
@comment = Comment.new def self.run_one_method(*)
@post = Post.new stderr = (capture(:stderr) do
@resource = build_named_collection @comment, @post super
@serializer = ArraySerializer.new(@resource, { some: :options }) end)
if stderr !~ /Calling deprecated ArraySerializer/
fail Minitest::Assertion, stderr
end
end end
def build_named_collection(*resource) def collection_serializer
resource.define_singleton_method(:name) { 'MeResource' } ArraySerializer
resource
end
def test_has_object_reader_serializer_interface
assert_equal @serializer.object, @resource
end
def test_respond_to_each
assert_respond_to @serializer, :each
end
def test_each_object_should_be_serialized_with_appropriate_serializer
serializers = @serializer.to_a
assert_kind_of CommentSerializer, serializers.first
assert_kind_of Comment, serializers.first.object
assert_kind_of PostSerializer, serializers.last
assert_kind_of Post, serializers.last.object
assert_equal serializers.last.custom_options[:some], :options
end
def test_serializer_option_not_passed_to_each_serializer
serializers = ArraySerializer.new([@post], { serializer: PostSerializer }).to_a
refute serializers.first.custom_options.key?(:serializer)
end
def test_root_default
@serializer = ArraySerializer.new([@comment, @post])
assert_equal @serializer.root, nil
end
def test_root
expected = 'custom_root'
@serializer = ArraySerializer.new([@comment, @post], root: expected)
assert_equal @serializer.root, expected
end
def test_root_with_no_serializers
expected = 'custom_root'
@serializer = ArraySerializer.new([], root: expected)
assert_equal @serializer.root, expected
end
def test_json_key
assert_equal @serializer.json_key, 'comments'
end
def test_json_key_with_resource_with_name_and_no_serializers
serializer = ArraySerializer.new(build_named_collection)
assert_equal serializer.json_key, 'me_resources'
end
def test_json_key_with_resource_with_nil_name_and_no_serializers
resource = []
resource.define_singleton_method(:name) { nil }
serializer = ArraySerializer.new(resource)
assert_equal serializer.json_key, nil
end
def test_json_key_with_resource_without_name_and_no_serializers
serializer = ArraySerializer.new([])
assert_equal serializer.json_key, nil
end
def test_json_key_with_root
serializer = ArraySerializer.new(@resource, root: 'custom_root')
assert_equal serializer.json_key, 'custom_roots'
end
def test_json_key_with_root_and_no_serializers
serializer = ArraySerializer.new(build_named_collection, root: 'custom_root')
assert_equal serializer.json_key, 'custom_roots'
end end
end end
end end

View File

@ -0,0 +1,97 @@
require 'test_helper'
module ActiveModel
class Serializer
class CollectionSerializerTest < Minitest::Test
def setup
@comment = Comment.new
@post = Post.new
@resource = build_named_collection @comment, @post
@serializer = collection_serializer.new(@resource, { some: :options })
end
def collection_serializer
CollectionSerializer
end
def build_named_collection(*resource)
resource.define_singleton_method(:name) { 'MeResource' }
resource
end
def test_has_object_reader_serializer_interface
assert_equal @serializer.object, @resource
end
def test_respond_to_each
assert_respond_to @serializer, :each
end
def test_each_object_should_be_serialized_with_appropriate_serializer
serializers = @serializer.to_a
assert_kind_of CommentSerializer, serializers.first
assert_kind_of Comment, serializers.first.object
assert_kind_of PostSerializer, serializers.last
assert_kind_of Post, serializers.last.object
assert_equal serializers.last.custom_options[:some], :options
end
def test_serializer_option_not_passed_to_each_serializer
serializers = collection_serializer.new([@post], { serializer: PostSerializer }).to_a
refute serializers.first.custom_options.key?(:serializer)
end
def test_root_default
@serializer = collection_serializer.new([@comment, @post])
assert_equal @serializer.root, nil
end
def test_root
expected = 'custom_root'
@serializer = collection_serializer.new([@comment, @post], root: expected)
assert_equal @serializer.root, expected
end
def test_root_with_no_serializers
expected = 'custom_root'
@serializer = collection_serializer.new([], root: expected)
assert_equal @serializer.root, expected
end
def test_json_key
assert_equal @serializer.json_key, 'comments'
end
def test_json_key_with_resource_with_name_and_no_serializers
serializer = collection_serializer.new(build_named_collection)
assert_equal serializer.json_key, 'me_resources'
end
def test_json_key_with_resource_with_nil_name_and_no_serializers
resource = []
resource.define_singleton_method(:name) { nil }
serializer = collection_serializer.new(resource)
assert_equal serializer.json_key, nil
end
def test_json_key_with_resource_without_name_and_no_serializers
serializer = collection_serializer.new([])
assert_equal serializer.json_key, nil
end
def test_json_key_with_root
serializer = collection_serializer.new(@resource, root: 'custom_root')
assert_equal serializer.json_key, 'custom_roots'
end
def test_json_key_with_root_and_no_serializers
serializer = collection_serializer.new(build_named_collection, root: 'custom_root')
assert_equal serializer.json_key, 'custom_roots'
end
end
end
end

View File

@ -154,7 +154,7 @@ BlogSerializer = Class.new(ActiveModel::Serializer) do
has_many :articles has_many :articles
end end
PaginatedSerializer = Class.new(ActiveModel::Serializer::ArraySerializer) do PaginatedSerializer = Class.new(ActiveModel::Serializer::CollectionSerializer) do
def json_key def json_key
'paginated' 'paginated'
end end

View File

@ -33,13 +33,13 @@ module ActiveModel
case key case key
when :posts when :posts
assert_equal({}, options) assert_equal({}, options)
assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) assert_kind_of(ActiveModel::Serializer.config.collection_serializer, serializer)
when :bio when :bio
assert_equal({}, options) assert_equal({}, options)
assert_nil serializer assert_nil serializer
when :roles when :roles
assert_equal({}, options) assert_equal({}, options)
assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) assert_kind_of(ActiveModel::Serializer.config.collection_serializer, serializer)
else else
flunk "Unknown association: #{key}" flunk "Unknown association: #{key}"
end end

View File

@ -3,8 +3,25 @@ require 'test_helper'
module ActiveModel module ActiveModel
class Serializer class Serializer
class ConfigurationTest < Minitest::Test class ConfigurationTest < Minitest::Test
def test_collection_serializer
assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModel::Serializer.config.collection_serializer
end
def test_array_serializer def test_array_serializer
assert_equal ActiveModel::Serializer::ArraySerializer, ActiveModel::Serializer.config.array_serializer assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModel::Serializer.config.array_serializer
end
def test_setting_array_serializer_sets_collection_serializer
config = ActiveModel::Serializer.config
old_config = config.dup
begin
assert_equal ActiveModel::Serializer::CollectionSerializer, config.collection_serializer
config.array_serializer = :foo
assert_equal config.array_serializer, :foo
assert_equal config.collection_serializer, :foo
ensure
ActiveModel::Serializer.config.replace(old_config)
end
end end
def test_default_adapter def test_default_adapter

View File

@ -3,26 +3,26 @@ require 'test_helper'
module ActiveModel module ActiveModel
class Serializer class Serializer
class SerializerForTest < Minitest::Test class SerializerForTest < Minitest::Test
class ArraySerializerTest < Minitest::Test class CollectionSerializerTest < Minitest::Test
def setup def setup
@array = [1, 2, 3] @array = [1, 2, 3]
@previous_array_serializer = ActiveModel::Serializer.config.array_serializer @previous_collection_serializer = ActiveModel::Serializer.config.collection_serializer
end end
def teardown def teardown
ActiveModel::Serializer.config.array_serializer = @previous_array_serializer ActiveModel::Serializer.config.collection_serializer = @previous_collection_serializer
end end
def test_serializer_for_array def test_serializer_for_array
serializer = ActiveModel::Serializer.serializer_for(@array) serializer = ActiveModel::Serializer.serializer_for(@array)
assert_equal ActiveModel::Serializer.config.array_serializer, serializer assert_equal ActiveModel::Serializer.config.collection_serializer, serializer
end end
def test_overwritten_serializer_for_array def test_overwritten_serializer_for_array
new_array_serializer = Class.new new_collection_serializer = Class.new
ActiveModel::Serializer.config.array_serializer = new_array_serializer ActiveModel::Serializer.config.collection_serializer = new_collection_serializer
serializer = ActiveModel::Serializer.serializer_for(@array) serializer = ActiveModel::Serializer.serializer_for(@array)
assert_equal new_array_serializer, serializer assert_equal new_collection_serializer, serializer
end end
end end