-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Conditional attributes/associations (if/unless) #1403
Changes from all commits
a502b5d
6860318
40ed7b5
7fbf7e5
7af1986
2696557
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kept it as a struct because tests in |
||
# 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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like how you slipped the scope in there :) ref: #1252 |
||
# 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 @@ class Serializer | |
# # [ | ||
# # 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. | ||
# | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 @@ def test_virtual_attribute_block | |
|
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should look into if rubocop has a linter for parens around assert.. hashtag lazy comment |
||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grammar:
s/i.e./e.g.
reason: meaning is to change 'that is' to 'for example'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually meant i.e., because I'm just defining what "field" means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still think it should be e.g., but since it's intentional on your part and it's super unimportant, ok to merge when green
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that it's not super important, but as I like grammar as well: a field is defined as "either an attribute or an association".