From 557b56a50ede576635970529ea9291b8e2f1830c Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Wed, 15 Oct 2014 17:35:50 -0500 Subject: [PATCH] Refactor adapters to implement support for array serialization --- lib/active_model/serializer/adapter.rb | 8 +--- lib/active_model/serializer/adapter/json.rb | 24 +++++++---- .../serializer/adapter/json_api.rb | 41 +++++++++++-------- .../serializer/array_serializer.rb | 8 ++++ test/action_controller/serialization_test.rb | 28 ++++++++++++- test/adapter/json/collection_test.rb | 28 +++++++++++++ test/adapter/json_api/belongs_to_test.rb | 2 +- test/adapter/json_api/collection_test.rb | 28 +++++++++++++ test/adapter/json_api/has_many_embed_ids.rb | 2 +- test/adapter/json_api/has_many_test.rb | 2 +- test/array_serializer_test.rb | 4 +- 11 files changed, 137 insertions(+), 38 deletions(-) create mode 100644 test/adapter/json/collection_test.rb create mode 100644 test/adapter/json_api/collection_test.rb diff --git a/lib/active_model/serializer/adapter.rb b/lib/active_model/serializer/adapter.rb index 8eacc39a3..f0a82573f 100644 --- a/lib/active_model/serializer/adapter.rb +++ b/lib/active_model/serializer/adapter.rb @@ -17,13 +17,7 @@ def serializable_hash(options = {}) end def to_json(options = {}) - result = serializable_hash(options) - - if root = options.fetch(:root, serializer.json_key) - result = { root => result } - end - - result.to_json + serializable_hash(options).to_json end end end diff --git a/lib/active_model/serializer/adapter/json.rb b/lib/active_model/serializer/adapter/json.rb index 82bc319e7..3b5681942 100644 --- a/lib/active_model/serializer/adapter/json.rb +++ b/lib/active_model/serializer/adapter/json.rb @@ -3,18 +3,26 @@ class Serializer class Adapter class Json < Adapter def serializable_hash(options = {}) - @hash = serializer.attributes(options) + if serializer.respond_to?(:each) + @result = serializer.map{|s| self.class.new(s).serializable_hash } + else + @result = serializer.attributes(options) - serializer.each_association do |name, association, options| - if association.respond_to?(:each) - array_serializer = association - @hash[name] = array_serializer.map { |item| item.attributes(options) } - else - @hash[name] = association.attributes(options) + serializer.each_association do |name, association, opts| + if association.respond_to?(:each) + array_serializer = association + @result[name] = array_serializer.map { |item| item.attributes(opts) } + else + @result[name] = association.attributes(options) + end end end - @hash + if root = options.fetch(:root, serializer.json_key) + @result = { root => @result } + end + + @result end end end diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 76340613b..2d4977ea6 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -4,22 +4,29 @@ class Adapter class JsonApi < Adapter def initialize(serializer, options = {}) super - serializer.root ||= true + serializer.root = true end - def serializable_hash(opts = {}) - @hash = serializer.attributes - - serializer.each_association do |name, association, options| - @hash[:links] ||= {} - unless options[:embed] == :ids - @hash[:linked] ||= {} - end - - if association.respond_to?(:each) - add_links(name, association, options) - else - add_link(name, association, options) + def serializable_hash(options = {}) + @root = (options[:root] || serializer.json_key).to_s.pluralize.to_sym + @hash = {} + + if serializer.respond_to?(:each) + @hash[@root] = serializer.map{|s| self.class.new(s).serializable_hash[@root] } + else + @hash[@root] = serializer.attributes + + serializer.each_association do |name, association, opts| + @hash[@root][:links] ||= {} + unless options[:embed] == :ids + @hash[:linked] ||= {} + end + + if association.respond_to?(:each) + add_links(name, association, opts) + else + add_link(name, association, opts) + end end end @@ -27,8 +34,8 @@ def serializable_hash(opts = {}) end def add_links(name, serializers, options) - @hash[:links][name] ||= [] - @hash[:links][name] += serializers.map(&:id) + @hash[@root][:links][name] ||= [] + @hash[@root][:links][name] += serializers.map(&:id) unless options[:embed] == :ids @hash[:linked][name] ||= [] @@ -37,7 +44,7 @@ def add_links(name, serializers, options) end def add_link(name, serializer, options) - @hash[:links][name] = serializer.id + @hash[@root][:links][name] = serializer.id unless options[:embed] == :ids plural_name = name.to_s.pluralize.to_sym diff --git a/lib/active_model/serializer/array_serializer.rb b/lib/active_model/serializer/array_serializer.rb index 697c44542..b52a4f51d 100644 --- a/lib/active_model/serializer/array_serializer.rb +++ b/lib/active_model/serializer/array_serializer.rb @@ -10,6 +10,14 @@ def initialize(objects, options = {}) serializer_class.new(object) end end + + def json_key + @objects.first.json_key if @objects.first + end + + def root=(root) + @objects.first.root = root if @objects.first + end end end end diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index dcfd9ea54..4103e2733 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -23,6 +23,14 @@ def render_using_default_adapter_root ensure ActiveModel::Serializer.config.adapter = old_adapter end + + def render_array_using_implicit_serializer + array = [ + Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), + Profile.new({ name: 'Name 2', description: 'Description 2', comments: 'Comments 2' }) + ] + render json: array + end end tests MyController @@ -46,7 +54,25 @@ def test_render_using_default_root get :render_using_default_adapter_root assert_equal 'application/json', @response.content_type - assert_equal '{"profile":{"name":"Name 1","description":"Description 1"}}', @response.body + assert_equal '{"profiles":{"name":"Name 1","description":"Description 1"}}', @response.body + end + + def test_render_array_using_implicit_serializer + get :render_array_using_implicit_serializer + assert_equal 'application/json', @response.content_type + + expected = [ + { + name: 'Name 1', + description: 'Description 1', + }, + { + name: 'Name 2', + description: 'Description 2', + } + ] + + assert_equal expected.to_json, @response.body end end end diff --git a/test/adapter/json/collection_test.rb b/test/adapter/json/collection_test.rb new file mode 100644 index 000000000..4d02f65a9 --- /dev/null +++ b/test/adapter/json/collection_test.rb @@ -0,0 +1,28 @@ +require 'test_helper' + +module ActiveModel + class Serializer + class Adapter + class Json + class Collection < Minitest::Test + def setup + @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') + @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') + @first_post.comments = [] + @second_post.comments = [] + + @serializer = ArraySerializer.new([@first_post, @second_post]) + @adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) + end + + def test_include_multiple_posts + assert_equal([ + {title: "Hello!!", body: "Hello, world!!", id: 1, comments: []}, + {title: "New Post", body: "Body", id: 2, comments: []} + ], @adapter.serializable_hash) + end + end + end + end + end +end diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb index 151fd8cce..5c1df9f98 100644 --- a/test/adapter/json_api/belongs_to_test.rb +++ b/test/adapter/json_api/belongs_to_test.rb @@ -16,7 +16,7 @@ def setup end def test_includes_post_id - assert_equal(42, @adapter.serializable_hash[:links][:post]) + assert_equal(42, @adapter.serializable_hash[:comments][:links][:post]) end def test_includes_linked_post diff --git a/test/adapter/json_api/collection_test.rb b/test/adapter/json_api/collection_test.rb new file mode 100644 index 000000000..ee4626711 --- /dev/null +++ b/test/adapter/json_api/collection_test.rb @@ -0,0 +1,28 @@ +require 'test_helper' + +module ActiveModel + class Serializer + class Adapter + class JsonApi + class Collection < Minitest::Test + def setup + @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') + @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') + @first_post.comments = [] + @second_post.comments = [] + + @serializer = ArraySerializer.new([@first_post, @second_post]) + @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) + end + + def test_include_multiple_posts + assert_equal([ + {title: "Hello!!", body: "Hello, world!!", id: 1, links: {comments: []}}, + {title: "New Post", body: "Body", id: 2, links: {comments: []}} + ], @adapter.serializable_hash[:posts]) + end + end + end + end + end +end diff --git a/test/adapter/json_api/has_many_embed_ids.rb b/test/adapter/json_api/has_many_embed_ids.rb index d3e42154e..f7755b725 100644 --- a/test/adapter/json_api/has_many_embed_ids.rb +++ b/test/adapter/json_api/has_many_embed_ids.rb @@ -18,7 +18,7 @@ def setup end def test_includes_comment_ids - assert_equal([1, 2], @adapter.serializable_hash[:links][:posts]) + assert_equal([1, 2], @adapter.serializable_hash[:authors][:links][:posts]) end def test_no_includes_linked_comments diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb index 435079d7c..9fad0f8b5 100644 --- a/test/adapter/json_api/has_many_test.rb +++ b/test/adapter/json_api/has_many_test.rb @@ -18,7 +18,7 @@ def setup end def test_includes_comment_ids - assert_equal([1, 2], @adapter.serializable_hash[:links][:comments]) + assert_equal([1, 2], @adapter.serializable_hash[:posts][:links][:comments]) end def test_includes_linked_comments diff --git a/test/array_serializer_test.rb b/test/array_serializer_test.rb index 66840a4ac..4c9b63e16 100644 --- a/test/array_serializer_test.rb +++ b/test/array_serializer_test.rb @@ -5,7 +5,7 @@ class Serializer class ArraySerializerTest < Minitest::Test def setup @comment = Comment.new - @post= Post.new + @post = Post.new @serializer = ArraySerializer.new([@comment, @post]) end @@ -13,7 +13,7 @@ def test_respond_to_each assert_respond_to @serializer, :each end - def test_each_object_should_be_serializer_with_appropriate_serializer + def test_each_object_should_be_serialized_with_appropriate_serializer serializers = @serializer.to_a assert_kind_of CommentSerializer, serializers.first