From c84430cdadeefe7382668f596c230c0599ba6b3e Mon Sep 17 00:00:00 2001 From: Gary Gordon Date: Mon, 27 Oct 2014 10:44:21 -0400 Subject: [PATCH 01/10] Support serializer and each_serializer in render json --- README.md | 16 ++++++ lib/action_controller/serialization.rb | 21 +++++--- .../serializer/array_serializer.rb | 5 +- .../explicit_serializer_test.rb | 53 +++++++++++++++++++ test/fixtures/poro.rb | 12 +++++ 5 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 test/action_controller/explicit_serializer_test.rb diff --git a/README.md b/README.md index 4121dfe2..bb976b28 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,22 @@ member when the resource names are included in the `include` option. render @posts, include: 'authors,comments' ``` +### Specify a serializer + +If you wish to use a serializer other than the default, you can explicitly pass it to the renderer. + +#### 1. For a resource: + +```ruby + render json: @post, serializer: PostPreviewSerializer +``` + +#### 2. For an array resource: + +```ruby + render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPreviewSerializer +``` + ## Installation Add this line to your application's Gemfile: diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index 2b460cb4..150b9aa2 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -8,16 +8,25 @@ module ActionController ADAPTER_OPTION_KEYS = [:include, :root] + def get_serializer(resource, options) + @_serializer ||= if (serializer = options.delete :serializer) + options[:serializer] = options.delete :each_serializer + serializer + else + ActiveModel::Serializer.serializer_for(resource) + end + end + [:_render_option_json, :_render_with_renderer_json].each do |renderer_method| define_method renderer_method do |resource, options| - serializer = ActiveModel::Serializer.serializer_for(resource) - if serializer - adapter_opts, serializer_opts = - options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k } + adapter_opts, serializer_opts = + options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] } + + if (serializer = get_serializer(resource, serializer_opts)) # omg hax - object = serializer.new(resource, Hash[serializer_opts]) - adapter = ActiveModel::Serializer.adapter.new(object, Hash[adapter_opts]) + object = serializer.new(resource, serializer_opts) + adapter = ActiveModel::Serializer.adapter.new(object, adapter_opts) super(adapter, options) else super(resource, options) diff --git a/lib/active_model/serializer/array_serializer.rb b/lib/active_model/serializer/array_serializer.rb index b52a4f51..83693c97 100644 --- a/lib/active_model/serializer/array_serializer.rb +++ b/lib/active_model/serializer/array_serializer.rb @@ -6,7 +6,10 @@ module ActiveModel def initialize(objects, options = {}) @objects = objects.map do |object| - serializer_class = ActiveModel::Serializer.serializer_for(object) + serializer_class = options.fetch( + :serializer, + ActiveModel::Serializer.serializer_for(object) + ) serializer_class.new(object) end end diff --git a/test/action_controller/explicit_serializer_test.rb b/test/action_controller/explicit_serializer_test.rb new file mode 100644 index 00000000..e4bddd04 --- /dev/null +++ b/test/action_controller/explicit_serializer_test.rb @@ -0,0 +1,53 @@ +require 'test_helper' + +module ActionController + module Serialization + class ExplicitSerializerTest < ActionController::TestCase + class MyController < ActionController::Base + def render_using_explicit_serializer + @profile = Profile.new(name: 'Name 1', + description: 'Description 1', + comments: 'Comments 1') + render json: @profile, serializer: ProfilePreviewSerializer + end + + def render_array_using_explicit_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, + serializer: PaginatedSerializer, + each_serializer: ProfilePreviewSerializer + end + end + + tests MyController + + def test_render_using_explicit_serializer + get :render_using_explicit_serializer + + assert_equal 'application/json', @response.content_type + assert_equal '{"name":"Name 1"}', @response.body + end + + def test_render_array_using_explicit_serializer + get :render_array_using_explicit_serializer + assert_equal 'application/json', @response.content_type + + expected = { + 'paginated' => [ + { 'name' => 'Name 1' }, + { 'name' => 'Name 2' } + ] + } + + assert_equal expected.to_json, @response.body + end + end + end +end diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index 7d8d57b4..9b52c870 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -35,6 +35,12 @@ class ProfileSerializer < ActiveModel::Serializer urls :posts, :comments end +class ProfilePreviewSerializer < ActiveModel::Serializer + attributes :name + + urls :posts, :comments +end + Post = Class.new(Model) Comment = Class.new(Model) Author = Class.new(Model) @@ -67,3 +73,9 @@ BlogSerializer = Class.new(ActiveModel::Serializer) do belongs_to :writer has_many :articles end + +PaginatedSerializer = Class.new(ActiveModel::Serializer::ArraySerializer) do + def json_key + 'paginated' + end +end From 58b6c4a6b7e04764db55dcbffb92b66f8642463f Mon Sep 17 00:00:00 2001 From: "Mike A. Owens" Date: Fri, 7 Nov 2014 09:39:36 -0500 Subject: [PATCH 02/10] Allow for the implicit use of ArraySerializer when :each_serializer is specified. --- README.md | 7 +++++- lib/action_controller/serialization.rb | 12 +++++---- .../explicit_serializer_test.rb | 25 +++++++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bb976b28..1d9db323 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,12 @@ If you wish to use a serializer other than the default, you can explicitly pass #### 2. For an array resource: ```ruby - render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPreviewSerializer +# Use the default `ArraySerializer`, which will use `each_serializer` to +# serialize each element +render json: @posts, each_serializer: PostPreviewSerializer + +# Or, you can explicitly provide the collection serializer as well +render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPreviewSerializer ``` ## Installation diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index 150b9aa2..d157e2f9 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -9,12 +9,14 @@ module ActionController ADAPTER_OPTION_KEYS = [:include, :root] def get_serializer(resource, options) - @_serializer ||= if (serializer = options.delete :serializer) - options[:serializer] = options.delete :each_serializer - serializer - else - ActiveModel::Serializer.serializer_for(resource) + @_serializer ||= options.delete(:serializer) + @_serializer ||= ActiveModel::Serializer.serializer_for(resource) + + if options.key?(:each_serializer) + options[:serializer] = options.delete(:each_serializer) end + + @_serializer end [:_render_option_json, :_render_with_renderer_json].each do |renderer_method| diff --git a/test/action_controller/explicit_serializer_test.rb b/test/action_controller/explicit_serializer_test.rb index e4bddd04..cb8b2bd7 100644 --- a/test/action_controller/explicit_serializer_test.rb +++ b/test/action_controller/explicit_serializer_test.rb @@ -24,6 +24,19 @@ module ActionController serializer: PaginatedSerializer, each_serializer: ProfilePreviewSerializer 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, + each_serializer: ProfilePreviewSerializer + end end tests MyController @@ -48,6 +61,18 @@ module ActionController assert_equal expected.to_json, @response.body end + + def test_render_array_using_explicit_serializer + get :render_array_using_implicit_serializer + assert_equal 'application/json', @response.content_type + + expected = [ + { 'name' => 'Name 1' }, + { 'name' => 'Name 2' } + ] + assert_equal expected.to_json, @response.body + end + end end end From d97b2f5005ee7edc6db1890523b82e8e02ee52e8 Mon Sep 17 00:00:00 2001 From: Gary Gordon Date: Fri, 7 Nov 2014 09:28:10 -0500 Subject: [PATCH 03/10] Fix infinite recursion The method for testing whether to include an association was causing an infinite loop when two models referenced each other. --- .../serializer/adapter/json_api.rb | 24 +++++++++++++------ .../action_controller/json_api_linked_test.rb | 2 ++ test/adapter/json_api/belongs_to_test.rb | 1 + test/adapter/json_api/collection_test.rb | 1 + .../json_api/has_many_embed_ids_test.rb | 1 + test/adapter/json_api/has_many_test.rb | 1 + test/adapter/json_api/linked_test.rb | 10 ++++++-- test/fixtures/poro.rb | 8 +++++++ test/serializers/associations_test.rb | 19 +++++++++++---- 9 files changed, 54 insertions(+), 13 deletions(-) diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 693ec316..520e9d6a 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -83,11 +83,9 @@ module ActiveModel @top[:linked][plural_name].push attrs unless @top[:linked][plural_name].include? attrs end - unless serializer.respond_to?(:each) - serializer.each_association do |name, association, opts| - add_linked(name, association, resource) if association - end - end + serializer.each_association do |name, association, opts| + add_linked(name, association, resource_path) if association + end if include_nested_assoc? resource_path end private @@ -98,8 +96,20 @@ module ActiveModel attributes end - def include_assoc? assoc - @options[:include] && @options[:include].split(',').include?(assoc.to_s) + def include_assoc?(assoc) + return false unless @options[:include] + check_assoc("#{assoc}$") + end + + def include_nested_assoc?(assoc) + return false unless @options[:include] + check_assoc("#{assoc}.") + end + + def check_assoc(assoc) + @options[:include].split(',').any? do |s| + s.match(/^#{assoc.gsub('.', '\.')}/) + end end end end diff --git a/test/action_controller/json_api_linked_test.rb b/test/action_controller/json_api_linked_test.rb index f02e90e2..0770c78d 100644 --- a/test/action_controller/json_api_linked_test.rb +++ b/test/action_controller/json_api_linked_test.rb @@ -7,8 +7,10 @@ module ActionController def setup_post @author = Author.new(id: 1, name: 'Steve K.') @author.posts = [] + @author.bio = nil @author2 = Author.new(id: 2, name: 'Anonymous') @author2.posts = [] + @author2.bio = nil @post = Post.new(id: 1, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb index e25a71d8..f18226af 100644 --- a/test/adapter/json_api/belongs_to_test.rb +++ b/test/adapter/json_api/belongs_to_test.rb @@ -7,6 +7,7 @@ module ActiveModel class BelongsToTest < Minitest::Test def setup @author = Author.new(id: 1, name: 'Steve K.') + @author.bio = nil @post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') diff --git a/test/adapter/json_api/collection_test.rb b/test/adapter/json_api/collection_test.rb index 922103ea..a48e66b0 100644 --- a/test/adapter/json_api/collection_test.rb +++ b/test/adapter/json_api/collection_test.rb @@ -7,6 +7,7 @@ module ActiveModel class CollectionTest < Minitest::Test def setup @author = Author.new(id: 1, name: 'Steve K.') + @author.bio = nil @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 = [] diff --git a/test/adapter/json_api/has_many_embed_ids_test.rb b/test/adapter/json_api/has_many_embed_ids_test.rb index 4690e3c6..8b3c0218 100644 --- a/test/adapter/json_api/has_many_embed_ids_test.rb +++ b/test/adapter/json_api/has_many_embed_ids_test.rb @@ -7,6 +7,7 @@ module ActiveModel class HasManyEmbedIdsTest < Minitest::Test def setup @author = Author.new(name: 'Steve K.') + @author.bio = nil @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @author.posts = [@first_post, @second_post] diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb index 975c5b63..6ded298a 100644 --- a/test/adapter/json_api/has_many_test.rb +++ b/test/adapter/json_api/has_many_test.rb @@ -8,6 +8,7 @@ module ActiveModel def setup @author = Author.new(id: 1, name: 'Steve K.') @author.posts = [] + @author.bio = nil @post = Post.new(id: 1, title: 'New Post', body: 'Body') @post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') diff --git a/test/adapter/json_api/linked_test.rb b/test/adapter/json_api/linked_test.rb index 160d3daa..2808e969 100644 --- a/test/adapter/json_api/linked_test.rb +++ b/test/adapter/json_api/linked_test.rb @@ -7,6 +7,7 @@ module ActiveModel class LinkedTest < Minitest::Test def setup @author = Author.new(id: 1, name: 'Steve K.') + @bio = Bio.new(id: 1, content: 'AMS Contributor') @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 = [] @@ -14,9 +15,11 @@ module ActiveModel @first_post.author = @author @second_post.author = @author @author.posts = [@first_post, @second_post] + @author.bio = @bio + @bio.author = @author @serializer = ArraySerializer.new([@first_post, @second_post]) - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'author,comments') + @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'author,author.bio,comments') end def test_include_multiple_posts_and_linked @@ -31,7 +34,10 @@ module ActiveModel { title: "Hello!!", body: "Hello, world!!", id: "1", links: { comments: ['1', '2'], author: "1" } }, { title: "New Post", body: "Body", id: "2", links: { comments: [], :author => "1" } } ], @adapter.serializable_hash[:posts]) - assert_equal({ :comments => [{ :id => "1", :body => "ZOMG A COMMENT" }, { :id => "2", :body => "ZOMG ANOTHER COMMENT" }], :authors => [{ :id => "1", :name => "Steve K." }] }, @adapter.serializable_hash[:linked]) + assert_equal({ :comments => [{ :id => "1", :body => "ZOMG A COMMENT" }, + { :id => "2", :body => "ZOMG ANOTHER COMMENT" }], + :authors => [{ :id => "1", :name => "Steve K." }], + :bios=>[{:id=>"1", :content=>"AMS Contributor"}] }, @adapter.serializable_hash[:linked]) end end end diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index 7d8d57b4..07ce073e 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -38,6 +38,7 @@ end Post = Class.new(Model) Comment = Class.new(Model) Author = Class.new(Model) +Bio = Class.new(Model) Blog = Class.new(Model) PostSerializer = Class.new(ActiveModel::Serializer) do @@ -59,6 +60,13 @@ AuthorSerializer = Class.new(ActiveModel::Serializer) do attributes :id, :name has_many :posts, embed: :ids + belongs_to :bio +end + +BioSerializer = Class.new(ActiveModel::Serializer) do + attributes :id, :content + + belongs_to :author end BlogSerializer = Class.new(ActiveModel::Serializer) do diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb index 99162acc..65169496 100644 --- a/test/serializers/associations_test.rb +++ b/test/serializers/associations_test.rb @@ -26,6 +26,7 @@ module ActiveModel def setup @author = Author.new(name: 'Steve K.') + @author.bio = nil @post = Post.new({ title: 'New Post', body: 'Body' }) @comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' }) @post.comments = [@comment] @@ -39,11 +40,21 @@ module ActiveModel end def test_has_many - assert_equal({posts: {type: :has_many, options: {embed: :ids}}}, @author_serializer.class._associations) + assert_equal( + { posts: { type: :has_many, options: { embed: :ids } }, + bio: { type: :belongs_to, options: {} } }, + @author_serializer.class._associations + ) @author_serializer.each_association do |name, serializer, options| - assert_equal(:posts, name) - assert_equal({embed: :ids}, options) - assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) + if name == :posts + assert_equal({embed: :ids}, options) + assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) + elsif name == :bio + assert_equal({}, options) + assert_nil serializer + else + flunk "Unknown association: #{name}" + end end end From 971f501e554a5b8ccfb1b082083ec02fe642e646 Mon Sep 17 00:00:00 2001 From: Alexandre de Oliveira Date: Tue, 11 Nov 2014 14:35:00 -0200 Subject: [PATCH 04/10] Bugfix: include nested has_many association Currently, doing `include: author.bio` would work correctly, but not for has_many associations such as `include: author.roles`. This fixes it. The problem was basically that we were not handling arrays for has_many linked, as happens for ArraySerializers. --- .../serializer/adapter/json_api.rb | 23 ++++++++++++---- .../action_controller/json_api_linked_test.rb | 26 +++++++++++++++++++ .../json_api/has_many_embed_ids_test.rb | 1 + test/adapter/json_api/linked_test.rb | 1 + test/fixtures/poro.rb | 8 ++++++ test/serializers/associations_test.rb | 5 ++++ 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 520e9d6a..e1d2ea01 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -77,10 +77,13 @@ module ActiveModel resource_path = [parent, resource].compact.join('.') if include_assoc? resource_path plural_name = resource.to_s.pluralize.to_sym - attrs = attributes_for_serializer(serializer, @options) + attrs = [attributes_for_serializer(serializer, @options)].flatten @top[:linked] ||= {} @top[:linked][plural_name] ||= [] - @top[:linked][plural_name].push attrs unless @top[:linked][plural_name].include? attrs + + attrs.each do |attrs| + @top[:linked][plural_name].push(attrs) unless @top[:linked][plural_name].include?(attrs) + end end serializer.each_association do |name, association, opts| @@ -91,9 +94,19 @@ module ActiveModel private def attributes_for_serializer(serializer, options) - attributes = serializer.attributes(options) - attributes[:id] = attributes[:id].to_s if attributes[:id] - attributes + if serializer.respond_to?(:each) + result = [] + serializer.each do |object| + attributes = object.attributes(options) + attributes[:id] = attributes[:id].to_s if attributes[:id] + result << attributes + end + else + result = serializer.attributes(options) + result[:id] = result[:id].to_s if result[:id] + end + + result end def include_assoc?(assoc) diff --git a/test/action_controller/json_api_linked_test.rb b/test/action_controller/json_api_linked_test.rb index 0770c78d..a5b7c8db 100644 --- a/test/action_controller/json_api_linked_test.rb +++ b/test/action_controller/json_api_linked_test.rb @@ -5,12 +5,15 @@ module ActionController class JsonApiLinkedTest < ActionController::TestCase class MyController < ActionController::Base def setup_post + @role1 = Role.new(id: 1, name: 'admin') @author = Author.new(id: 1, name: 'Steve K.') @author.posts = [] @author.bio = nil + @author.roles = [@role1] @author2 = Author.new(id: 2, name: 'Anonymous') @author2.posts = [] @author2.bio = nil + @author2.roles = [] @post = Post.new(id: 1, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @@ -51,6 +54,13 @@ module ActionController end end + def render_resource_with_nested_has_many_include + with_json_api_adapter do + setup_post + render json: @post, include: 'author,author.roles' + end + end + def render_collection_without_include with_json_api_adapter do setup_post @@ -82,6 +92,22 @@ module ActionController assert_equal 'Steve K.', response['linked']['authors'].first['name'] end + def test_render_resource_with_nested_has_many_include + get :render_resource_with_nested_has_many_include + response = JSON.parse(@response.body) + expected_linked = { + "authors" => [{ + "id" => "1", + "name" => "Steve K." + }], + "roles"=>[{ + "id" => "1", + "name" => "admin" + }] + } + assert_equal expected_linked, response['linked'] + end + def test_render_resource_with_nested_include get :render_resource_with_nested_include response = JSON.parse(@response.body) diff --git a/test/adapter/json_api/has_many_embed_ids_test.rb b/test/adapter/json_api/has_many_embed_ids_test.rb index 8b3c0218..a6072aa4 100644 --- a/test/adapter/json_api/has_many_embed_ids_test.rb +++ b/test/adapter/json_api/has_many_embed_ids_test.rb @@ -8,6 +8,7 @@ module ActiveModel def setup @author = Author.new(name: 'Steve K.') @author.bio = nil + @author.roles = nil @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @author.posts = [@first_post, @second_post] diff --git a/test/adapter/json_api/linked_test.rb b/test/adapter/json_api/linked_test.rb index 2808e969..a7982247 100644 --- a/test/adapter/json_api/linked_test.rb +++ b/test/adapter/json_api/linked_test.rb @@ -16,6 +16,7 @@ module ActiveModel @second_post.author = @author @author.posts = [@first_post, @second_post] @author.bio = @bio + @author.roles = [] @bio.author = @author @serializer = ArraySerializer.new([@first_post, @second_post]) diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index 07ce073e..ae319a03 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -40,6 +40,7 @@ Comment = Class.new(Model) Author = Class.new(Model) Bio = Class.new(Model) Blog = Class.new(Model) +Role = Class.new(Model) PostSerializer = Class.new(ActiveModel::Serializer) do attributes :title, :body, :id @@ -60,9 +61,16 @@ AuthorSerializer = Class.new(ActiveModel::Serializer) do attributes :id, :name has_many :posts, embed: :ids + has_many :roles, embed: :ids belongs_to :bio end +RoleSerializer = Class.new(ActiveModel::Serializer) do + attributes :id, :name + + belongs_to :author +end + BioSerializer = Class.new(ActiveModel::Serializer) do attributes :id, :content diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb index 65169496..b2278b45 100644 --- a/test/serializers/associations_test.rb +++ b/test/serializers/associations_test.rb @@ -27,6 +27,7 @@ module ActiveModel def setup @author = Author.new(name: 'Steve K.') @author.bio = nil + @author.roles = [] @post = Post.new({ title: 'New Post', body: 'Body' }) @comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' }) @post.comments = [@comment] @@ -42,6 +43,7 @@ module ActiveModel def test_has_many assert_equal( { posts: { type: :has_many, options: { embed: :ids } }, + roles: { type: :has_many, options: { embed: :ids } }, bio: { type: :belongs_to, options: {} } }, @author_serializer.class._associations ) @@ -52,6 +54,9 @@ module ActiveModel elsif name == :bio assert_equal({}, options) assert_nil serializer + elsif name == :roles + assert_equal({embed: :ids}, options) + assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) else flunk "Unknown association: #{name}" end From 91b3fba50943440cc05ee732962f5b68ace6c75c Mon Sep 17 00:00:00 2001 From: Alexandre de Oliveira Date: Wed, 12 Nov 2014 15:12:29 -0200 Subject: [PATCH 05/10] Includes links inside of linked resources According to http://jsonapi.org/format/#document-structure-resource-objects, > Resource objects have the same internal structure, regardless of > whether they represent primary or linked resources. And then http://jsonapi.org/format/#document-structure-resource-object-attributes, > There are four reserved keys in resource objects: > > "id" > "type" > "href" > "links" This commits includes `links` inside of linked resources. --- .../serializer/adapter/json_api.rb | 102 ++++++++++-------- .../action_controller/json_api_linked_test.rb | 23 +++- test/adapter/json_api/belongs_to_test.rb | 11 +- test/adapter/json_api/has_many_test.rb | 20 +++- test/adapter/json_api/linked_test.rb | 40 ++++++- 5 files changed, 141 insertions(+), 55 deletions(-) diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index e1d2ea01..567630f8 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -19,69 +19,57 @@ module ActiveModel else @hash[@root] = attributes_for_serializer(serializer, @options) - serializer.each_association do |name, association, opts| - @hash[@root][:links] ||= {} - - if association.respond_to?(:each) - add_links(name, association, opts) - else - add_link(name, association, opts) - end - end + add_resource_links(@hash[@root], serializer) end @hash end - def add_links(name, serializers, options) - if serializers.first - type = serializers.first.object.class.to_s.underscore.pluralize - end + private + + def add_links(resource, name, serializers) + type = serialized_object_type(serializers) + resource[:links] ||= {} + if name.to_s == type || !type - @hash[@root][:links][name] ||= [] - @hash[@root][:links][name] += serializers.map{|serializer| serializer.id.to_s } + resource[:links][name] ||= [] + resource[:links][name] += serializers.map{|serializer| serializer.id.to_s } else - @hash[@root][:links][name] ||= {} - @hash[@root][:links][name][:type] = type - @hash[@root][:links][name][:ids] ||= [] - @hash[@root][:links][name][:ids] += serializers.map{|serializer| serializer.id.to_s } - end - - unless serializers.none? || @options[:embed] == :ids - serializers.each do |serializer| - add_linked(name, serializer) - end + resource[:links][name] ||= {} + resource[:links][name][:type] = type + resource[:links][name][:ids] ||= [] + resource[:links][name][:ids] += serializers.map{|serializer| serializer.id.to_s } end end - def add_link(name, serializer, options) + def add_link(resource, name, serializer) + resource[:links] ||= {} + resource[:links][name] = nil + if serializer - type = serializer.object.class.to_s.underscore + type = serialized_object_type(serializer) if name.to_s == type || !type - @hash[@root][:links][name] = serializer.id.to_s + resource[:links][name] = serializer.id.to_s else - @hash[@root][:links][name] ||= {} - @hash[@root][:links][name][:type] = type - @hash[@root][:links][name][:id] = serializer.id.to_s + resource[:links][name] ||= {} + resource[:links][name][:type] = type + resource[:links][name][:id] = serializer.id.to_s end - - unless @options[:embed] == :ids - add_linked(name, serializer) - end - else - @hash[@root][:links][name] = nil end end - def add_linked(resource, serializer, parent = nil) - resource_path = [parent, resource].compact.join('.') - if include_assoc? resource_path - plural_name = resource.to_s.pluralize.to_sym + def add_linked(resource_name, serializer, parent = nil) + resource_path = [parent, resource_name].compact.join('.') + + if include_assoc?(resource_path) + plural_name = resource_name.to_s.pluralize.to_sym attrs = [attributes_for_serializer(serializer, @options)].flatten @top[:linked] ||= {} @top[:linked][plural_name] ||= [] attrs.each do |attrs| + add_resource_links(attrs, serializer, add_linked: false) + @top[:linked][plural_name].push(attrs) unless @top[:linked][plural_name].include?(attrs) end end @@ -91,8 +79,6 @@ module ActiveModel end if include_nested_assoc? resource_path end - private - def attributes_for_serializer(serializer, options) if serializer.respond_to?(:each) result = [] @@ -124,6 +110,36 @@ module ActiveModel s.match(/^#{assoc.gsub('.', '\.')}/) end end + + def serialized_object_type(serializer) + return false unless Array(serializer).first + type_name = Array(serializer).first.object.class.to_s.underscore + if serializer.respond_to?(:first) + type_name.pluralize + else + type_name + end + end + + def add_resource_links(attrs, serializer, options = {}) + options[:add_linked] = options.fetch(:add_linked, true) + + Array(serializer).first.each_association do |name, association, opts| + attrs[:links] ||= {} + + if association.respond_to?(:each) + add_links(attrs, name, association) + else + add_link(attrs, name, association) + end + + if @options[:embed] != :ids && options[:add_linked] + Array(association).each do |association| + add_linked(name, association) + end + end + end + end end end end diff --git a/test/action_controller/json_api_linked_test.rb b/test/action_controller/json_api_linked_test.rb index a5b7c8db..d0138538 100644 --- a/test/action_controller/json_api_linked_test.rb +++ b/test/action_controller/json_api_linked_test.rb @@ -6,10 +6,13 @@ module ActionController class MyController < ActionController::Base def setup_post @role1 = Role.new(id: 1, name: 'admin') + @role2 = Role.new(id: 2, name: 'colab') @author = Author.new(id: 1, name: 'Steve K.') @author.posts = [] @author.bio = nil - @author.roles = [@role1] + @author.roles = [@role1, @role2] + @role1.author = @author + @role2.author = @author @author2 = Author.new(id: 2, name: 'Anonymous') @author2.posts = [] @author2.bio = nil @@ -98,11 +101,25 @@ module ActionController expected_linked = { "authors" => [{ "id" => "1", - "name" => "Steve K." + "name" => "Steve K.", + "links" => { + "posts" => [], + "roles" => ["1", "2"], + "bio" => nil + } }], "roles"=>[{ "id" => "1", - "name" => "admin" + "name" => "admin", + "links" => { + "author" => "1" + } + }, { + "id" => "2", + "name" => "colab", + "links" => { + "author" => "1" + } }] } assert_equal expected_linked, response['linked'] diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb index f18226af..5084dcc0 100644 --- a/test/adapter/json_api/belongs_to_test.rb +++ b/test/adapter/json_api/belongs_to_test.rb @@ -32,7 +32,16 @@ module ActiveModel def test_includes_linked_post @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'post') - assert_equal([{id: "42", title: 'New Post', body: 'Body'}], @adapter.serializable_hash[:linked][:posts]) + expected = [{ + id: "42", + title: 'New Post', + body: 'Body', + links: { + comments: ["1"], + author: "1" + } + }] + assert_equal expected, @adapter.serializable_hash[:linked][:posts] end def test_include_nil_author diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb index 6ded298a..e6a511fb 100644 --- a/test/adapter/json_api/has_many_test.rb +++ b/test/adapter/json_api/has_many_test.rb @@ -35,10 +35,22 @@ module ActiveModel def test_includes_linked_comments @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments') - assert_equal([ - {id: "1", body: 'ZOMG A COMMENT'}, - {id: "2", body: 'ZOMG ANOTHER COMMENT'} - ], @adapter.serializable_hash[:linked][:comments]) + expected = [{ + id: "1", + body: 'ZOMG A COMMENT', + links: { + post: "1", + author: nil + } + }, { + id: "2", + body: 'ZOMG ANOTHER COMMENT', + links: { + post: "1", + author: nil + } + }] + assert_equal expected, @adapter.serializable_hash[:linked][:comments] end def test_no_include_linked_if_comments_is_empty diff --git a/test/adapter/json_api/linked_test.rb b/test/adapter/json_api/linked_test.rb index a7982247..d25cfa06 100644 --- a/test/adapter/json_api/linked_test.rb +++ b/test/adapter/json_api/linked_test.rb @@ -35,10 +35,42 @@ module ActiveModel { title: "Hello!!", body: "Hello, world!!", id: "1", links: { comments: ['1', '2'], author: "1" } }, { title: "New Post", body: "Body", id: "2", links: { comments: [], :author => "1" } } ], @adapter.serializable_hash[:posts]) - assert_equal({ :comments => [{ :id => "1", :body => "ZOMG A COMMENT" }, - { :id => "2", :body => "ZOMG ANOTHER COMMENT" }], - :authors => [{ :id => "1", :name => "Steve K." }], - :bios=>[{:id=>"1", :content=>"AMS Contributor"}] }, @adapter.serializable_hash[:linked]) + + + expected = { + comments: [{ + id: "1", + body: "ZOMG A COMMENT", + links: { + post: "1", + author: nil + } + }, { + id: "2", + body: "ZOMG ANOTHER COMMENT", + links: { + post: "1", + author: nil + } + }], + authors: [{ + id: "1", + name: "Steve K.", + links: { + posts: ["1", "2"], + roles: [], + bio: "1" + } + }], + bios: [{ + id: "1", + content: "AMS Contributor", + links: { + author: "1" + } + }] + } + assert_equal expected, @adapter.serializable_hash[:linked] end end end From 7333f202856e7d4309497755a1dfc3c1937122c5 Mon Sep 17 00:00:00 2001 From: Alexandre de Oliveira Date: Wed, 12 Nov 2014 19:59:27 -0200 Subject: [PATCH 06/10] Use type as key for linked resources If type is `author` but the association is called `writer`, the linked resource key should be called `authors`, e.g { ... linked: { authors: [{ ... }] } ... } --- .../serializer/adapter/json_api.rb | 2 +- test/adapter/json_api/belongs_to_test.rb | 49 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 567630f8..587485e5 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -62,7 +62,7 @@ module ActiveModel resource_path = [parent, resource_name].compact.join('.') if include_assoc?(resource_path) - plural_name = resource_name.to_s.pluralize.to_sym + plural_name = serialized_object_type(serializer).pluralize.to_sym attrs = [attributes_for_serializer(serializer, @options)].flatten @top[:linked] ||= {} @top[:linked][plural_name] ||= [] diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb index 5084dcc0..e641429e 100644 --- a/test/adapter/json_api/belongs_to_test.rb +++ b/test/adapter/json_api/belongs_to_test.rb @@ -8,6 +8,7 @@ module ActiveModel def setup @author = Author.new(id: 1, name: 'Steve K.') @author.bio = nil + @author.roles = [] @post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @@ -54,7 +55,53 @@ module ActiveModel def test_include_type_for_association_when_is_different_than_name serializer = BlogSerializer.new(@blog) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - assert_equal({type: "author", id: "1"}, adapter.serializable_hash[:blogs][:links][:writer]) + links = adapter.serializable_hash[:blogs][:links] + expected = { + writer: { + type: "author", + id: "1" + }, + articles: { + type: "posts", + ids: ["42", "43"] + } + } + assert_equal expected, links + end + + def test_include_linked_resources_with_type_name + serializer = BlogSerializer.new(@blog) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, include: "writer,articles") + linked = adapter.serializable_hash[:linked] + expected = { + authors: [{ + id: "1", + name: "Steve K.", + links: { + posts: [], + roles: [], + bio: nil + } + }], + posts: [{ + title: "New Post", + body: "Body", + id: "42", + links: { + comments: ["1"], + author: "1" + } + }, { + title: "Hello!!", + body: "Hello, world!!", + id: "43", + links: { + comments: [], + author: nil + } + }] + } + assert_equal expected, linked end end end From 90649554999ca2f6d8e3522149ef181cf842d1d7 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Wed, 12 Nov 2014 14:34:34 -0800 Subject: [PATCH 07/10] Use the new beta build env on Travis also remove the custom bundle install line as Travis does that by default --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad22ffe3..ae72a4dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: ruby + +sudo: false + rvm: - 1.9.3 - 2.0.0 @@ -6,13 +9,13 @@ rvm: - jruby-19mode - rbx-2 - ruby-head -install: - - bundle install --retry=3 + env: - "RAILS_VERSION=3.2" - "RAILS_VERSION=4.0" - "RAILS_VERSION=4.1" - "RAILS_VERSION=master" + matrix: allow_failures: - rvm: ruby-head From 90b88da70a1865a42374fbab52f8d7e426ad6c4f Mon Sep 17 00:00:00 2001 From: Alexandre de Oliveira Date: Sun, 2 Nov 2014 01:23:29 -0200 Subject: [PATCH 08/10] Fixes rbx gems bundling on TravisCI Fixes https://github.com/travis-ci/travis-ci/issues/2821 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index ae72a4dc..78612434 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,9 @@ rvm: - rbx-2 - ruby-head +install: + - bundle install --retry=3 + env: - "RAILS_VERSION=3.2" - "RAILS_VERSION=4.0" From 08716d20c91d321b69e8b3720efdb7c5c4ecb7ec Mon Sep 17 00:00:00 2001 From: Gary Gordon Date: Thu, 6 Nov 2014 11:10:15 -0500 Subject: [PATCH 09/10] Rename attribute with :key (0.8.x compatibility) --- README.md | 12 ++++++++++++ lib/active_model/serializer.rb | 9 ++++++++- test/fixtures/poro.rb | 5 +++++ test/serializers/attribute_test.rb | 23 +++++++++++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test/serializers/attribute_test.rb diff --git a/README.md b/README.md index 1d9db323..6bb70f3a 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,18 @@ ActiveModel::Serializer.config.adapter = :hal You won't need to implement an adapter unless you wish to use a new format or media type with AMS. +If you would like the key in the outputted JSON to be different from its name in ActiveRecord, you can use the :key option to customize it: + +```ruby +class PostSerializer < ActiveModel::Serializer + attributes :id, :body + + # look up :subject on the model, but use +title+ in the JSON + attribute :subject, :key => :title + has_many :comments +end +``` + In your controllers, when you use `render :json`, Rails will now first search for a serializer for the object and use it if available. diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index dbc2060d..9f9c91db 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -21,7 +21,6 @@ module ActiveModel def self.attributes(*attrs) @_attributes.concat attrs - attrs.each do |attr| define_method attr do object.read_attribute_for_serialization(attr) @@ -29,6 +28,14 @@ module ActiveModel end end + def self.attribute(attr, options = {}) + key = options.fetch(:key, attr) + @_attributes.concat [key] + define_method key do + object.read_attribute_for_serialization(attr) + end unless method_defined?(key) + end + # Defines an association in the object should be rendered. # # The serializer object should implement the association name diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index 4f584fed..f7c1becb 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -95,3 +95,8 @@ PaginatedSerializer = Class.new(ActiveModel::Serializer::ArraySerializer) do 'paginated' end end + +AlternateBlogSerializer = Class.new(ActiveModel::Serializer) do + attribute :id + attribute :name, key: :title +end diff --git a/test/serializers/attribute_test.rb b/test/serializers/attribute_test.rb new file mode 100644 index 00000000..35f27ee4 --- /dev/null +++ b/test/serializers/attribute_test.rb @@ -0,0 +1,23 @@ +require 'test_helper' + +module ActiveModel + class Serializer + class AttributeTest < Minitest::Test + def setup + @blog = Blog.new({ id: 1, name: 'AMS Hints' }) + @blog_serializer = AlternateBlogSerializer.new(@blog) + end + + def test_attributes_definition + assert_equal([:id, :title], + @blog_serializer.class._attributes) + end + + def test_json_serializable_hash + adapter = ActiveModel::Serializer::Adapter::Json.new(@blog_serializer) + assert_equal({:id=>1, :title=>"AMS Hints"}, adapter.serializable_hash) + end + end + end +end + From 5560b49098b486d29ce4c00bd2ada965ee1c67e1 Mon Sep 17 00:00:00 2001 From: Gary Gordon Date: Tue, 4 Nov 2014 15:36:52 -0500 Subject: [PATCH 10/10] Allow overriding the adapter with render option Make it easy to use multiple adapters in an app. use "adapter: false" to not use ams make a test override config.adapter --- lib/action_controller/serialization.rb | 23 +++++----- lib/active_model/serializer.rb | 3 +- lib/active_model/serializer/adapter.rb | 10 +++++ .../adapter_selector_test.rb | 41 +++++++++++++++++ .../action_controller/json_api_linked_test.rb | 44 +++++-------------- test/action_controller/serialization_test.rb | 6 +-- test/adapter_test.rb | 20 +++++++++ test/serializers/configuration_test.rb | 2 +- 8 files changed, 99 insertions(+), 50 deletions(-) create mode 100644 test/action_controller/adapter_selector_test.rb diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index d157e2f9..edb5bc0e 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -6,29 +6,32 @@ module ActionController include ActionController::Renderers - ADAPTER_OPTION_KEYS = [:include, :root] + ADAPTER_OPTION_KEYS = [:include, :root, :adapter] - def get_serializer(resource, options) - @_serializer ||= options.delete(:serializer) + def get_serializer(resource) + @_serializer ||= @_serializer_opts.delete(:serializer) @_serializer ||= ActiveModel::Serializer.serializer_for(resource) - if options.key?(:each_serializer) - options[:serializer] = options.delete(:each_serializer) + if @_serializer_opts.key?(:each_serializer) + @_serializer_opts[:serializer] = @_serializer_opts.delete(:each_serializer) end @_serializer end + def use_adapter? + !(@_adapter_opts.key?(:adapter) && !@_adapter_opts[:adapter]) + end + [:_render_option_json, :_render_with_renderer_json].each do |renderer_method| define_method renderer_method do |resource, options| - - adapter_opts, serializer_opts = + @_adapter_opts, @_serializer_opts = options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] } - if (serializer = get_serializer(resource, serializer_opts)) + if use_adapter? && (serializer = get_serializer(resource)) # omg hax - object = serializer.new(resource, serializer_opts) - adapter = ActiveModel::Serializer.adapter.new(object, adapter_opts) + object = serializer.new(resource, @_serializer_opts) + adapter = ActiveModel::Serializer::Adapter.create(object, @_adapter_opts) super(adapter, options) else super(resource, options) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 9f9c91db..edf4e024 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -90,8 +90,7 @@ module ActiveModel def self.adapter adapter_class = case config.adapter when Symbol - class_name = "ActiveModel::Serializer::Adapter::#{config.adapter.to_s.classify}" - class_name.safe_constantize + ActiveModel::Serializer::Adapter.adapter_class(config.adapter) when Class config.adapter end diff --git a/lib/active_model/serializer/adapter.rb b/lib/active_model/serializer/adapter.rb index 84649285..bf546097 100644 --- a/lib/active_model/serializer/adapter.rb +++ b/lib/active_model/serializer/adapter.rb @@ -20,6 +20,16 @@ module ActiveModel def as_json(options = {}) serializable_hash(options) end + + def self.create(resource, options = {}) + override = options.delete(:adapter) + klass = override ? adapter_class(override) : ActiveModel::Serializer.adapter + klass.new(resource, options) + end + + def self.adapter_class(adapter) + "ActiveModel::Serializer::Adapter::#{adapter.to_s.classify}".safe_constantize + end end end end diff --git a/test/action_controller/adapter_selector_test.rb b/test/action_controller/adapter_selector_test.rb new file mode 100644 index 00000000..96d7dd52 --- /dev/null +++ b/test/action_controller/adapter_selector_test.rb @@ -0,0 +1,41 @@ +require 'test_helper' + +module ActionController + module Serialization + class AdapterSelectorTest < ActionController::TestCase + class MyController < ActionController::Base + def render_using_default_adapter + @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + render json: @profile + end + + def render_using_adapter_override + @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + render json: @profile, adapter: :json_api + end + + def render_skipping_adapter + @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + render json: @profile, adapter: false + end + end + + tests MyController + + def test_render_using_default_adapter + get :render_using_default_adapter + assert_equal '{"name":"Name 1","description":"Description 1"}', response.body + end + + def test_render_using_adapter_override + get :render_using_adapter_override + assert_equal '{"profiles":{"name":"Name 1","description":"Description 1"}}', response.body + end + + def test_render_skipping_adapter + get :render_skipping_adapter + assert_equal '{"attributes":{"name":"Name 1","description":"Description 1","comments":"Comments 1"}}', response.body + end + end + end +end diff --git a/test/action_controller/json_api_linked_test.rb b/test/action_controller/json_api_linked_test.rb index d0138538..dca55e67 100644 --- a/test/action_controller/json_api_linked_test.rb +++ b/test/action_controller/json_api_linked_test.rb @@ -28,54 +28,34 @@ module ActionController @second_comment.author = nil end - def with_json_api_adapter - old_adapter = ActiveModel::Serializer.config.adapter - ActiveModel::Serializer.config.adapter = :json_api - yield - ensure - ActiveModel::Serializer.config.adapter = old_adapter - end - def render_resource_without_include - with_json_api_adapter do - setup_post - render json: @post - end + setup_post + render json: @post, adapter: :json_api end def render_resource_with_include - with_json_api_adapter do - setup_post - render json: @post, include: 'author' - end + setup_post + render json: @post, include: 'author', adapter: :json_api end def render_resource_with_nested_include - with_json_api_adapter do - setup_post - render json: @post, include: 'comments.author' - end + setup_post + render json: @post, include: 'comments.author', adapter: :json_api end def render_resource_with_nested_has_many_include - with_json_api_adapter do - setup_post - render json: @post, include: 'author,author.roles' - end + setup_post + render json: @post, include: 'author,author.roles', adapter: :json_api end def render_collection_without_include - with_json_api_adapter do - setup_post - render json: [@post] - end + setup_post + render json: [@post], adapter: :json_api end def render_collection_with_include - with_json_api_adapter do - setup_post - render json: [@post], include: 'author,comments' - end + setup_post + render json: [@post], include: 'author,comments', adapter: :json_api end end diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index fbbb48f9..a4c20a54 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -25,13 +25,9 @@ module ActionController end def render_using_custom_root_in_adapter_with_a_default - old_adapter = ActiveModel::Serializer.config.adapter # JSON-API adapter sets root by default - ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) - render json: @profile, root: "profile" - ensure - ActiveModel::Serializer.config.adapter = old_adapter + render json: @profile, root: "profile", adapter: :json_api end def render_array_using_implicit_serializer diff --git a/test/adapter_test.rb b/test/adapter_test.rb index 737e0c4e..d76559d5 100644 --- a/test/adapter_test.rb +++ b/test/adapter_test.rb @@ -18,6 +18,26 @@ module ActiveModel def test_serializer assert_equal @serializer, @adapter.serializer end + + def test_adapter_class_for_known_adapter + klass = ActiveModel::Serializer::Adapter.adapter_class(:json_api) + assert_equal ActiveModel::Serializer::Adapter::JsonApi, klass + end + + def test_adapter_class_for_unknown_adapter + klass = ActiveModel::Serializer::Adapter.adapter_class(:json_simple) + assert_nil klass + end + + def test_create_adapter + adapter = ActiveModel::Serializer::Adapter.create(@serializer) + assert_equal ActiveModel::Serializer::Adapter::Json, adapter.class + end + + def test_create_adapter_with_override + adapter = ActiveModel::Serializer::Adapter.create(@serializer, { adapter: :json_api}) + assert_equal ActiveModel::Serializer::Adapter::JsonApi, adapter.class + end end end end diff --git a/test/serializers/configuration_test.rb b/test/serializers/configuration_test.rb index eec00436..9c6c5fea 100644 --- a/test/serializers/configuration_test.rb +++ b/test/serializers/configuration_test.rb @@ -7,7 +7,7 @@ module ActiveModel assert_equal ActiveModel::Serializer::ArraySerializer, ActiveModel::Serializer.config.array_serializer end - def test_adapter + def test_default_adapter assert_equal :json, ActiveModel::Serializer.config.adapter end end