From 1dcb3b3a279e0b364d9352cdcf0dd995b84e4798 Mon Sep 17 00:00:00 2001 From: Leandro Cesquini Pereira Date: Fri, 24 Jul 2015 23:05:52 -0300 Subject: [PATCH] Adds support for top-level links to JsonApi adapter http://jsonapi.org/format/#document-top-level fix failing tests support for top-level links limited to jsonapi adapter --- .gitignore | 1 + CHANGELOG.md | 3 +- README.md | 8 +++ lib/active_model/serializer.rb | 3 +- .../serializer/adapter/json_api.rb | 5 ++ .../serializer/array_serializer.rb | 3 +- test/action_controller/serialization_test.rb | 34 ++++++++++ test/array_serializer_test.rb | 6 ++ test/serializers/links_test.rb | 62 +++++++++++++++++++ 9 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 test/serializers/links_test.rb diff --git a/.gitignore b/.gitignore index 0374e060e..a2e13e610 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ test/version_tmp tmp *.swp .ruby-version +tags diff --git a/CHANGELOG.md b/CHANGELOG.md index 28f1e822e..02960fe67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,5 @@ * adds cache support to attributes and associations [@joaomdmoura] * uses model name to determine the type [@lsylvester] * remove root key option and split JSON adapter [@joaomdmoura] - * adds FlattenJSON as default adapter [@joaomdmoura] \ No newline at end of file + * adds FlattenJSON as default adapter [@joaomdmoura] + * adds support for `links` at top level of JsonApi adapter [@leandrocp] diff --git a/README.md b/README.md index 15e869472..84828befe 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,14 @@ render json: @post, meta: { total: 10 }, meta_key: "custom_meta" `meta` will only be included in your response if you are using an Adapter that supports `root`, as JsonAPI and Json adapters, the default adapter (FlattenJson) doesn't have `root`. +### Links + +A top-level `links` object may be specified in the `render` call: + +```ruby +render json: @post, links: { self: "/posts/1" } +``` + ### Overriding association methods If you want to override any association, you can use: diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index c267e5f0f..a89c9f5cf 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -105,7 +105,7 @@ def self.root_name name.demodulize.underscore.sub(/_serializer$/, '') if name end - attr_accessor :object, :root, :meta, :meta_key, :scope + attr_accessor :object, :root, :meta, :meta_key, :links, :scope def initialize(object, options = {}) @object = object @@ -113,6 +113,7 @@ def initialize(object, options = {}) @root = options[:root] @meta = options[:meta] @meta_key = options[:meta_key] + @links = options[:links] @scope = options[:scope] scope_name = options[:scope_name] diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 551ed54b1..db42182c8 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -31,6 +31,7 @@ def serializable_hash(options = nil) @hash[:data] = attributes_for_serializer(serializer, options) add_resource_relationships(@hash[:data], serializer) end + @hash[:links] = attributes_for_top_level_links(serializer) if serializer.links @hash end @@ -157,6 +158,10 @@ def add_resource_relationships(attrs, serializer, options = {}) end end end + + def attributes_for_top_level_links(serializer) + serializer.links + end end end end diff --git a/lib/active_model/serializer/array_serializer.rb b/lib/active_model/serializer/array_serializer.rb index f2f916e57..61b9efab8 100644 --- a/lib/active_model/serializer/array_serializer.rb +++ b/lib/active_model/serializer/array_serializer.rb @@ -5,7 +5,7 @@ class ArraySerializer include Enumerable delegate :each, to: :@objects - attr_reader :root, :meta, :meta_key + attr_reader :root, :meta, :meta_key, :links def initialize(objects, options = {}) @root = options[:root] @@ -24,6 +24,7 @@ def initialize(objects, options = {}) end @meta = options[:meta] @meta_key = options[:meta_key] + @links = options[:links] end def json_key diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index 8c89ceac0..e5ee159ca 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -58,6 +58,17 @@ def render_array_using_implicit_serializer_and_meta end end + def render_array_using_implicit_serializer_and_links + with_adapter ActiveModel::Serializer::Adapter::JsonApi do + + @profiles = [ + Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + ] + + render json: @profiles, links: { self: "/profiles/1" } + end + end + def render_object_with_cache_enabled @comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' }) @author = Author.new(id: 1, name: 'Joao Moura.') @@ -266,6 +277,29 @@ def test_render_array_using_implicit_serializer_and_meta assert_equal expected.to_json, @response.body end + def test_render_array_using_implicit_serializer_and_links + get :render_array_using_implicit_serializer_and_links + + expected = { + data: [ + { + id: assigns(:profiles).first.id.to_s, + type: "profiles", + attributes: { + name: "Name 1", + description: "Description 1" + } + } + ], + links: { + self: "/profiles/1" + } + } + + assert_equal 'application/json', @response.content_type + assert_equal expected.to_json, @response.body + end + def test_render_with_cache_enable expected = { id: 1, diff --git a/test/array_serializer_test.rb b/test/array_serializer_test.rb index 3eff3ef8a..b20a34c08 100644 --- a/test/array_serializer_test.rb +++ b/test/array_serializer_test.rb @@ -45,6 +45,12 @@ def test_meta_and_meta_key_attr_readers assert_equal @serializer.meta_key, "the meta key" end + def test_links_attr_reader + @serializer = ArraySerializer.new([@comment, @post], :links => {"self" => "/array/1"}) + + assert_equal @serializer.links, {"self" => "/array/1"} + end + def test_root_default @serializer = ArraySerializer.new([@comment, @post]) assert_equal @serializer.root, nil diff --git a/test/serializers/links_test.rb b/test/serializers/links_test.rb new file mode 100644 index 000000000..c9c77e6c0 --- /dev/null +++ b/test/serializers/links_test.rb @@ -0,0 +1,62 @@ +require 'test_helper' + +module ActiveModel + class Serializer + class LinksTest < Minitest::Test + def setup + ActionController::Base.cache_store.clear + @blog = Blog.new(id: 1, + name: 'AMS Hints', + writer: Author.new(id: 2, name: "Steve"), + articles: [Post.new(id: 3, title: "AMS")]) + end + + def test_links_is_present_with_root + serializer = AlternateBlogSerializer.new(@blog, :links => {:self => "/blogs/1"}) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + expected = { + data: { + id: "1", + type: "blogs", + attributes: { + title: "AMS Hints" + } + }, + links: { + self: "/blogs/1" + } + } + assert_equal expected, adapter.as_json + end + + def test_links_is_not_present_when_not_declared + serializer = AlternateBlogSerializer.new(@blog) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + expected = { + data: { + id: "1", + type: "blogs", + attributes: { + title: "AMS Hints" + } + } + } + assert_equal expected, adapter.as_json + end + + def test_links_is_not_present_on_flattenjson_adapter + serializer = AlternateBlogSerializer.new(@blog, :links => {:self => "/blogs/1"}) + adapter = ActiveModel::Serializer::Adapter::FlattenJson.new(serializer) + expected = {:id=>1, :title=>"AMS Hints"} + assert_equal expected, adapter.as_json + end + + def test_links_is_not_present_on_json_adapter + serializer = AlternateBlogSerializer.new(@blog, :links => {:self => "/blogs/1"}) + adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) + expected = {:blog=>{:id=>1, :title=>"AMS Hints"}} + assert_equal expected, adapter.as_json + end + end + end +end