grape 0.16.2 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +4 -0
- data/CHANGELOG.md +54 -27
- data/Dangerfile +80 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +61 -27
- data/README.md +135 -7
- data/Rakefile +34 -30
- data/UPGRADING.md +21 -0
- data/gemfiles/rack_1.5.2.gemfile +21 -0
- data/gemfiles/rails_3.gemfile +22 -1
- data/gemfiles/rails_4.gemfile +21 -0
- data/gemfiles/rails_5.gemfile +34 -0
- data/grape.gemspec +0 -14
- data/lib/grape.rb +2 -0
- data/lib/grape/api.rb +9 -2
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +15 -17
- data/lib/grape/dsl/middleware.rb +15 -1
- data/lib/grape/dsl/parameters.rb +16 -14
- data/lib/grape/dsl/request_response.rb +24 -20
- data/lib/grape/dsl/routing.rb +11 -10
- data/lib/grape/dsl/settings.rb +16 -0
- data/lib/grape/endpoint.rb +77 -60
- data/lib/grape/exceptions/validation.rb +5 -2
- data/lib/grape/exceptions/validation_array_errors.rb +11 -0
- data/lib/grape/formatter/xml.rb +1 -1
- data/lib/grape/middleware/error.rb +34 -25
- data/lib/grape/middleware/formatter.rb +9 -9
- data/lib/grape/middleware/stack.rb +110 -0
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/path.rb +10 -2
- data/lib/grape/request.rb +1 -1
- data/lib/grape/router.rb +10 -19
- data/lib/grape/router/pattern.rb +2 -2
- data/lib/grape/router/route.rb +3 -3
- data/lib/grape/util/content_types.rb +1 -1
- data/lib/grape/util/inheritable_setting.rb +7 -2
- data/lib/grape/util/reverse_stackable_values.rb +45 -0
- data/lib/grape/util/stackable_values.rb +10 -11
- data/lib/grape/validations/attributes_iterator.rb +32 -7
- data/lib/grape/validations/params_scope.rb +33 -21
- data/lib/grape/validations/types.rb +4 -4
- data/lib/grape/validations/types/build_coercer.rb +9 -1
- data/lib/grape/validations/validators/all_or_none.rb +2 -2
- data/lib/grape/validations/validators/allow_blank.rb +10 -11
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
- data/lib/grape/validations/validators/base.rb +16 -6
- data/lib/grape/validations/validators/coerce.rb +3 -6
- data/lib/grape/validations/validators/default.rb +26 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +1 -1
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
- data/lib/grape/validations/validators/presence.rb +1 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/validations/validators/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +3 -3
- data/spec/grape/api/parameters_modification_spec.rb +41 -0
- data/spec/grape/api_spec.rb +335 -108
- data/spec/grape/dsl/logger_spec.rb +1 -1
- data/spec/grape/dsl/middleware_spec.rb +25 -5
- data/spec/grape/dsl/request_response_spec.rb +20 -6
- data/spec/grape/dsl/validations_spec.rb +1 -1
- data/spec/grape/endpoint_spec.rb +166 -23
- data/spec/grape/entity_spec.rb +0 -2
- data/spec/grape/exceptions/body_parse_errors_spec.rb +37 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
- data/spec/grape/exceptions/validation_spec.rb +10 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +1 -1
- data/spec/grape/integration/rack_spec.rb +1 -1
- data/spec/grape/middleware/base_spec.rb +1 -1
- data/spec/grape/middleware/exception_spec.rb +2 -2
- data/spec/grape/middleware/formatter_spec.rb +4 -4
- data/spec/grape/middleware/stack_spec.rb +123 -0
- data/spec/grape/middleware/versioner/header_spec.rb +6 -6
- data/spec/grape/request_spec.rb +22 -22
- data/spec/grape/util/inheritable_setting_spec.rb +23 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +131 -0
- data/spec/grape/validations/params_scope_spec.rb +88 -1
- data/spec/grape/validations/validators/allow_blank_spec.rb +5 -0
- data/spec/grape/validations/validators/coerce_spec.rb +5 -5
- data/spec/grape/validations/validators/default_spec.rb +44 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +36 -17
- data/spec/spec_helper.rb +1 -8
- data/spec/support/versioned_helpers.rb +3 -3
- metadata +13 -188
- data/gemfiles/rails_3.gemfile.lock +0 -225
- data/pkg/grape-0.16.1.gem +0 -0
- data/pkg/patch.diff +0 -24
- data/tmp/Gemfile.lock +0 -63
data/lib/grape/router/route.rb
CHANGED
@@ -6,13 +6,13 @@ require 'pathname'
|
|
6
6
|
module Grape
|
7
7
|
class Router
|
8
8
|
class Route
|
9
|
-
ROUTE_ATTRIBUTE_REGEXP = /route_([_a-zA-Z]\w*)
|
10
|
-
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')
|
9
|
+
ROUTE_ATTRIBUTE_REGEXP = /route_([_a-zA-Z]\w*)/
|
10
|
+
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')?$/
|
11
11
|
FIXED_NAMED_CAPTURES = %w(format version).freeze
|
12
12
|
|
13
13
|
attr_accessor :pattern, :translator, :app, :index, :regexp, :options
|
14
14
|
|
15
|
-
|
15
|
+
alias attributes translator
|
16
16
|
|
17
17
|
extend Forwardable
|
18
18
|
def_delegators :pattern, :path, :origin
|
@@ -3,7 +3,8 @@ module Grape
|
|
3
3
|
# A branchable, inheritable settings object which can store both stackable
|
4
4
|
# and inheritable values (see InheritableValues and StackableValues).
|
5
5
|
class InheritableSetting
|
6
|
-
attr_accessor :route, :api_class, :namespace
|
6
|
+
attr_accessor :route, :api_class, :namespace
|
7
|
+
attr_accessor :namespace_inheritable, :namespace_stackable, :namespace_reverse_stackable
|
7
8
|
attr_accessor :parent, :point_in_time_copies
|
8
9
|
|
9
10
|
# Retrieve global settings.
|
@@ -28,6 +29,7 @@ module Grape
|
|
28
29
|
# used with a mount, or should every API::Class be a separate namespace by default?
|
29
30
|
self.namespace_inheritable = InheritableValues.new
|
30
31
|
self.namespace_stackable = StackableValues.new
|
32
|
+
self.namespace_reverse_stackable = ReverseStackableValues.new
|
31
33
|
|
32
34
|
self.point_in_time_copies = []
|
33
35
|
|
@@ -50,6 +52,7 @@ module Grape
|
|
50
52
|
|
51
53
|
namespace_inheritable.inherited_values = parent.namespace_inheritable
|
52
54
|
namespace_stackable.inherited_values = parent.namespace_stackable
|
55
|
+
namespace_reverse_stackable.inherited_values = parent.namespace_reverse_stackable
|
53
56
|
self.route = parent.route.merge(route)
|
54
57
|
|
55
58
|
point_in_time_copies.map { |cloned_one| cloned_one.inherit_from parent }
|
@@ -67,6 +70,7 @@ module Grape
|
|
67
70
|
new_setting.namespace = namespace.clone
|
68
71
|
new_setting.namespace_inheritable = namespace_inheritable.clone
|
69
72
|
new_setting.namespace_stackable = namespace_stackable.clone
|
73
|
+
new_setting.namespace_reverse_stackable = namespace_reverse_stackable.clone
|
70
74
|
new_setting.route = route.clone
|
71
75
|
new_setting.api_class = api_class
|
72
76
|
|
@@ -87,7 +91,8 @@ module Grape
|
|
87
91
|
route: route.clone,
|
88
92
|
namespace: namespace.to_hash,
|
89
93
|
namespace_inheritable: namespace_inheritable.to_hash,
|
90
|
-
namespace_stackable: namespace_stackable.to_hash
|
94
|
+
namespace_stackable: namespace_stackable.to_hash,
|
95
|
+
namespace_reverse_stackable: namespace_reverse_stackable.to_hash
|
91
96
|
}
|
92
97
|
end
|
93
98
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Grape
|
2
|
+
module Util
|
3
|
+
class ReverseStackableValues
|
4
|
+
attr_accessor :inherited_values
|
5
|
+
attr_accessor :new_values
|
6
|
+
|
7
|
+
def initialize(inherited_values = {})
|
8
|
+
@inherited_values = inherited_values
|
9
|
+
@new_values = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](name)
|
13
|
+
[].tap do |value|
|
14
|
+
value.concat(@new_values[name] || [])
|
15
|
+
value.concat(@inherited_values[name] || [])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(name, value)
|
20
|
+
@new_values[name] ||= []
|
21
|
+
@new_values[name].push value
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(key)
|
25
|
+
new_values.delete key
|
26
|
+
end
|
27
|
+
|
28
|
+
def keys
|
29
|
+
(@new_values.keys + @inherited_values.keys).sort.uniq
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_hash
|
33
|
+
keys.each_with_object({}) do |key, result|
|
34
|
+
result[key] = self[key]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize_copy(other)
|
39
|
+
super
|
40
|
+
self.inherited_values = other.inherited_values
|
41
|
+
self.new_values = other.new_values.dup
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -2,25 +2,26 @@ module Grape
|
|
2
2
|
module Util
|
3
3
|
class StackableValues
|
4
4
|
attr_accessor :inherited_values
|
5
|
-
|
6
|
-
attr_reader :
|
5
|
+
attr_accessor :new_values
|
6
|
+
attr_reader :frozen_values
|
7
7
|
|
8
8
|
def initialize(inherited_values = {})
|
9
9
|
@inherited_values = inherited_values
|
10
10
|
@new_values = {}
|
11
|
-
@
|
11
|
+
@frozen_values = {}
|
12
12
|
end
|
13
13
|
|
14
14
|
def [](name)
|
15
|
-
return @
|
16
|
-
|
17
|
-
value
|
18
|
-
value.
|
15
|
+
return @frozen_values[name] if @frozen_values.key? name
|
16
|
+
|
17
|
+
value = []
|
18
|
+
value.concat(@inherited_values[name]) if @inherited_values[name]
|
19
|
+
value.concat(@new_values[name]) if @new_values[name]
|
19
20
|
value
|
20
21
|
end
|
21
22
|
|
22
23
|
def []=(name, value)
|
23
|
-
|
24
|
+
raise if @frozen_values.key? name
|
24
25
|
@new_values[name] ||= []
|
25
26
|
@new_values[name].push value
|
26
27
|
end
|
@@ -29,8 +30,6 @@ module Grape
|
|
29
30
|
new_values.delete key
|
30
31
|
end
|
31
32
|
|
32
|
-
attr_writer :new_values
|
33
|
-
|
34
33
|
def keys
|
35
34
|
(@new_values.keys + @inherited_values.keys).sort.uniq
|
36
35
|
end
|
@@ -42,7 +41,7 @@ module Grape
|
|
42
41
|
end
|
43
42
|
|
44
43
|
def freeze_value(key)
|
45
|
-
@
|
44
|
+
@frozen_values[key] = self[key].freeze
|
46
45
|
end
|
47
46
|
|
48
47
|
def initialize_copy(other)
|
@@ -8,16 +8,41 @@ module Grape
|
|
8
8
|
def initialize(validator, scope, params)
|
9
9
|
@scope = scope
|
10
10
|
@attrs = validator.attrs
|
11
|
-
@
|
11
|
+
@original_params = scope.params(params)
|
12
|
+
@params = Array.wrap(@original_params)
|
12
13
|
end
|
13
14
|
|
14
|
-
def each
|
15
|
-
@params
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def each(&block)
|
16
|
+
do_each(@params, &block) # because we need recursion for nested arrays
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def do_each(params_to_process, parent_indicies = [], &block)
|
22
|
+
params_to_process.each_with_index do |resource_params, index|
|
23
|
+
# when we get arrays of arrays it means that target element located inside array
|
24
|
+
# we need this because we want to know parent arrays indicies
|
25
|
+
if resource_params.is_a?(Array)
|
26
|
+
do_each(resource_params, [index] + parent_indicies, &block)
|
27
|
+
next
|
28
|
+
end
|
29
|
+
|
30
|
+
if @scope.type == Array
|
31
|
+
next unless @original_params.is_a?(Array) # do not validate content of array if it isn't array
|
32
|
+
inside_array = true
|
33
|
+
end
|
34
|
+
if inside_array
|
35
|
+
# fill current and parent scopes with correct array indicies
|
36
|
+
parent_scope = @scope.parent
|
37
|
+
parent_indicies.each do |parent_index|
|
38
|
+
parent_scope.index = parent_index
|
39
|
+
parent_scope = parent_scope.parent
|
19
40
|
end
|
20
|
-
|
41
|
+
@scope.index = index
|
42
|
+
end
|
43
|
+
|
44
|
+
@attrs.each do |attr_name|
|
45
|
+
yield resource_params, attr_name, inside_array
|
21
46
|
end
|
22
47
|
end
|
23
48
|
end
|
@@ -2,6 +2,7 @@ module Grape
|
|
2
2
|
module Validations
|
3
3
|
class ParamsScope
|
4
4
|
attr_accessor :element, :parent, :index
|
5
|
+
attr_reader :type
|
5
6
|
|
6
7
|
include Grape::DSL::Parameters
|
7
8
|
|
@@ -35,10 +36,19 @@ module Grape
|
|
35
36
|
# @return [Boolean] whether or not this entire scope needs to be
|
36
37
|
# validated
|
37
38
|
def should_validate?(parameters)
|
38
|
-
return false if @optional &&
|
39
|
+
return false if @optional && (params(parameters).blank? ||
|
40
|
+
any_element_blank?(parameters))
|
41
|
+
|
39
42
|
@dependent_on.each do |dependency|
|
40
|
-
|
43
|
+
if dependency.is_a?(Hash)
|
44
|
+
dependency_key = dependency.keys[0]
|
45
|
+
proc = dependency.values[0]
|
46
|
+
return false unless proc.call(params(parameters).try(:[], dependency_key))
|
47
|
+
elsif params(parameters).try(:[], dependency).blank?
|
48
|
+
return false
|
49
|
+
end
|
41
50
|
end if @dependent_on
|
51
|
+
|
42
52
|
return true if parent.nil?
|
43
53
|
parent.should_validate?(parameters)
|
44
54
|
end
|
@@ -48,7 +58,7 @@ module Grape
|
|
48
58
|
case
|
49
59
|
when nested?
|
50
60
|
# Find our containing element's name, and append ours.
|
51
|
-
"#{@parent.full_name(@element)}#{
|
61
|
+
"#{@parent.full_name(@element)}#{array_index}[#{name}]"
|
52
62
|
when lateral?
|
53
63
|
# Find the name of the element as if it was at the
|
54
64
|
# same nesting level as our parent.
|
@@ -59,8 +69,8 @@ module Grape
|
|
59
69
|
end
|
60
70
|
end
|
61
71
|
|
62
|
-
def
|
63
|
-
"[#{@
|
72
|
+
def array_index
|
73
|
+
"[#{@index}]" if @index.present?
|
64
74
|
end
|
65
75
|
|
66
76
|
# @return [Boolean] whether or not this scope is the root-level scope
|
@@ -111,7 +121,7 @@ module Grape
|
|
111
121
|
end
|
112
122
|
required_fields.each do |field|
|
113
123
|
field_opts = opts[:using][field]
|
114
|
-
|
124
|
+
raise ArgumentError, "required field not exist: #{field}" unless field_opts
|
115
125
|
requires(field, field_opts)
|
116
126
|
end
|
117
127
|
optional_fields.each do |field|
|
@@ -146,8 +156,8 @@ module Grape
|
|
146
156
|
# if required params are grouped and no type or unsupported type is provided, raise an error
|
147
157
|
type = attrs[1] ? attrs[1][:type] : nil
|
148
158
|
if attrs.first && !optional
|
149
|
-
|
150
|
-
|
159
|
+
raise Grape::Exceptions::MissingGroupTypeError.new if type.nil?
|
160
|
+
raise Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
|
151
161
|
end
|
152
162
|
|
153
163
|
opts = attrs[1] || { type: Array }
|
@@ -187,7 +197,7 @@ module Grape
|
|
187
197
|
@api.namespace_stackable(:declared_params, @declared_params)
|
188
198
|
|
189
199
|
@api.route_setting(:declared_params, []) unless @api.route_setting(:declared_params)
|
190
|
-
@api.route_setting(:declared_params
|
200
|
+
@api.route_setting(:declared_params, @api.namespace_stackable(:declared_params).flatten)
|
191
201
|
end
|
192
202
|
end
|
193
203
|
|
@@ -204,7 +214,7 @@ module Grape
|
|
204
214
|
default = validations[:default]
|
205
215
|
doc_attrs[:default] = default if validations.key?(:default)
|
206
216
|
|
207
|
-
values =
|
217
|
+
values = options_key?(:values, :value, validations) ? validations[:values][:value] : validations[:values]
|
208
218
|
doc_attrs[:values] = values if values
|
209
219
|
|
210
220
|
coerce_type = guess_coerce_type(coerce_type, values)
|
@@ -252,7 +262,7 @@ module Grape
|
|
252
262
|
# @raise [ArgumentError] if the given type options are invalid
|
253
263
|
def infer_coercion(validations)
|
254
264
|
if validations.key?(:type) && validations.key?(:types)
|
255
|
-
|
265
|
+
raise ArgumentError, ':type may not be supplied with :types'
|
256
266
|
end
|
257
267
|
|
258
268
|
validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
|
@@ -284,12 +294,12 @@ module Grape
|
|
284
294
|
def check_coerce_with(validations)
|
285
295
|
return unless validations.key?(:coerce_with)
|
286
296
|
# type must be supplied for coerce_with..
|
287
|
-
|
297
|
+
raise ArgumentError, 'must supply type for coerce_with' unless validations.key?(:coerce)
|
288
298
|
|
289
299
|
# but not special JSON types, which
|
290
300
|
# already imply coercion method
|
291
301
|
return unless [JSON, Array[JSON]].include? validations[:coerce]
|
292
|
-
|
302
|
+
raise ArgumentError, 'coerce_with disallowed for type: JSON'
|
293
303
|
end
|
294
304
|
|
295
305
|
# Add type coercion validation to this scope,
|
@@ -324,18 +334,16 @@ module Grape
|
|
324
334
|
return unless values && default
|
325
335
|
return if values.is_a?(Proc) || default.is_a?(Proc)
|
326
336
|
return if values.include?(default) || (Array(default) - Array(values)).empty?
|
327
|
-
|
337
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values)
|
328
338
|
end
|
329
339
|
|
330
340
|
def validate(type, options, attrs, doc_attrs)
|
331
341
|
validator_class = Validations.validators[type.to_s]
|
332
342
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
fail Grape::Exceptions::UnknownValidator.new(type)
|
338
|
-
end
|
343
|
+
raise Grape::Exceptions::UnknownValidator.new(type) unless validator_class
|
344
|
+
|
345
|
+
value = validator_class.new(attrs, options, doc_attrs[:required], self)
|
346
|
+
@api.namespace_stackable(:validations, value)
|
339
347
|
end
|
340
348
|
|
341
349
|
def validate_value_coercion(coerce_type, values)
|
@@ -347,7 +355,7 @@ module Grape
|
|
347
355
|
value_types = value_types.map { |type| Virtus::Attribute.build(type) }
|
348
356
|
end
|
349
357
|
return unless value_types.any? { |v| !v.is_a?(coerce_type) }
|
350
|
-
|
358
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
|
351
359
|
end
|
352
360
|
|
353
361
|
def extract_message_option(attrs)
|
@@ -359,6 +367,10 @@ module Grape
|
|
359
367
|
def options_key?(type, key, validations)
|
360
368
|
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
|
361
369
|
end
|
370
|
+
|
371
|
+
def any_element_blank?(parameters)
|
372
|
+
params(parameters).respond_to?(:any?) && params(parameters).any?(&:blank?)
|
373
|
+
end
|
362
374
|
end
|
363
375
|
end
|
364
376
|
end
|
@@ -45,14 +45,14 @@ module Grape
|
|
45
45
|
String,
|
46
46
|
Symbol,
|
47
47
|
Rack::Multipart::UploadedFile
|
48
|
-
]
|
48
|
+
].freeze
|
49
49
|
|
50
50
|
# Types representing data structures.
|
51
51
|
STRUCTURES = [
|
52
52
|
Hash,
|
53
53
|
Array,
|
54
54
|
Set
|
55
|
-
]
|
55
|
+
].freeze
|
56
56
|
|
57
57
|
# Types for which Grape provides special coercion
|
58
58
|
# and type-checking logic.
|
@@ -61,14 +61,14 @@ module Grape
|
|
61
61
|
Array[JSON] => JsonArray,
|
62
62
|
::File => File,
|
63
63
|
Rack::Multipart::UploadedFile => File
|
64
|
-
}
|
64
|
+
}.freeze
|
65
65
|
|
66
66
|
GROUPS = [
|
67
67
|
Array,
|
68
68
|
Hash,
|
69
69
|
JSON,
|
70
70
|
Array[JSON]
|
71
|
-
]
|
71
|
+
].freeze
|
72
72
|
|
73
73
|
# Is the given class a primitive type as recognized by Grape?
|
74
74
|
#
|
@@ -26,7 +26,15 @@ module Grape
|
|
26
26
|
converter_options = {
|
27
27
|
nullify_blank: true
|
28
28
|
}
|
29
|
-
conversion_type =
|
29
|
+
conversion_type = if method == JSON
|
30
|
+
Object
|
31
|
+
# because we want just parsed JSON content:
|
32
|
+
# if type is Array and data is `"{}"`
|
33
|
+
# result will be [] because Virtus converts hashes
|
34
|
+
# to arrays
|
35
|
+
else
|
36
|
+
type
|
37
|
+
end
|
30
38
|
|
31
39
|
# Use a special coercer for multiply-typed parameters.
|
32
40
|
if Types.multiple?(type)
|
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
def validate!(params)
|
6
6
|
super
|
7
7
|
if scope_requires_params && only_subset_present
|
8
|
-
|
8
|
+
raise Grape::Exceptions::Validation, params: all_keys, message: message(:all_or_none)
|
9
9
|
end
|
10
10
|
params
|
11
11
|
end
|
@@ -13,7 +13,7 @@ module Grape
|
|
13
13
|
private
|
14
14
|
|
15
15
|
def only_subset_present
|
16
|
-
scoped_params.any? { |resource_params| keys_in_common(resource_params).
|
16
|
+
scoped_params.any? { |resource_params| !keys_in_common(resource_params).empty? && keys_in_common(resource_params).length < attrs.length }
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -9,21 +9,20 @@ module Grape
|
|
9
9
|
|
10
10
|
key_exists = params.key?(attr_name)
|
11
11
|
|
12
|
-
if @scope.root?
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
12
|
+
should_validate = if @scope.root?
|
13
|
+
# root scope. validate if it's a required param. if it's optional, validate only if key exists in hash
|
14
|
+
@required || key_exists
|
15
|
+
else # nested scope
|
16
|
+
(@required && params.present?) ||
|
17
|
+
# optional param but key inside scoping element exists
|
18
|
+
(!@required && params.key?(attr_name))
|
19
|
+
end
|
21
20
|
|
22
21
|
return unless should_validate
|
23
22
|
|
24
|
-
return if
|
23
|
+
return if false == value || value.present?
|
25
24
|
|
26
|
-
|
25
|
+
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:blank)
|
27
26
|
end
|
28
27
|
end
|
29
28
|
end
|