-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy pathconditional_fields.rb
More file actions
126 lines (110 loc) · 3.68 KB
/
conditional_fields.rb
File metadata and controls
126 lines (110 loc) · 3.68 KB
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
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.module_eval do
prepend 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
_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
_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
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