-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathconditional_fields.rb
133 lines (120 loc) · 4.07 KB
/
conditional_fields.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
module JSONAPI
module Serializable
class Resource
# Extension for handling conditional fields in serializable resources.
#
# @usage
# class SerializableUser < JSONAPI::Serializable::Resource
# extend JSONAPI::Serializable::Resource::ConditionalFields
#
# attribute :email, if: -> { @current_user.admin? }
# has_many :friends, unless: -> { @object.private_profile? }
# end
#
module ConditionalFields
def self.prepended(klass)
warn <<-EOT
DERPRECATION WARNING (called from #{caller_locations(1...2).first}):
Prepending `#{name}' is deprecated and will be removed in future releases. Use `Object#extend' instead.
EOT
klass.extend self
end
def self.extended(klass)
klass.class_eval do
include InstanceMethods
class << self
attr_accessor :field_condition_blocks
attr_accessor :link_condition_blocks
end
self.field_condition_blocks ||= {}
self.link_condition_blocks ||= {}
end
end
def inherited(klass)
super
klass.field_condition_blocks = field_condition_blocks.dup
klass.link_condition_blocks = link_condition_blocks.dup
end
# Handle the `if` and `unless` options for attributes.
#
# @example
# attribute :email, if: -> { @current_user.admin? }
#
def attribute(name, options = {}, &block)
super
name = _format_key_for_condition(name)
_register_condition(field_condition_blocks, name, options)
end
# Handle the `if` and `unless` options for relationships (has_one,
# belongs_to, has_many).
#
# @example
# has_many :friends, unless: -> { @object.private_profile? }
#
def relationship(name, options = {}, &block)
super
name = _format_key_for_condition(name)
_register_condition(field_condition_blocks, name, options)
end
# Handle the `if` and `unless` options for links.
#
# @example
#
# link :self, if: -> { @object.render_self_link? } do
# "..."
# end
def link(name, options = {}, &block)
super(name, &block)
_register_condition(link_condition_blocks, name, options)
end
# NOTE(beauby): Re-aliasing those is necessary for the
# overridden `#relationship` method to be called.
alias has_many relationship
alias has_one relationship
alias belongs_to relationship
# @api private
def _register_condition(condition_blocks, name, options)
condition_blocks[name.to_sym] =
if options.key?(:if)
options[:if]
elsif options.key?(:unless)
proc { !instance_exec(&options[:unless]) }
end
end
def _format_key_for_condition(name)
ancestors = self.singleton_class.included_modules
if ancestors.include?(KeyFormat) && ancestors.index(KeyFormat) > ancestors.index(ConditionalFields)
_key_formatter.call(name)
else
name
end
end
end
module InstanceMethods
# @api private
def requested_attributes(fields)
super.select do |k, _|
_conditionally_included?(self.class.field_condition_blocks, k)
end
end
# @api private
def requested_relationships(fields)
super.select do |k, _|
_conditionally_included?(self.class.field_condition_blocks, k)
end
end
# @api private
def link_blocks
super.select do |k, _|
_conditionally_included?(self.class.link_condition_blocks, k)
end
end
# @api private
def _conditionally_included?(condition_blocks, field)
condition = condition_blocks[field]
condition.nil? || instance_exec(&condition)
end
end
end
end
end