jsonapi_compliable 0.11.34 → 1.0.alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +1 -2
- data/Rakefile +7 -3
- data/jsonapi_compliable.gemspec +7 -3
- data/lib/generators/jsonapi/resource_generator.rb +8 -79
- data/lib/generators/jsonapi/templates/application_resource.rb.erb +2 -1
- data/lib/generators/jsonapi/templates/controller.rb.erb +19 -64
- data/lib/generators/jsonapi/templates/resource.rb.erb +5 -47
- data/lib/generators/jsonapi/templates/resource_reads_spec.rb.erb +62 -0
- data/lib/generators/jsonapi/templates/resource_writes_spec.rb.erb +63 -0
- data/lib/jsonapi_compliable.rb +87 -18
- data/lib/jsonapi_compliable/adapters/abstract.rb +202 -45
- data/lib/jsonapi_compliable/adapters/active_record.rb +6 -130
- data/lib/jsonapi_compliable/adapters/active_record/base.rb +247 -0
- data/lib/jsonapi_compliable/adapters/active_record/belongs_to_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/has_many_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/has_one_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/inferrence.rb +12 -0
- data/lib/jsonapi_compliable/adapters/active_record/many_to_many_sideload.rb +30 -0
- data/lib/jsonapi_compliable/adapters/null.rb +177 -6
- data/lib/jsonapi_compliable/base.rb +33 -320
- data/lib/jsonapi_compliable/context.rb +16 -0
- data/lib/jsonapi_compliable/deserializer.rb +14 -39
- data/lib/jsonapi_compliable/errors.rb +227 -24
- data/lib/jsonapi_compliable/extensions/extra_attribute.rb +3 -1
- data/lib/jsonapi_compliable/filter_operators.rb +25 -0
- data/lib/jsonapi_compliable/hash_renderer.rb +57 -0
- data/lib/jsonapi_compliable/query.rb +190 -202
- data/lib/jsonapi_compliable/rails.rb +12 -6
- data/lib/jsonapi_compliable/railtie.rb +64 -0
- data/lib/jsonapi_compliable/renderer.rb +60 -0
- data/lib/jsonapi_compliable/resource.rb +35 -663
- data/lib/jsonapi_compliable/resource/configuration.rb +239 -0
- data/lib/jsonapi_compliable/resource/dsl.rb +138 -0
- data/lib/jsonapi_compliable/resource/interface.rb +32 -0
- data/lib/jsonapi_compliable/resource/polymorphism.rb +68 -0
- data/lib/jsonapi_compliable/resource/sideloading.rb +102 -0
- data/lib/jsonapi_compliable/resource_proxy.rb +127 -0
- data/lib/jsonapi_compliable/responders.rb +19 -0
- data/lib/jsonapi_compliable/runner.rb +25 -0
- data/lib/jsonapi_compliable/scope.rb +37 -79
- data/lib/jsonapi_compliable/scoping/extra_attributes.rb +29 -0
- data/lib/jsonapi_compliable/scoping/filter.rb +39 -58
- data/lib/jsonapi_compliable/scoping/filterable.rb +9 -14
- data/lib/jsonapi_compliable/scoping/paginate.rb +9 -3
- data/lib/jsonapi_compliable/scoping/sort.rb +16 -4
- data/lib/jsonapi_compliable/sideload.rb +221 -347
- data/lib/jsonapi_compliable/sideload/belongs_to.rb +34 -0
- data/lib/jsonapi_compliable/sideload/has_many.rb +16 -0
- data/lib/jsonapi_compliable/sideload/has_one.rb +9 -0
- data/lib/jsonapi_compliable/sideload/many_to_many.rb +24 -0
- data/lib/jsonapi_compliable/sideload/polymorphic_belongs_to.rb +108 -0
- data/lib/jsonapi_compliable/stats/payload.rb +4 -8
- data/lib/jsonapi_compliable/types.rb +172 -0
- data/lib/jsonapi_compliable/util/attribute_check.rb +88 -0
- data/lib/jsonapi_compliable/util/persistence.rb +29 -7
- data/lib/jsonapi_compliable/util/relationship_payload.rb +4 -4
- data/lib/jsonapi_compliable/util/render_options.rb +4 -32
- data/lib/jsonapi_compliable/util/serializer_attributes.rb +98 -0
- data/lib/jsonapi_compliable/util/validation_response.rb +15 -9
- data/lib/jsonapi_compliable/version.rb +1 -1
- metadata +105 -24
- data/lib/generators/jsonapi/field_generator.rb +0 -0
- data/lib/generators/jsonapi/templates/create_request_spec.rb.erb +0 -29
- data/lib/generators/jsonapi/templates/destroy_request_spec.rb.erb +0 -20
- data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
- data/lib/generators/jsonapi/templates/payload.rb.erb +0 -39
- data/lib/generators/jsonapi/templates/serializer.rb.erb +0 -25
- data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -19
- data/lib/generators/jsonapi/templates/update_request_spec.rb.erb +0 -33
- data/lib/jsonapi_compliable/adapters/active_record_sideloading.rb +0 -152
- data/lib/jsonapi_compliable/scoping/extra_fields.rb +0 -58
@@ -0,0 +1,34 @@
|
|
1
|
+
class JsonapiCompliable::Sideload::BelongsTo < JsonapiCompliable::Sideload
|
2
|
+
def type
|
3
|
+
:belongs_to
|
4
|
+
end
|
5
|
+
|
6
|
+
def load_params(parents, query)
|
7
|
+
query.to_hash.tap do |hash|
|
8
|
+
hash[:filter] ||= {}
|
9
|
+
hash[:filter][primary_key] = ids_for_parents(parents)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def assign_each(parent, children)
|
14
|
+
children.find { |c| c.send(primary_key) == parent.send(foreign_key) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def ids_for_parents(parents)
|
18
|
+
parent_ids = parents.map(&foreign_key)
|
19
|
+
parent_ids.compact!
|
20
|
+
parent_ids.uniq!
|
21
|
+
parent_ids
|
22
|
+
end
|
23
|
+
|
24
|
+
def infer_foreign_key
|
25
|
+
if polymorphic_child?
|
26
|
+
parent.foreign_key
|
27
|
+
else
|
28
|
+
model = resource.model
|
29
|
+
namespace = namespace_for(model)
|
30
|
+
model_name = model.name.gsub("#{namespace}::", '')
|
31
|
+
:"#{model_name.underscore}_id"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class JsonapiCompliable::Sideload::HasMany < JsonapiCompliable::Sideload
|
2
|
+
def type
|
3
|
+
:has_many
|
4
|
+
end
|
5
|
+
|
6
|
+
def load_params(parents, query)
|
7
|
+
query.to_hash.tap do |hash|
|
8
|
+
hash[:filter] ||= {}
|
9
|
+
hash[:filter][foreign_key] = ids_for_parents(parents)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def assign_each(parent, children)
|
14
|
+
children.select { |c| c.send(foreign_key) == parent.send(primary_key) }
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class JsonapiCompliable::Sideload::ManyToMany < JsonapiCompliable::Sideload
|
2
|
+
def type
|
3
|
+
:many_to_many
|
4
|
+
end
|
5
|
+
|
6
|
+
def through
|
7
|
+
foreign_key.keys.first
|
8
|
+
end
|
9
|
+
|
10
|
+
def true_foreign_key
|
11
|
+
foreign_key.values.first
|
12
|
+
end
|
13
|
+
|
14
|
+
def infer_foreign_key
|
15
|
+
raise 'You must explicitly pass :foreign_key for many-to-many relaitonships, or override in subclass to return a hash.'
|
16
|
+
end
|
17
|
+
|
18
|
+
def assign_each(parent, children)
|
19
|
+
children.select do |c|
|
20
|
+
match = ->(ct) { ct.send(true_foreign_key) == parent.send(primary_key) }
|
21
|
+
c.send(through).any?(&match)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
class JsonapiCompliable::Sideload::PolymorphicBelongsTo < JsonapiCompliable::Sideload::BelongsTo
|
2
|
+
class Group
|
3
|
+
attr_reader :name, :calls
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
@name = name
|
7
|
+
@calls = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(name, *args, &blk)
|
11
|
+
@calls << [name, args, blk]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Grouper
|
16
|
+
attr_reader :column_name
|
17
|
+
|
18
|
+
def initialize(column_name)
|
19
|
+
@column_name = column_name
|
20
|
+
@groups = []
|
21
|
+
end
|
22
|
+
|
23
|
+
def on(name, &blk)
|
24
|
+
group = Group.new(name)
|
25
|
+
@groups << group
|
26
|
+
group.belongs_to(name.to_s.underscore.to_sym)
|
27
|
+
group
|
28
|
+
end
|
29
|
+
|
30
|
+
def apply(sideload, resource_class)
|
31
|
+
@groups.each do |group|
|
32
|
+
group.calls.each do |call|
|
33
|
+
args = call[1]
|
34
|
+
opts = args.extract_options!
|
35
|
+
opts.merge! as: sideload.name,
|
36
|
+
parent: sideload,
|
37
|
+
group_name: group.name,
|
38
|
+
polymorphic_child: true
|
39
|
+
if !sideload.resource.class.abstract_class?
|
40
|
+
opts[:foreign_key] ||= sideload.foreign_key
|
41
|
+
opts[:primary_key] ||= sideload.primary_key
|
42
|
+
end
|
43
|
+
args << opts
|
44
|
+
resource_class.send(call[0], *args, &call[2])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class_attribute :grouper
|
51
|
+
attr_accessor :children
|
52
|
+
self.grouper = Grouper.new(:default)
|
53
|
+
|
54
|
+
def type
|
55
|
+
:polymorphic_belongs_to
|
56
|
+
end
|
57
|
+
|
58
|
+
def infer_foreign_key
|
59
|
+
:"#{name}_id"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.group_by(name, &blk)
|
63
|
+
self.grouper = Grouper.new(name)
|
64
|
+
self.grouper.instance_eval(&blk)
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(name, opts)
|
68
|
+
super
|
69
|
+
self.children = {}
|
70
|
+
grouper.apply(self, parent_resource_class)
|
71
|
+
end
|
72
|
+
|
73
|
+
def child_for_type(type)
|
74
|
+
children.values.find do |sideload|
|
75
|
+
sideload.resource.type == type
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def resolve(parents, query)
|
80
|
+
parents.group_by(&grouper.column_name).each_pair do |group_name, group|
|
81
|
+
next if group_name.nil?
|
82
|
+
|
83
|
+
match = ->(name, sl) { sl.group_name == group_name.to_sym }
|
84
|
+
if child = children.find(&match)
|
85
|
+
sideload = child[1]
|
86
|
+
query = remove_invalid_sideloads(sideload.resource, query)
|
87
|
+
sideload.resolve(group, query)
|
88
|
+
else
|
89
|
+
err = ::JsonapiCompliable::Errors::PolymorphicChildNotFound
|
90
|
+
raise err.new(self, group_name)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# We may be requesting a relationship that some subclasses support,
|
98
|
+
# but not others. Remove anything we don't support.
|
99
|
+
def remove_invalid_sideloads(resource, query)
|
100
|
+
query = query.dup
|
101
|
+
query.sideloads.each_pair do |key, value|
|
102
|
+
unless resource.class.sideload(key)
|
103
|
+
query.sideloads.delete(key)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
query
|
107
|
+
end
|
108
|
+
end
|
@@ -14,12 +14,9 @@ module JsonapiCompliable
|
|
14
14
|
# meta: { stats: { total: { count: 100 } } }
|
15
15
|
# }
|
16
16
|
class Payload
|
17
|
-
|
18
|
-
# @param [Hash] query_hash the Query#to_hash for the current resource
|
19
|
-
# @param scope the scope we are chaining/modifying
|
20
|
-
def initialize(resource, query_hash, scope)
|
17
|
+
def initialize(resource, query, scope)
|
21
18
|
@resource = resource
|
22
|
-
@
|
19
|
+
@query = query
|
23
20
|
@scope = scope
|
24
21
|
end
|
25
22
|
|
@@ -29,12 +26,11 @@ module JsonapiCompliable
|
|
29
26
|
# @return [Hash] the generated payload
|
30
27
|
def generate
|
31
28
|
{}.tap do |stats|
|
32
|
-
@
|
29
|
+
@query.stats.each_pair do |name, calculation|
|
33
30
|
stats[name] = {}
|
34
31
|
|
35
32
|
each_calculation(name, calculation) do |calc, function|
|
36
|
-
|
37
|
-
stats[name][calc] = function.call(*args)
|
33
|
+
stats[name][calc] = function.call(@scope, name)
|
38
34
|
end
|
39
35
|
end
|
40
36
|
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
class Types
|
3
|
+
def self.create(primitive, &blk)
|
4
|
+
definition = Dry::Types::Definition.new(primitive)
|
5
|
+
definition.constructor(&blk)
|
6
|
+
end
|
7
|
+
|
8
|
+
WriteDateTime = create(::DateTime) do |input|
|
9
|
+
if input.is_a?(::Date) || input.is_a?(::Time)
|
10
|
+
input = ::DateTime.parse(input.to_s)
|
11
|
+
end
|
12
|
+
input = Dry::Types['json.date_time'][input]
|
13
|
+
Dry::Types['strict.date_time'][input] if input
|
14
|
+
end
|
15
|
+
|
16
|
+
ReadDateTime = create(::DateTime) do |input|
|
17
|
+
if input.is_a?(::Date) || input.is_a?(::Time)
|
18
|
+
input = ::DateTime.parse(input.to_s)
|
19
|
+
end
|
20
|
+
input = Dry::Types['json.date_time'][input]
|
21
|
+
Dry::Types['strict.date_time'][input].iso8601 if input
|
22
|
+
end
|
23
|
+
|
24
|
+
PresentParamsDateTime = create(::DateTime) do |input|
|
25
|
+
input = Dry::Types['params.date_time'][input]
|
26
|
+
Dry::Types['strict.date_time'][input]
|
27
|
+
end
|
28
|
+
|
29
|
+
Date = create(::Date) do |input|
|
30
|
+
input = ::Date.parse(input.to_s) if input.is_a?(::Time)
|
31
|
+
input = Dry::Types['json.date'][input]
|
32
|
+
Dry::Types['strict.date'][input] if input
|
33
|
+
end
|
34
|
+
|
35
|
+
PresentDate = create(::Date) do |input|
|
36
|
+
input = ::Date.parse(input.to_s) if input.is_a?(::Time)
|
37
|
+
input = Dry::Types['json.date'][input]
|
38
|
+
Dry::Types['strict.date'][input]
|
39
|
+
end
|
40
|
+
|
41
|
+
Bool = create(nil) do |input|
|
42
|
+
input = Dry::Types['params.bool'][input]
|
43
|
+
Dry::Types['strict.bool'][input] if input
|
44
|
+
end
|
45
|
+
|
46
|
+
PresentBool = create(nil) do |input|
|
47
|
+
input = Dry::Types['params.bool'][input]
|
48
|
+
Dry::Types['strict.bool'][input]
|
49
|
+
end
|
50
|
+
|
51
|
+
Integer = create(::Integer) do |input|
|
52
|
+
Dry::Types['coercible.integer'][input] if input
|
53
|
+
end
|
54
|
+
|
55
|
+
# The Float() check here is to ensure we have a number
|
56
|
+
# Otherwise BigDecimal('foo') *will return a decima;*
|
57
|
+
ParamDecimal = create(::BigDecimal) do |input|
|
58
|
+
Float(input)
|
59
|
+
input = Dry::Types['coercible.decimal'][input]
|
60
|
+
Dry::Types['strict.decimal'][input]
|
61
|
+
end
|
62
|
+
|
63
|
+
PresentInteger = create(::Integer) do |input|
|
64
|
+
Dry::Types['coercible.integer'][input]
|
65
|
+
end
|
66
|
+
|
67
|
+
Float = create(::Float) do |input|
|
68
|
+
Dry::Types['coercible.float'][input] if input
|
69
|
+
end
|
70
|
+
|
71
|
+
PresentParamsHash = create(::Hash) do |input|
|
72
|
+
Dry::Types['params.hash'][input].deep_symbolize_keys
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.map
|
76
|
+
@map ||= begin
|
77
|
+
hash = {
|
78
|
+
integer_id: {
|
79
|
+
canonical_name: :integer,
|
80
|
+
params: Dry::Types['coercible.integer'],
|
81
|
+
read: Dry::Types['coercible.string'],
|
82
|
+
write: Dry::Types['coercible.integer']
|
83
|
+
},
|
84
|
+
string: {
|
85
|
+
params: Dry::Types['coercible.string'],
|
86
|
+
read: Dry::Types['coercible.string'],
|
87
|
+
write: Dry::Types['coercible.string']
|
88
|
+
},
|
89
|
+
integer: {
|
90
|
+
params: PresentInteger,
|
91
|
+
read: Integer,
|
92
|
+
write: Integer
|
93
|
+
},
|
94
|
+
decimal: {
|
95
|
+
params: ParamDecimal,
|
96
|
+
read: Dry::Types['json.decimal'],
|
97
|
+
write: Dry::Types['json.decimal']
|
98
|
+
},
|
99
|
+
float: {
|
100
|
+
params: Dry::Types['coercible.float'],
|
101
|
+
read: Float,
|
102
|
+
write: Float
|
103
|
+
},
|
104
|
+
boolean: {
|
105
|
+
params: PresentBool,
|
106
|
+
read: Bool,
|
107
|
+
write: Bool
|
108
|
+
},
|
109
|
+
date: {
|
110
|
+
params: PresentDate,
|
111
|
+
read: Date,
|
112
|
+
write: Date
|
113
|
+
},
|
114
|
+
datetime: {
|
115
|
+
params: PresentParamsDateTime,
|
116
|
+
read: ReadDateTime,
|
117
|
+
write: WriteDateTime
|
118
|
+
},
|
119
|
+
hash: {
|
120
|
+
params: PresentParamsHash,
|
121
|
+
read: Dry::Types['strict.hash'],
|
122
|
+
write: Dry::Types['strict.hash']
|
123
|
+
},
|
124
|
+
array: {
|
125
|
+
params: Dry::Types['strict.array'],
|
126
|
+
read: Dry::Types['strict.array'],
|
127
|
+
write: Dry::Types['strict.array']
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
hash.each_pair do |k, v|
|
132
|
+
hash[k][:canonical_name] ||= k
|
133
|
+
end
|
134
|
+
|
135
|
+
arrays = {}
|
136
|
+
hash.each_pair do |name, map|
|
137
|
+
arrays[:"array_of_#{name.to_s.pluralize}"] = {
|
138
|
+
canonical_name: name,
|
139
|
+
params: Dry::Types['strict.array'].of(map[:params]),
|
140
|
+
read: Dry::Types['strict.array'].of(map[:read]),
|
141
|
+
test: Dry::Types['strict.array'].of(map[:test]),
|
142
|
+
write: Dry::Types['strict.array'].of(map[:write])
|
143
|
+
}
|
144
|
+
end
|
145
|
+
hash.merge!(arrays)
|
146
|
+
|
147
|
+
hash
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.[](key)
|
152
|
+
map[key.to_sym]
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.[]=(key, value)
|
156
|
+
unless value.is_a?(Hash)
|
157
|
+
value = {
|
158
|
+
read: value,
|
159
|
+
params: value,
|
160
|
+
test: value
|
161
|
+
}
|
162
|
+
end
|
163
|
+
map[key.to_sym] = value
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.name_for(key)
|
167
|
+
key = key.to_sym
|
168
|
+
type = map[key]
|
169
|
+
type[:canonical_name]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Private, tested in resource specs
|
2
|
+
module JsonapiCompliable
|
3
|
+
module Util
|
4
|
+
class AttributeCheck
|
5
|
+
attr_reader :resource, :name, :flag, :request, :raise_error
|
6
|
+
|
7
|
+
def self.run(resource, name, flag, request, raise_error)
|
8
|
+
new(resource, name, flag, request, raise_error).run
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(resource, name, flag, request, raise_error)
|
12
|
+
@resource = resource
|
13
|
+
@name = name.to_sym
|
14
|
+
@flag = flag
|
15
|
+
@request = request
|
16
|
+
@raise_error = raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
if attribute?
|
21
|
+
if supported?
|
22
|
+
if guarded?
|
23
|
+
if guard_passes?
|
24
|
+
attribute
|
25
|
+
else
|
26
|
+
maybe_raise(request: true, guard: attribute[flag])
|
27
|
+
end
|
28
|
+
else
|
29
|
+
attribute
|
30
|
+
end
|
31
|
+
else
|
32
|
+
maybe_raise(exists: true)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
maybe_raise(exists: false)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def maybe_raise(opts = {})
|
40
|
+
default = { request: request, exists: true }
|
41
|
+
opts = default.merge(opts)
|
42
|
+
if raise_error?(opts[:exists])
|
43
|
+
raise error_class.new(resource, name, flag, opts)
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def guard_passes?
|
50
|
+
!!resource.send(attribute[flag])
|
51
|
+
end
|
52
|
+
|
53
|
+
def guarded?
|
54
|
+
request? &&
|
55
|
+
attribute[flag].is_a?(Symbol) &&
|
56
|
+
attribute[flag] != :required
|
57
|
+
end
|
58
|
+
|
59
|
+
def error_class
|
60
|
+
Errors::AttributeError
|
61
|
+
end
|
62
|
+
|
63
|
+
def supported?
|
64
|
+
attribute[flag] != false
|
65
|
+
end
|
66
|
+
|
67
|
+
def attribute
|
68
|
+
resource.all_attributes[name]
|
69
|
+
end
|
70
|
+
|
71
|
+
def attribute?
|
72
|
+
!!attribute
|
73
|
+
end
|
74
|
+
|
75
|
+
def raise_error?(exists)
|
76
|
+
if raise_error == :only_unsupported
|
77
|
+
exists ? true : false
|
78
|
+
else
|
79
|
+
!!raise_error
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def request?
|
84
|
+
!!request
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|