Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for resource-level meta. #1340

Merged
merged 3 commits into from
Feb 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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
Expand All @@ -20,6 +21,7 @@ class Serializer
include Attributes
include Caching
include Links
include Meta
include Type
require 'active_model/serializer/adapter'

Expand Down
8 changes: 8 additions & 0 deletions lib/active_model/serializer/adapter/json_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class JsonApi < Base
autoload :PaginationLinks
autoload :FragmentCache
autoload :Link
autoload :Meta
autoload :Deserialization

# TODO: if we like this abstraction and other API objects to it,
Expand Down Expand Up @@ -157,6 +158,9 @@ def resource_object_for(serializer)
links = links_for(serializer)
resource_object[:links] = links if links.any?

meta = meta_for(serializer)
resource_object[:meta] = meta unless meta.nil?

resource_object
end

Expand Down Expand Up @@ -217,6 +221,10 @@ def links_for(serializer)
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
Expand Down
29 changes: 29 additions & 0 deletions lib/active_model/serializer/adapter/json_api/meta.rb
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions lib/active_model/serializer/meta.rb
Original file line number Diff line number Diff line change
@@ -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
63 changes: 63 additions & 0 deletions test/adapter/json_api/resource_meta_test.rb
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, there's a test support helper serializable but it wouldn't make things much shorter. Do you think we should test Ruby hashes made from as_json instead of serializable_hash in the future?

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
16 changes: 16 additions & 0 deletions test/serializers/meta_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
module ActiveModel
class Serializer
class MetaTest < ActiveSupport::TestCase
MetaBlogSerializer = Class.new(ActiveModel::Serializer)

def setup
@blog = Blog.new(id: 1,
name: 'AMS Hints',
Expand Down Expand Up @@ -125,6 +127,20 @@ def test_meta_is_present_on_arrays_with_root
}
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to validate that meta creates valid JSON API objects. Do you think this PR is a good spot for that?

end
end
end