From 0bd5c6584fc9f258782927e905115e4ee72a1a8e Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Sun, 22 Nov 2015 23:21:13 +0100 Subject: [PATCH 1/3] Add support for resource-level meta. --- lib/active_model/serializer.rb | 25 +++++++++++++++++++ .../serializer/adapter/json_api.rb | 3 +++ 2 files changed, 28 insertions(+) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 714ff65d..5e56ec17 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -23,6 +23,23 @@ module ActiveModel include Type require 'active_model/serializer/adapter' + with_options instance_writer: false, instance_reader: false do |serializer| + serializer.class_attribute :_meta # @api private : meta definition, @see Serializer#meta + end + + # Register a meta attribute for the corresponding resource. + # + # @param [Hash] hash Optional hash + # @param [Block] block Optional block + def self.meta(hash = nil, &block) + self._meta = + if !block.nil? + block + else + hash + end + end + # @param resource [ActiveRecord::Base, ActiveModelSerializers::Model] # @return [ActiveModel::Serializer] # Preferentially returns @@ -128,6 +145,14 @@ module ActiveModel end end + def meta + if self.class._meta.respond_to?(:call) + instance_eval(&self.class._meta) + else + self.class._meta + end + end + protected attr_accessor :instance_options diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 744d62e4..b72ec6e1 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -157,6 +157,9 @@ module ActiveModel links = links_for(serializer) resource_object[:links] = links if links.any? + meta = serializer.meta + resource_object[:meta] = meta unless meta.nil? + resource_object end From 207c85f0fd3fc75277406f257ea72421c5990623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Chac=C3=B3n?= Date: Fri, 27 Nov 2015 09:56:39 -0800 Subject: [PATCH 2/3] Add tests for meta on resource objects. --- test/adapter/json_api/resource_meta_test.rb | 63 +++++++++++++++++++++ test/serializers/meta_test.rb | 16 ++++++ 2 files changed, 79 insertions(+) create mode 100644 test/adapter/json_api/resource_meta_test.rb diff --git a/test/adapter/json_api/resource_meta_test.rb b/test/adapter/json_api/resource_meta_test.rb new file mode 100644 index 00000000..4298f03c --- /dev/null +++ b/test/adapter/json_api/resource_meta_test.rb @@ -0,0 +1,63 @@ +require 'test_helper' + +module ActiveModel + class Serializer + module Adapter + class JsonApi + class ResourceMetaTest < Minitest::Test + class MetaHashPostSerializer < ActiveModel::Serializer + attributes :id + meta stuff: 'value' + end + + class MetaBlockPostSerializer < ActiveModel::Serializer + attributes :id + meta do + { comments_count: object.comments.count } + end + end + + def setup + @post = Post.new(id: 1337, comments: [], author: nil) + end + + def test_meta_hash_object_resource + hash = ActiveModel::SerializableResource.new( + @post, + serializer: MetaHashPostSerializer, + adapter: :json_api + ).serializable_hash + expected = { + stuff: 'value' + } + assert_equal(expected, hash[:data][:meta]) + end + + def test_meta_block_object_resource + hash = ActiveModel::SerializableResource.new( + @post, + serializer: MetaBlockPostSerializer, + adapter: :json_api + ).serializable_hash + expected = { + comments_count: @post.comments.count + } + assert_equal(expected, hash[:data][:meta]) + end + + def test_meta_object_resource_in_array + hash = ActiveModel::SerializableResource.new( + [@post, @post], + each_serializer: MetaBlockPostSerializer, + adapter: :json_api + ).serializable_hash + expected = { + comments_count: @post.comments.count + } + assert_equal([expected, expected], hash[:data].map { |obj| obj[:meta] }) + end + end + end + end + end +end diff --git a/test/serializers/meta_test.rb b/test/serializers/meta_test.rb index a555adb7..e7595476 100644 --- a/test/serializers/meta_test.rb +++ b/test/serializers/meta_test.rb @@ -3,6 +3,8 @@ require 'test_helper' module ActiveModel class Serializer class MetaTest < ActiveSupport::TestCase + MetaBlogSerializer = Class.new(ActiveModel::Serializer) + def setup @blog = Blog.new(id: 1, name: 'AMS Hints', @@ -125,6 +127,20 @@ module ActiveModel } assert_equal(expected, actual) end + + def test_meta_is_set_with_direct_attributes + MetaBlogSerializer.meta stuff: 'value' + blog_meta_serializer = MetaBlogSerializer.new(@blog) + assert_equal(blog_meta_serializer.meta, stuff: 'value') + end + + def test_meta_is_set_with_block + MetaBlogSerializer.meta do + { articles_count: object.articles.count } + end + blog_meta_serializer = MetaBlogSerializer.new(@blog) + assert_equal(blog_meta_serializer.meta, articles_count: @blog.articles.count) + end end end end From 701404f757d6bf3795ac57df6d144fa2fab0ed7a Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Wed, 20 Jan 2016 01:00:14 +0100 Subject: [PATCH 3/3] Clean up meta handling. --- lib/active_model/serializer.rb | 27 ++--------------- .../serializer/adapter/json_api.rb | 7 ++++- .../serializer/adapter/json_api/meta.rb | 29 +++++++++++++++++++ lib/active_model/serializer/meta.rb | 29 +++++++++++++++++++ 4 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 lib/active_model/serializer/adapter/json_api/meta.rb create mode 100644 lib/active_model/serializer/meta.rb diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 5e56ec17..3a0abe9c 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -9,6 +9,7 @@ require 'active_model/serializer/configuration' require 'active_model/serializer/fieldset' require 'active_model/serializer/lint' require 'active_model/serializer/links' +require 'active_model/serializer/meta' require 'active_model/serializer/type' # ActiveModel::Serializer is an abstract class that is @@ -20,26 +21,10 @@ module ActiveModel include Attributes include Caching include Links + include Meta include Type require 'active_model/serializer/adapter' - with_options instance_writer: false, instance_reader: false do |serializer| - serializer.class_attribute :_meta # @api private : meta definition, @see Serializer#meta - end - - # Register a meta attribute for the corresponding resource. - # - # @param [Hash] hash Optional hash - # @param [Block] block Optional block - def self.meta(hash = nil, &block) - self._meta = - if !block.nil? - block - else - hash - end - end - # @param resource [ActiveRecord::Base, ActiveModelSerializers::Model] # @return [ActiveModel::Serializer] # Preferentially returns @@ -145,14 +130,6 @@ module ActiveModel end end - def meta - if self.class._meta.respond_to?(:call) - instance_eval(&self.class._meta) - else - self.class._meta - end - end - protected attr_accessor :instance_options diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index b72ec6e1..d5ceaa91 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -6,6 +6,7 @@ module ActiveModel autoload :PaginationLinks autoload :FragmentCache autoload :Link + autoload :Meta autoload :Deserialization # TODO: if we like this abstraction and other API objects to it, @@ -157,7 +158,7 @@ module ActiveModel links = links_for(serializer) resource_object[:links] = links if links.any? - meta = serializer.meta + meta = meta_for(serializer) resource_object[:meta] = meta unless meta.nil? resource_object @@ -220,6 +221,10 @@ module ActiveModel def pagination_links_for(serializer, options) JsonApi::PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options) end + + def meta_for(serializer) + Meta.new(serializer).as_json + end end end end diff --git a/lib/active_model/serializer/adapter/json_api/meta.rb b/lib/active_model/serializer/adapter/json_api/meta.rb new file mode 100644 index 00000000..8fba8986 --- /dev/null +++ b/lib/active_model/serializer/adapter/json_api/meta.rb @@ -0,0 +1,29 @@ +module ActiveModel + class Serializer + module Adapter + class JsonApi + class Meta + def initialize(serializer) + @object = serializer.object + @scope = serializer.scope + + # Use the return value of the block unless it is nil. + if serializer._meta.respond_to?(:call) + @value = instance_eval(&serializer._meta) + else + @value = serializer._meta + end + end + + def as_json + @value + end + + protected + + attr_reader :object, :scope + end + end + end + end +end diff --git a/lib/active_model/serializer/meta.rb b/lib/active_model/serializer/meta.rb new file mode 100644 index 00000000..5160585e --- /dev/null +++ b/lib/active_model/serializer/meta.rb @@ -0,0 +1,29 @@ +module ActiveModel + class Serializer + module Meta + extend ActiveSupport::Concern + + included do + with_options instance_writer: false, instance_reader: true do |serializer| + serializer.class_attribute :_meta # @api private + end + + extend ActiveSupport::Autoload + end + + module ClassMethods + # Set the JSON API meta attribute of a serializer. + # @example + # class AdminAuthorSerializer < ActiveModel::Serializer + # meta { stuff: 'value' } + # @example + # meta do + # { comment_count: object.comments.count } + # end + def meta(value = nil, &block) + self._meta = block || value + end + end + end + end +end