diff --git a/CHANGELOG.md b/CHANGELOG.md index f85e9cffd..b2079e3ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,4 +2,5 @@ * adds support for `meta` and `meta_key` [@kurko] * adds method to override association [adcb99e, @kurko] - * add `has_one` attribute for backwards compatibility [@ggordon] + * adds `has_one` attribute for backwards compatibility [@ggordon] + * updates JSON API support to RC3 [@mateomurphy] diff --git a/README.md b/README.md index 4f75004a3..127b0451c 100644 --- a/README.md +++ b/README.md @@ -181,9 +181,9 @@ end #### JSONAPI -This adapter follows the format specified in +This adapter follows RC3 of the format specified in [jsonapi.org/format](http://jsonapi.org/format). It will include the associated -resources in the `"linked"` member when the resource names are included in the +resources in the `"included"` member when the resource names are included in the `include` option. ```ruby diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index b61934005..588abc99f 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -164,6 +164,14 @@ def json_key end end + def id + object.id if object + end + + def type + object.class.to_s.demodulize.underscore.pluralize + end + def attributes(options = {}) attributes = if options[:fields] @@ -172,6 +180,8 @@ def attributes(options = {}) self.class._attributes.dup end + attributes += options[:required_fields] if options[:required_fields] + attributes.each_with_object({}) do |name, hash| hash[name] = send(name) end diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index f604b67fa..96a3f4428 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -16,16 +16,14 @@ def initialize(serializer, options = {}) end def serializable_hash(options = {}) - @root = (@options[:root] || serializer.json_key.to_s.pluralize).to_sym - if serializer.respond_to?(:each) - @hash[@root] = serializer.map do |s| - self.class.new(s, @options.merge(top: @top, fieldset: @fieldset)).serializable_hash[@root] + @hash[:data] = serializer.map do |s| + self.class.new(s, @options.merge(top: @top, fieldset: @fieldset)).serializable_hash[:data] end else @hash = cached_object do - @hash[@root] = attributes_for_serializer(serializer, @options) - add_resource_links(@hash[@root], serializer) + @hash[:data] = attributes_for_serializer(serializer, @options) + add_resource_links(@hash[:data], serializer) @hash end end @@ -35,58 +33,40 @@ def serializable_hash(options = {}) private def add_links(resource, name, serializers) - type = serialized_object_type(serializers) resource[:links] ||= {} - - if name.to_s == type || !type - resource[:links][name] ||= [] - resource[:links][name] += serializers.map{|serializer| serializer.id.to_s } - else - resource[:links][name] ||= {} - resource[:links][name][:type] = type - resource[:links][name][:ids] ||= [] - resource[:links][name][:ids] += serializers.map{|serializer| serializer.id.to_s } - end + resource[:links][name] ||= { linkage: [] } + resource[:links][name][:linkage] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } } end def add_link(resource, name, serializer) resource[:links] ||= {} - resource[:links][name] = nil + resource[:links][name] = { linkage: nil } if serializer && serializer.object - type = serialized_object_type(serializer) - if name.to_s == type || !type - resource[:links][name] = serializer.id.to_s - else - resource[:links][name] ||= {} - resource[:links][name][:type] = type - resource[:links][name][:id] = serializer.id.to_s - end + resource[:links][name][:linkage] = { type: serializer.type, id: serializer.id.to_s } end end - def add_linked(resource_name, serializers, parent = nil) + def add_included(resource_name, serializers, parent = nil) serializers = Array(serializers) unless serializers.respond_to?(:each) resource_path = [parent, resource_name].compact.join('.') - if include_assoc?(resource_path) && resource_type = serialized_object_type(serializers) - plural_name = resource_type.pluralize.to_sym - @top[:linked] ||= {} - @top[:linked][plural_name] ||= [] + if include_assoc?(resource_path) + @top[:included] ||= [] serializers.each do |serializer| attrs = attributes_for_serializer(serializer, @options) - add_resource_links(attrs, serializer, add_linked: false) + add_resource_links(attrs, serializer, add_included: false) - @top[:linked][plural_name].push(attrs) unless @top[:linked][plural_name].include?(attrs) + @top[:included].push(attrs) unless @top[:included].include?(attrs) end end serializers.each do |serializer| serializer.each_association do |name, association, opts| - add_linked(name, association, resource_path) if association + add_included(name, association, resource_path) if association end if include_nested_assoc? resource_path end end @@ -97,14 +77,16 @@ def attributes_for_serializer(serializer, options) result = [] serializer.each do |object| options[:fields] = @fieldset && @fieldset.fields_for(serializer) + options[:required_fields] = [:id, :type] attributes = object.attributes(options) - attributes[:id] = attributes[:id].to_s if attributes[:id] + attributes[:id] = attributes[:id].to_s result << attributes end else options[:fields] = @fieldset && @fieldset.fields_for(serializer) + options[:required_fields] = [:id, :type] result = serializer.attributes(options) - result[:id] = result[:id].to_s if result[:id] + result[:id] = result[:id].to_s end result @@ -128,18 +110,8 @@ def check_assoc(assoc) end end - def serialized_object_type(serializer) - return false unless Array(serializer).first - type_name = Array(serializer).first.object.class.to_s.demodulize.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) + options[:add_included] = options.fetch(:add_included, true) serializer.each_association do |name, association, opts| attrs[:links] ||= {} @@ -150,9 +122,9 @@ def add_resource_links(attrs, serializer, options = {}) add_link(attrs, name, association) end - if @options[:embed] != :ids && options[:add_linked] + if options[:add_included] Array(association).each do |association| - add_linked(name, association) + add_included(name, association) end end end diff --git a/test/action_controller/adapter_selector_test.rb b/test/action_controller/adapter_selector_test.rb index 96d7dd524..efa61f7b1 100644 --- a/test/action_controller/adapter_selector_test.rb +++ b/test/action_controller/adapter_selector_test.rb @@ -29,7 +29,17 @@ def test_render_using_default_adapter def test_render_using_adapter_override get :render_using_adapter_override - assert_equal '{"profiles":{"name":"Name 1","description":"Description 1"}}', response.body + + expected = { + data: { + name: "Name 1", + description: "Description 1", + id: assigns(:profile).id.to_s, + type: "profiles" + } + } + + assert_equal expected.to_json, response.body end def test_render_skipping_adapter diff --git a/test/action_controller/json_api_linked_test.rb b/test/action_controller/json_api_linked_test.rb index 7d4dcbf85..321974e39 100644 --- a/test/action_controller/json_api_linked_test.rb +++ b/test/action_controller/json_api_linked_test.rb @@ -83,80 +83,87 @@ def render_collection_with_include def test_render_resource_without_include get :render_resource_without_include response = JSON.parse(@response.body) - refute response.key? 'linked' + refute response.key? 'included' end def test_render_resource_with_include get :render_resource_with_include response = JSON.parse(@response.body) - assert response.key? 'linked' - assert_equal 1, response['linked']['authors'].size - assert_equal 'Steve K.', response['linked']['authors'].first['name'] + assert response.key? 'included' + assert_equal 1, response['included'].size + assert_equal 'Steve K.', response['included'].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" => [{ + expected_linked = [ + { "id" => "1", + "type" => "authors", "name" => "Steve K.", "links" => { - "posts" => [], - "roles" => ["1", "2"], - "bio" => nil + "posts" => { "linkage" => [] }, + "roles" => { "linkage" => [{ "type" =>"roles", "id" => "1" }, { "type" =>"roles", "id" => "2" }] }, + "bio" => { "linkage" => nil } } - }], - "roles"=>[{ + }, { "id" => "1", + "type" => "roles", "name" => "admin", "links" => { - "author" => "1" + "author" => { "linkage" => { "type" =>"authors", "id" => "1" } } } }, { "id" => "2", + "type" => "roles", "name" => "colab", "links" => { - "author" => "1" + "author" => { "linkage" => { "type" =>"authors", "id" => "1" } } } - }] - } - assert_equal expected_linked, response['linked'] + } + ] + assert_equal expected_linked, response['included'] end def test_render_resource_with_nested_include get :render_resource_with_nested_include response = JSON.parse(@response.body) - assert response.key? 'linked' - assert_equal 1, response['linked']['authors'].size - assert_equal 'Anonymous', response['linked']['authors'].first['name'] + assert response.key? 'included' + assert_equal 1, response['included'].size + assert_equal 'Anonymous', response['included'].first['name'] end def test_render_collection_without_include get :render_collection_without_include response = JSON.parse(@response.body) - refute response.key? 'linked' + refute response.key? 'included' end def test_render_collection_with_include get :render_collection_with_include response = JSON.parse(@response.body) - assert response.key? 'linked' + assert response.key? 'included' end def test_render_resource_with_nested_attributes_even_when_missing_associations get :render_resource_with_missing_nested_has_many_include response = JSON.parse(@response.body) - assert response.key? 'linked' - refute response['linked'].key? 'roles' + assert response.key? 'included' + refute has_type?(response['included'], 'roles') end def test_render_collection_with_missing_nested_has_many_include get :render_collection_with_missing_nested_has_many_include response = JSON.parse(@response.body) - assert response.key? 'linked' - assert response['linked'].key? 'roles' + assert response.key? 'included' + assert has_type?(response['included'], 'roles') + end + + def has_type?(collection, value) + collection.detect { |i| i['type'] == value} end + end end end diff --git a/test/action_controller/serialization_scope_name_test.rb b/test/action_controller/serialization_scope_name_test.rb index f1e81761a..7a406e7a3 100644 --- a/test/action_controller/serialization_scope_name_test.rb +++ b/test/action_controller/serialization_scope_name_test.rb @@ -2,8 +2,6 @@ require 'pathname' class DefaultScopeNameTest < ActionController::TestCase - TestUser = Struct.new(:name, :admin) - class UserSerializer < ActiveModel::Serializer attributes :admin? def admin? @@ -17,11 +15,11 @@ class UserTestController < ActionController::Base before_filter { request.format = :json } def current_user - TestUser.new('Pete', false) + User.new(id: 1, name: 'Pete', admin: false) end def render_new_user - render json: TestUser.new('pete', false), serializer: UserSerializer, adapter: :json_api + render json: User.new(id: 1, name: 'Pete', admin: false), serializer: UserSerializer, adapter: :json_api end end @@ -29,13 +27,11 @@ def render_new_user def test_default_scope_name get :render_new_user - assert_equal '{"users":{"admin?":false}}', @response.body + assert_equal '{"data":{"admin?":false,"id":"1","type":"users"}}', @response.body end end class SerializationScopeNameTest < ActionController::TestCase - TestUser = Struct.new(:name, :admin) - class AdminUserSerializer < ActiveModel::Serializer attributes :admin? def admin? @@ -50,11 +46,11 @@ class AdminUserTestController < ActionController::Base before_filter { request.format = :json } def current_admin - TestUser.new('Bob', true) + User.new(id: 2, name: 'Bob', admin: true) end def render_new_user - render json: TestUser.new('pete', false), serializer: AdminUserSerializer, adapter: :json_api + render json: User.new(id: 1, name: 'Pete', admin: false), serializer: AdminUserSerializer, adapter: :json_api end end @@ -62,6 +58,6 @@ def render_new_user def test_override_scope_name_with_controller get :render_new_user - assert_equal '{"admin_users":{"admin?":true}}', @response.body + assert_equal '{"data":{"admin?":true,"id":"1","type":"users"}}', @response.body end end \ No newline at end of file diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index a10cb039b..b47db5218 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -15,13 +15,11 @@ def render_using_custom_root end def render_using_default_adapter_root - 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 - ensure - ActiveModel::Serializer.config.adapter = old_adapter + with_adapter ActiveModel::Serializer::Adapter::JsonApi do + # JSON-API adapter sets root by default + @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + render json: @profile + end end def render_using_custom_root_in_adapter_with_a_default @@ -39,15 +37,14 @@ def render_array_using_implicit_serializer end def render_array_using_implicit_serializer_and_meta - old_adapter = ActiveModel::Serializer.config.adapter + with_adapter ActiveModel::Serializer::Adapter::JsonApi do - ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi - array = [ - Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) - ] - render json: array, meta: { total: 10 } - ensure - ActiveModel::Serializer.config.adapter = old_adapter + @profiles = [ + Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + ] + + render json: @profiles, meta: { total: 10 } + end end def render_object_with_cache_enabled @@ -88,6 +85,15 @@ def generate_cached_serializer(obj) adapter = ActiveModel::Serializer.adapter.new(serializer) adapter.to_json end + + def with_adapter(adapter) + old_adapter = ActiveModel::Serializer.config.adapter + # JSON-API adapter sets root by default + ActiveModel::Serializer.config.adapter = adapter + yield + ensure + ActiveModel::Serializer.config.adapter = old_adapter + end end tests MyController @@ -96,8 +102,13 @@ def generate_cached_serializer(obj) def test_render_using_implicit_serializer get :render_using_implicit_serializer + expected = { + name: "Name 1", + description: "Description 1" + } + assert_equal 'application/json', @response.content_type - assert_equal '{"name":"Name 1","description":"Description 1"}', @response.body + assert_equal expected.to_json, @response.body end def test_render_using_custom_root @@ -110,15 +121,33 @@ def test_render_using_custom_root def test_render_using_default_root get :render_using_default_adapter_root + expected = { + data: { + name: "Name 1", + description: "Description 1", + id: assigns(:profile).id.to_s, + type: "profiles" + } + } + assert_equal 'application/json', @response.content_type - assert_equal '{"profiles":{"name":"Name 1","description":"Description 1"}}', @response.body + assert_equal expected.to_json, @response.body end def test_render_using_custom_root_in_adapter_with_a_default get :render_using_custom_root_in_adapter_with_a_default + expected = { + data: { + name: "Name 1", + description: "Description 1", + id: assigns(:profile).id.to_s, + type: "profiles" + } + } + assert_equal 'application/json', @response.content_type - assert_equal '{"profile":{"name":"Name 1","description":"Description 1"}}', @response.body + assert_equal expected.to_json, @response.body end def test_render_array_using_implicit_serializer @@ -142,8 +171,22 @@ def test_render_array_using_implicit_serializer def test_render_array_using_implicit_serializer_and_meta get :render_array_using_implicit_serializer_and_meta + expected = { + data: [ + { + name: "Name 1", + description: "Description 1", + id: assigns(:profiles).first.id.to_s, + type: "profiles" + } + ], + meta: { + total: 10 + } + } + assert_equal 'application/json', @response.content_type - assert_equal '{"profiles":[{"name":"Name 1","description":"Description 1"}],"meta":{"total":10}}', @response.body + assert_equal expected.to_json, @response.body end def test_render_with_cache_enable diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb index 206c41678..3ce8074e4 100644 --- a/test/adapter/json_api/belongs_to_test.rb +++ b/test/adapter/json_api/belongs_to_test.rb @@ -32,56 +32,71 @@ def setup end def test_includes_post_id - assert_equal("42", @adapter.serializable_hash[:comments][:links][:post]) + expected = { linkage: { type: "posts", id: "42" } } + + assert_equal(expected, @adapter.serializable_hash[:data][:links][:post]) end def test_includes_linked_post @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'post') expected = [{ id: "42", + type: "posts", title: 'New Post', body: 'Body', links: { - comments: ["1"], - blog: "999", - author: "1" + comments: { linkage: [ { type: "comments", id: "1" } ] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } } }] - assert_equal expected, @adapter.serializable_hash[:linked][:posts] + assert_equal expected, @adapter.serializable_hash[:included] end def test_limiting_linked_post_fields @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'post', fields: {post: [:title]}) expected = [{ + id: "42", + type: "posts", title: 'New Post', links: { - comments: ["1"], - blog: "999", - author: "1" + comments: { linkage: [ { type: "comments", id: "1" } ] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } } }] - assert_equal expected, @adapter.serializable_hash[:linked][:posts] + assert_equal expected, @adapter.serializable_hash[:included] end def test_include_nil_author serializer = PostSerializer.new(@anonymous_post) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - assert_equal({comments: [], blog: "999", author: nil}, adapter.serializable_hash[:posts][:links]) + assert_equal({comments: { linkage: [] }, blog: { linkage: { type: "blogs", id: "999" } }, author: { linkage: nil }}, adapter.serializable_hash[:data][:links]) end def test_include_type_for_association_when_different_than_name serializer = BlogSerializer.new(@blog) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - links = adapter.serializable_hash[:blogs][:links] + links = adapter.serializable_hash[:data][:links] expected = { writer: { - type: "author", - id: "1" + linkage: { + type: "authors", + id: "1" + } }, articles: { - type: "posts", - ids: ["42", "43"] + linkage: [ + { + type: "posts", + id: "42" + }, + { + type: "posts", + id: "43" + } + ] } } assert_equal expected, links @@ -90,37 +105,39 @@ def test_include_type_for_association_when_different_than_name 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: [{ + linked = adapter.serializable_hash[:included] + expected = [ + { id: "1", + type: "authors", name: "Steve K.", links: { - posts: [], - roles: [], - bio: nil + posts: { linkage: [] }, + roles: { linkage: [] }, + bio: { linkage: nil } } - }], - posts: [{ + },{ + id: "42", + type: "posts", title: "New Post", body: "Body", - id: "42", links: { - comments: ["1"], - blog: "999", - author: "1" + comments: { linkage: [ { type: "comments", id: "1" } ] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } } }, { + id: "43", + type: "posts", title: "Hello!!", body: "Hello, world!!", - id: "43", links: { - comments: [], - blog: "999", - author: nil + comments: { linkage: [] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: nil } } - }] - } + } + ] assert_equal expected, linked end end diff --git a/test/adapter/json_api/collection_test.rb b/test/adapter/json_api/collection_test.rb index 3f2070653..36fb68e28 100644 --- a/test/adapter/json_api/collection_test.rb +++ b/test/adapter/json_api/collection_test.rb @@ -25,18 +25,61 @@ def setup end def test_include_multiple_posts - assert_equal([ - { title: "Hello!!", body: "Hello, world!!", id: "1", links: { comments: [], blog: "999", author: "1" } }, - { title: "New Post", body: "Body", id: "2", links: { comments: [], blog: "999", author: "1" } } - ], @adapter.serializable_hash[:posts]) + expected = [ + { + id: "1", + type: "posts", + title: "Hello!!", + body: "Hello, world!!", + links: { + comments: { linkage: [] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } + } + }, + { + id: "2", + type: "posts", + title: "New Post", + body: "Body", + links: { + comments: { linkage: [] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } + } + } + ] + + assert_equal(expected, @adapter.serializable_hash[:data]) end def test_limiting_fields @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, fields: ['title']) - assert_equal([ - { title: "Hello!!", links: { comments: [], blog: "999", author: "1" } }, - { title: "New Post", links: { comments: [], blog: "999", author: "1" } } - ], @adapter.serializable_hash[:posts]) + + expected = [ + { + id: "1", + type: "posts", + title: "Hello!!", + links: { + comments: { linkage: [] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } + } + }, + { + id: "2", + type: "posts", + title: "New Post", + links: { + comments: { linkage: [] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } + } + } + ] + + assert_equal(expected, @adapter.serializable_hash[:data]) end end 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 0082e5858..50f367c17 100644 --- a/test/adapter/json_api/has_many_embed_ids_test.rb +++ b/test/adapter/json_api/has_many_embed_ids_test.rb @@ -25,7 +25,14 @@ def setup end def test_includes_comment_ids - assert_equal(["1", "2"], @adapter.serializable_hash[:authors][:links][:posts]) + expected = { + linkage: [ + { type: "posts", id: "1"}, + { type: "posts", id: "2"} + ] + } + + assert_equal(expected, @adapter.serializable_hash[:data][:links][:posts]) end def test_no_includes_linked_comments diff --git a/test/adapter/json_api/has_many_explicit_serializer_test.rb b/test/adapter/json_api/has_many_explicit_serializer_test.rb index 0f99f0ecf..4ff53728e 100644 --- a/test/adapter/json_api/has_many_explicit_serializer_test.rb +++ b/test/adapter/json_api/has_many_explicit_serializer_test.rb @@ -29,37 +29,67 @@ def setup end def test_includes_comment_ids - assert_equal(['1', '2'], - @adapter.serializable_hash[:posts][:links][:comments]) + expected = { + linkage: [ + { type: 'comments', id: '1' }, + { type: 'comments', id: '2' } + ] + } + + assert_equal(expected, @adapter.serializable_hash[:data][:links][:comments]) end - def test_includes_linked_comments + def test_includes_linked_data # If CommentPreviewSerializer is applied correctly the body text will not be present in the output - assert_equal([{ id: '1', links: { post: @post.id.to_s}}, - { id: '2', links: { post: @post.id.to_s}}], - @adapter.serializable_hash[:linked][:comments]) + expected = [ + { + id: '1', + type: 'comments', + links: { + post: { linkage: { type: 'posts', id: @post.id.to_s } } + } + }, + { + id: '2', + type: 'comments', + links: { + post: { linkage: { type: 'posts', id: @post.id.to_s } } + } + }, + { + id: @author.id.to_s, + type: "authors", + links: { + posts: { linkage: [ {type: "posts", id: @post.id.to_s } ] } + } + } + ] + + assert_equal(expected, @adapter.serializable_hash[:included]) end def test_includes_author_id - assert_equal(@author.id.to_s, - @adapter.serializable_hash[:posts][:links][:author]) - end + expected = { + linkage: { type: "authors", id: @author.id.to_s } + } - def test_includes_linked_authors - assert_equal([{ id: @author.id.to_s, links: { posts: [@post.id.to_s] } }], - @adapter.serializable_hash[:linked][:authors]) + assert_equal(expected, @adapter.serializable_hash[:data][:links][:author]) end def test_explicit_serializer_with_null_resource @post.author = nil - assert_equal(nil, - @adapter.serializable_hash[:posts][:links][:author]) + + expected = { linkage: nil } + + assert_equal(expected, @adapter.serializable_hash[:data][:links][:author]) end def test_explicit_serializer_with_null_collection @post.comments = [] - assert_equal([], - @adapter.serializable_hash[:posts][:links][:comments]) + + expected = { linkage: [] } + + assert_equal(expected, @adapter.serializable_hash[:data][:links][:comments]) end end end diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb index 87ef36f3f..cdd4bf3e5 100644 --- a/test/adapter/json_api/has_many_test.rb +++ b/test/adapter/json_api/has_many_test.rb @@ -33,45 +33,51 @@ def setup end def test_includes_comment_ids - assert_equal(["1", "2"], @adapter.serializable_hash[:posts][:links][:comments]) + expected = { linkage: [ { type: "comments", id: "1" }, { type: "comments", id: "2" } ] } + + assert_equal(expected, @adapter.serializable_hash[:data][:links][:comments]) end def test_includes_linked_comments @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments') expected = [{ id: "1", + type: "comments", body: 'ZOMG A COMMENT', links: { - post: "1", - author: nil + post: { linkage: { type: "posts", id: "1" } }, + author: { linkage: nil } } }, { id: "2", + type: "comments", body: 'ZOMG ANOTHER COMMENT', links: { - post: "1", - author: nil + post: { linkage: { type: "posts", id: "1" } }, + author: { linkage: nil } } }] - assert_equal expected, @adapter.serializable_hash[:linked][:comments] + assert_equal expected, @adapter.serializable_hash[:included] end def test_limit_fields_of_linked_comments @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments', fields: {comment: [:id]}) expected = [{ id: "1", + type: "comments", links: { - post: "1", - author: nil + post: { linkage: { type: "posts", id: "1" } }, + author: { linkage: nil } } }, { id: "2", + type: "comments", links: { - post: "1", - author: nil + post: { linkage: { type: "posts", id: "1" } }, + author: { linkage: nil } } }] - assert_equal expected, @adapter.serializable_hash[:linked][:comments] + assert_equal expected, @adapter.serializable_hash[:included] end def test_no_include_linked_if_comments_is_empty @@ -84,10 +90,12 @@ def test_no_include_linked_if_comments_is_empty def test_include_type_for_association_when_different_than_name serializer = BlogSerializer.new(@blog) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - actual = adapter.serializable_hash[:blogs][:links][:articles] + actual = adapter.serializable_hash[:data][:links][:articles] expected = { - type: "posts", - ids: ["1"] + linkage: [{ + type: "posts", + id: "1" + }] } assert_equal expected, actual end diff --git a/test/adapter/json_api/has_one_test.rb b/test/adapter/json_api/has_one_test.rb index 247bb2f96..8847e0203 100644 --- a/test/adapter/json_api/has_one_test.rb +++ b/test/adapter/json_api/has_one_test.rb @@ -30,12 +30,26 @@ def setup end def test_includes_bio_id - assert_equal("43", @adapter.serializable_hash[:authors][:links][:bio]) + expected = { linkage: { type: "bios", id: "43" } } + + assert_equal(expected, @adapter.serializable_hash[:data][:links][:bio]) end def test_includes_linked_bio @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'bio') - assert_equal([{id: "43", :content=>"AMS Contributor", :links=>{:author=>"1"}}], @adapter.serializable_hash[:linked][:bios]) + + expected = [ + { + id: "43", + type: "bios", + content:"AMS Contributor", + links: { + author: { linkage: { type: "authors", id: "1" } } + } + } + ] + + assert_equal(expected, @adapter.serializable_hash[:included]) end end end diff --git a/test/adapter/json_api/linked_test.rb b/test/adapter/json_api/linked_test.rb index 728972397..c6fada874 100644 --- a/test/adapter/json_api/linked_test.rb +++ b/test/adapter/json_api/linked_test.rb @@ -57,15 +57,15 @@ def test_include_multiple_posts_and_linked id: "1", body: "ZOMG A COMMENT", links: { - post: "1", - author: nil + post: { linkage: { type: "posts", id: "1" } }, + author: { linkage: nil } } }, { id: "2", body: "ZOMG ANOTHER COMMENT", links: { - post: "1", - author: nil + post: { linkage: { type: "posts", id: "1" } }, + author: { linkage: nil } } } ], @@ -74,17 +74,17 @@ def test_include_multiple_posts_and_linked id: "1", name: "Steve K.", links: { - posts: ["1", "3"], - roles: [], - bio: "1" + posts: { linkage: [ { type: "posts", id: "1" }, { type: "posts", id: "3" } ] }, + roles: { linkage: [] }, + bio: { linkage: { type: "bios", id: "1" } } } }, { id: "2", name: "Tenderlove", links: { - posts: ["2"], - roles: [], - bio: "2" + posts: { linkage: [ { type: "posts", id:"2" } ] }, + roles: { linkage: [] }, + bio: { linkage: { type: "bios", id: "2" } } } } ], @@ -93,13 +93,13 @@ def test_include_multiple_posts_and_linked id: "1", content: "AMS Contributor", links: { - author: "1" + author: { linkage: { type: "authors", id: "1" } } } }, { id: "2", content: "Rails Contributor", links: { - author: "2" + author: { linkage: { type: "authors", id: "2" } } } } ] @@ -110,9 +110,9 @@ def test_include_multiple_posts_and_linked title: "Hello!!", body: "Hello, world!!", links: { - comments: ['1', '2'], - blog: "999", - author: "1" + comments: { linkage: [ { type: "comments", id: '1' }, { type: "comments", id: '2' } ] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } } }, { @@ -120,9 +120,9 @@ def test_include_multiple_posts_and_linked title: "New Post", body: "Body", links: { - comments: [], - blog: "999", - author: "2" + comments: { linkage: [] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "2" } } } } ] @@ -142,42 +142,41 @@ def test_include_multiple_posts_and_linked include: 'author,author.posts' ) - expected = { - authors: [ - { - id: "1", - name: "Steve K.", - links: { - posts: ["10", "30"], - roles: [], - bio: "1" - } + expected = [ + { + id: "1", + type: "authors", + name: "Steve K.", + links: { + posts: { linkage: [ { type: "posts", id: "10"}, { type: "posts", id: "30" }] }, + roles: { linkage: [] }, + bio: { linkage: { type: "bios", id: "1" }} } - ], - posts: [ - { - id: "10", - title: "Hello!!", - body: "Hello, world!!", - links: { - comments: ["1", "2"], - blog: "999", - author: "1" - } - }, { - id: "30", - title: "Yet Another Post", - body: "Body", - links: { - comments: [], - blog: "999", - author: "1" - } + }, { + id: "10", + type: "posts", + title: "Hello!!", + body: "Hello, world!!", + links: { + comments: { linkage: [ { type: "comments", id: "1"}, { type: "comments", id: "2" }] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } } - ] - } - assert_equal expected, adapter.serializable_hash[:linked] - assert_equal expected, alt_adapter.serializable_hash[:linked] + }, { + id: "30", + type: "posts", + title: "Yet Another Post", + body: "Body", + links: { + comments: { linkage: [] }, + blog: { linkage: { type: "blogs", id: "999" } }, + author: { linkage: { type: "authors", id: "1" } } + } + } + ] + + assert_equal expected, adapter.serializable_hash[:included] + assert_equal expected, alt_adapter.serializable_hash[:included] end def test_ignore_model_namespace_for_linked_resource_type @@ -185,11 +184,13 @@ def test_ignore_model_namespace_for_linked_resource_type spammy_post.related = [Spam::UnrelatedLink.new(id: 456)] serializer = SpammyPostSerializer.new(spammy_post) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - links = adapter.serializable_hash[:posts][:links] + links = adapter.serializable_hash[:data][:links] expected = { related: { - type: 'unrelated_links', - ids: ['456'] + linkage: [{ + type: 'unrelated_links', + id: '456' + }] } } assert_equal expected, links diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index d04d79dd5..6ce52e443 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -63,6 +63,7 @@ class ProfilePreviewSerializer < ActiveModel::Serializer Bio = Class.new(Model) Blog = Class.new(Model) Role = Class.new(Model) +User = Class.new(Model) module Spam; end Spam::UnrelatedLink = Class.new(Model) diff --git a/test/serializers/attributes_test.rb b/test/serializers/attributes_test.rb index 7c4bbc8b5..692b4b4a1 100644 --- a/test/serializers/attributes_test.rb +++ b/test/serializers/attributes_test.rb @@ -15,7 +15,13 @@ def test_attributes_definition def test_attributes_with_fields_option assert_equal({name: 'Name 1'}, - @profile_serializer.attributes( { fields: [:name] } ) ) + @profile_serializer.attributes(fields: [:name])) + end + + def test_required_fields + assert_equal({name: 'Name 1', description: 'Description 1'}, + @profile_serializer.attributes(fields: [:name, :description], required_fields: [:name])) + end end end