mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 22:06:50 +00:00
Merge pull request #1403 from beauby/conditional-attributes
Conditional attributes/associations (if/unless).
This commit is contained in:
commit
53c59a1510
@ -16,6 +16,7 @@ Breaking changes:
|
||||
|
||||
Features:
|
||||
|
||||
- [#1403](https://github.com/rails-api/active_model_serializers/pull/1403) Add support for if/unless on attributes/associations (@beauby)
|
||||
- [#1248](https://github.com/rails-api/active_model_serializers/pull/1248) Experimental: Add support for JSON API deserialization (@beauby)
|
||||
- [#1378](https://github.com/rails-api/active_model_serializers/pull/1378) Change association blocks
|
||||
to be evaluated in *serializer* scope, rather than *association* scope. (@bf4)
|
||||
|
||||
@ -88,6 +88,7 @@ module ActiveModel
|
||||
|
||||
Enumerator.new do |y|
|
||||
self.class._reflections.each do |reflection|
|
||||
next if reflection.excluded?(self)
|
||||
key = reflection.options.fetch(:key, reflection.name)
|
||||
next unless include_tree.key?(key)
|
||||
y.yield reflection.build_association(self, instance_options)
|
||||
|
||||
@ -1,13 +1,25 @@
|
||||
require 'active_model/serializer/field'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
Attribute = Struct.new(:name, :block) do
|
||||
def value(serializer)
|
||||
if block
|
||||
serializer.instance_eval(&block)
|
||||
else
|
||||
serializer.read_attribute_for_serialization(name)
|
||||
end
|
||||
end
|
||||
# Holds all the meta-data about an attribute as it was specified in the
|
||||
# ActiveModel::Serializer class.
|
||||
#
|
||||
# @example
|
||||
# class PostSerializer < ActiveModel::Serializer
|
||||
# attribute :content
|
||||
# attribute :name, key: :title
|
||||
# attribute :email, key: :author_email, if: :user_logged_in?
|
||||
# attribute :preview do
|
||||
# truncate(object.content)
|
||||
# end
|
||||
#
|
||||
# def user_logged_in?
|
||||
# current_user.logged_in?
|
||||
# end
|
||||
# end
|
||||
#
|
||||
class Attribute < Field
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -17,6 +17,7 @@ module ActiveModel
|
||||
def attributes(requested_attrs = nil, reload = false)
|
||||
@attributes = nil if reload
|
||||
@attributes ||= self.class._attributes_data.each_with_object({}) do |(key, attr), hash|
|
||||
next if attr.excluded?(self)
|
||||
next unless requested_attrs.nil? || requested_attrs.include?(key)
|
||||
hash[key] = attr.value(self)
|
||||
end
|
||||
@ -54,7 +55,7 @@ module ActiveModel
|
||||
# end
|
||||
def attribute(attr, options = {}, &block)
|
||||
key = options.fetch(:key, attr)
|
||||
_attributes_data[key] = Attribute.new(attr, block)
|
||||
_attributes_data[key] = Attribute.new(attr, options, block)
|
||||
end
|
||||
|
||||
# @api private
|
||||
|
||||
56
lib/active_model/serializer/field.rb
Normal file
56
lib/active_model/serializer/field.rb
Normal file
@ -0,0 +1,56 @@
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
# Holds all the meta-data about a field (i.e. attribute or association) as it was
|
||||
# specified in the ActiveModel::Serializer class.
|
||||
# Notice that the field block is evaluated in the context of the serializer.
|
||||
Field = Struct.new(:name, :options, :block) do
|
||||
# Compute the actual value of a field for a given serializer instance.
|
||||
# @param [Serializer] The serializer instance for which the value is computed.
|
||||
# @return [Object] value
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def value(serializer)
|
||||
if block
|
||||
serializer.instance_eval(&block)
|
||||
else
|
||||
serializer.read_attribute_for_serialization(name)
|
||||
end
|
||||
end
|
||||
|
||||
# Decide whether the field should be serialized by the given serializer instance.
|
||||
# @param [Serializer] The serializer instance
|
||||
# @return [Bool]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def excluded?(serializer)
|
||||
case condition_type
|
||||
when :if
|
||||
!serializer.public_send(condition)
|
||||
when :unless
|
||||
serializer.public_send(condition)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def condition_type
|
||||
@condition_type ||=
|
||||
if options.key?(:if)
|
||||
:if
|
||||
elsif options.key?(:unless)
|
||||
:unless
|
||||
else
|
||||
:none
|
||||
end
|
||||
end
|
||||
|
||||
def condition
|
||||
options[condition_type]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,18 +1,24 @@
|
||||
require 'active_model/serializer/field'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
# Holds all the meta-data about an association as it was specified in the
|
||||
# ActiveModel::Serializer class.
|
||||
#
|
||||
# @example
|
||||
# class PostSerializer < ActiveModel::Serializer
|
||||
# class PostSerializer < ActiveModel::Serializer
|
||||
# has_one :author, serializer: AuthorSerializer
|
||||
# has_many :comments
|
||||
# has_many :comments, key: :last_comments do
|
||||
# object.comments.last(1)
|
||||
# end
|
||||
# end
|
||||
# has_many :secret_meta_data, if: :is_admin?
|
||||
#
|
||||
# def is_admin?
|
||||
# current_user.admin?
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Notice that the association block is evaluated in the context of the serializer.
|
||||
# Specifically, the association 'comments' is evaluated two different ways:
|
||||
# 1) as 'comments' and named 'comments'.
|
||||
# 2) as 'object.comments.last(1)' and named 'last_comments'.
|
||||
@ -21,20 +27,13 @@ module ActiveModel
|
||||
# # [
|
||||
# # HasOneReflection.new(:author, serializer: AuthorSerializer),
|
||||
# # HasManyReflection.new(:comments)
|
||||
# # HasManyReflection.new(:comments, { key: :last_comments }, #<Block>)
|
||||
# # HasManyReflection.new(:secret_meta_data, { if: :is_admin? })
|
||||
# # ]
|
||||
#
|
||||
# So you can inspect reflections in your Adapters.
|
||||
#
|
||||
Reflection = Struct.new(:name, :options, :block) do
|
||||
# @api private
|
||||
def value(instance)
|
||||
if block
|
||||
instance.instance_eval(&block)
|
||||
else
|
||||
instance.read_attribute_for_serialization(name)
|
||||
end
|
||||
end
|
||||
|
||||
class Reflection < Field
|
||||
# Build association. This method is used internally to
|
||||
# build serializer's association by its reflection.
|
||||
#
|
||||
|
||||
@ -238,6 +238,29 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_conditional_associations
|
||||
serializer = Class.new(ActiveModel::Serializer) do
|
||||
belongs_to :if_assoc_included, if: :true
|
||||
belongs_to :if_assoc_excluded, if: :false
|
||||
belongs_to :unless_assoc_included, unless: :false
|
||||
belongs_to :unless_assoc_excluded, unless: :true
|
||||
|
||||
def true
|
||||
true
|
||||
end
|
||||
|
||||
def false
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
model = ::Model.new
|
||||
hash = serializable(model, serializer: serializer).serializable_hash
|
||||
expected = { if_assoc_included: nil, unless_assoc_included: nil }
|
||||
|
||||
assert_equal(expected, hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -4,7 +4,7 @@ module ActiveModel
|
||||
class Serializer
|
||||
class AttributeTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@blog = Blog.new({ id: 1, name: 'AMS Hints', type: 'stuff' })
|
||||
@blog = Blog.new(id: 1, name: 'AMS Hints', type: 'stuff')
|
||||
@blog_serializer = AlternateBlogSerializer.new(@blog)
|
||||
end
|
||||
|
||||
@ -95,6 +95,29 @@ module ActiveModel
|
||||
|
||||
assert_equal(expected, hash)
|
||||
end
|
||||
|
||||
def test_conditional_attributes
|
||||
serializer = Class.new(ActiveModel::Serializer) do
|
||||
attribute :if_attribute_included, if: :true
|
||||
attribute :if_attribute_excluded, if: :false
|
||||
attribute :unless_attribute_included, unless: :false
|
||||
attribute :unless_attribute_excluded, unless: :true
|
||||
|
||||
def true
|
||||
true
|
||||
end
|
||||
|
||||
def false
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
model = ::Model.new
|
||||
hash = serializable(model, serializer: serializer).serializable_hash
|
||||
expected = { if_attribute_included: nil, unless_attribute_included: nil }
|
||||
|
||||
assert_equal(expected, hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user