grape 1.5.2 → 1.6.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 +4 -4
- data/CHANGELOG.md +47 -0
- data/CONTRIBUTING.md +2 -1
- data/README.md +33 -3
- data/UPGRADING.md +71 -2
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +13 -17
- data/lib/grape/api.rb +18 -13
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/headers.rb +5 -2
- data/lib/grape/dsl/helpers.rb +7 -5
- data/lib/grape/dsl/inside_route.rb +17 -8
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +3 -3
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +2 -2
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/endpoint.rb +21 -36
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/validation.rb +1 -2
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/auth/dsl.rb +7 -1
- data/lib/grape/middleware/base.rb +3 -1
- data/lib/grape/middleware/formatter.rb +4 -4
- data/lib/grape/middleware/stack.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/parser/json.rb +1 -1
- data/lib/grape/parser/xml.rb +1 -1
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +3 -0
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +6 -0
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -2
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/params_scope.rb +88 -55
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +3 -3
- data/lib/grape/validations/validators/all_or_none.rb +8 -5
- data/lib/grape/validations/validators/allow_blank.rb +9 -7
- data/lib/grape/validations/validators/as.rb +6 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
- data/lib/grape/validations/validators/base.rb +75 -70
- data/lib/grape/validations/validators/coerce.rb +63 -79
- data/lib/grape/validations/validators/default.rb +37 -34
- data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
- data/lib/grape/validations/validators/except_values.rb +13 -11
- data/lib/grape/validations/validators/multiple_params_base.rb +24 -20
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
- data/lib/grape/validations/validators/presence.rb +7 -4
- data/lib/grape/validations/validators/regexp.rb +8 -5
- data/lib/grape/validations/validators/same_as.rb +18 -15
- data/lib/grape/validations/validators/values.rb +61 -56
- data/lib/grape/validations.rb +6 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +4 -1
- data/spec/grape/api/custom_validations_spec.rb +77 -45
- data/spec/grape/api/deeply_included_options_spec.rb +3 -3
- data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
- data/spec/grape/api_remount_spec.rb +16 -15
- data/spec/grape/api_spec.rb +510 -220
- data/spec/grape/dsl/callbacks_spec.rb +2 -1
- data/spec/grape/dsl/headers_spec.rb +39 -9
- data/spec/grape/dsl/helpers_spec.rb +3 -2
- data/spec/grape/dsl/inside_route_spec.rb +6 -4
- data/spec/grape/dsl/logger_spec.rb +16 -18
- data/spec/grape/dsl/middleware_spec.rb +2 -1
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +10 -7
- data/spec/grape/endpoint/declared_spec.rb +259 -12
- data/spec/grape/endpoint_spec.rb +77 -55
- data/spec/grape/entity_spec.rb +22 -22
- data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
- data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
- data/spec/grape/exceptions/validation_spec.rb +5 -3
- data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
- data/spec/grape/integration/rack_sendfile_spec.rb +1 -1
- data/spec/grape/loading_spec.rb +8 -8
- data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
- data/spec/grape/middleware/auth/strategies_spec.rb +60 -20
- data/spec/grape/middleware/base_spec.rb +24 -15
- data/spec/grape/middleware/error_spec.rb +2 -2
- data/spec/grape/middleware/exception_spec.rb +111 -161
- data/spec/grape/middleware/formatter_spec.rb +27 -6
- data/spec/grape/middleware/globals_spec.rb +7 -4
- data/spec/grape/middleware/stack_spec.rb +14 -12
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
- data/spec/grape/middleware/versioner/header_spec.rb +14 -13
- data/spec/grape/middleware/versioner/param_spec.rb +7 -1
- data/spec/grape/middleware/versioner/path_spec.rb +5 -1
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/parser_spec.rb +4 -0
- data/spec/grape/path_spec.rb +52 -52
- data/spec/grape/presenters/presenter_spec.rb +7 -6
- data/spec/grape/request_spec.rb +6 -4
- data/spec/grape/util/inheritable_setting_spec.rb +7 -7
- data/spec/grape/util/inheritable_values_spec.rb +3 -2
- data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
- data/spec/grape/util/stackable_values_spec.rb +7 -5
- data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -0
- data/spec/grape/validations/params_scope_spec.rb +46 -10
- data/spec/grape/validations/single_attribute_iterator_spec.rb +2 -1
- data/spec/grape/validations/types/primitive_coercer_spec.rb +4 -4
- data/spec/grape/validations/types_spec.rb +8 -8
- data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
- data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
- data/spec/grape/validations/validators/coerce_spec.rb +99 -22
- data/spec/grape/validations/validators/default_spec.rb +72 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
- data/spec/grape/validations/validators/except_values_spec.rb +3 -3
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
- data/spec/grape/validations/validators/presence_spec.rb +16 -1
- data/spec/grape/validations/validators/regexp_spec.rb +25 -31
- data/spec/grape/validations/validators/same_as_spec.rb +14 -20
- data/spec/grape/validations/validators/values_spec.rb +183 -178
- data/spec/grape/validations_spec.rb +99 -58
- data/spec/integration/eager_load/eager_load_spec.rb +2 -2
- data/spec/integration/multi_json/json_spec.rb +1 -1
- data/spec/integration/multi_xml/xml_spec.rb +1 -1
- data/spec/shared/versioning_examples.rb +12 -9
- data/spec/spec_helper.rb +12 -2
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- metadata +102 -101
data/lib/grape/router/route.rb
CHANGED
@@ -84,8 +84,8 @@ module Grape
|
|
84
84
|
path, line = *location.scan(SOURCE_LOCATION_REGEXP).first
|
85
85
|
path = File.realpath(path) if Pathname.new(path).relative?
|
86
86
|
expected ||= name
|
87
|
-
warn
|
88
|
-
#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
|
87
|
+
warn <<~WARNING
|
88
|
+
#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
|
89
89
|
WARNING
|
90
90
|
end
|
91
91
|
end
|
data/lib/grape/router.rb
CHANGED
@@ -28,6 +28,7 @@ module Grape
|
|
28
28
|
|
29
29
|
def compile!
|
30
30
|
return if compiled
|
31
|
+
|
31
32
|
@union = Regexp.union(@neutral_regexes)
|
32
33
|
@neutral_regexes = nil
|
33
34
|
self.class.supported_methods.each do |method|
|
@@ -60,6 +61,7 @@ module Grape
|
|
60
61
|
def recognize_path(input)
|
61
62
|
any = with_optimization { greedy_match?(input) }
|
62
63
|
return if any == default_response
|
64
|
+
|
63
65
|
any.endpoint
|
64
66
|
end
|
65
67
|
|
@@ -80,6 +82,7 @@ module Grape
|
|
80
82
|
map[method].each do |route|
|
81
83
|
next if exact_route == route
|
82
84
|
next unless route.match?(input)
|
85
|
+
|
83
86
|
response = process_route(route, env)
|
84
87
|
break unless cascade?(response)
|
85
88
|
end
|
@@ -91,6 +94,7 @@ module Grape
|
|
91
94
|
response = yield(input, method)
|
92
95
|
|
93
96
|
return response if response && !(cascade = cascade?(response))
|
97
|
+
|
94
98
|
last_neighbor_route = greedy_match?(input)
|
95
99
|
|
96
100
|
# If last_neighbor_route exists and request method is OPTIONS,
|
@@ -139,12 +143,14 @@ module Grape
|
|
139
143
|
def match?(input, method)
|
140
144
|
current_regexp = @optimized_map[method]
|
141
145
|
return unless current_regexp.match(input)
|
146
|
+
|
142
147
|
last_match = Regexp.last_match
|
143
148
|
@map[method].detect { |route| last_match["_#{route.index}"] }
|
144
149
|
end
|
145
150
|
|
146
151
|
def greedy_match?(input)
|
147
152
|
return unless @union.match(input)
|
153
|
+
|
148
154
|
last_match = Regexp.last_match
|
149
155
|
@neutral_map.detect { |route| last_match["_#{route.index}"] }
|
150
156
|
end
|
@@ -5,9 +5,7 @@ module Grape
|
|
5
5
|
# A branchable, inheritable settings object which can store both stackable
|
6
6
|
# and inheritable values (see InheritableValues and StackableValues).
|
7
7
|
class InheritableSetting
|
8
|
-
attr_accessor :route, :api_class, :namespace
|
9
|
-
attr_accessor :namespace_inheritable, :namespace_stackable, :namespace_reverse_stackable
|
10
|
-
attr_accessor :parent, :point_in_time_copies
|
8
|
+
attr_accessor :route, :api_class, :namespace, :namespace_inheritable, :namespace_stackable, :namespace_reverse_stackable, :parent, :point_in_time_copies
|
11
9
|
|
12
10
|
# Retrieve global settings.
|
13
11
|
def self.global
|
@@ -49,9 +49,10 @@ module Grape
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def []=(key, value)
|
52
|
-
@value_hash[key] =
|
52
|
+
@value_hash[key] = case value
|
53
|
+
when Hash
|
53
54
|
LazyValueHash.new(value)
|
54
|
-
|
55
|
+
when Array
|
55
56
|
LazyValueArray.new(value)
|
56
57
|
else
|
57
58
|
LazyValue.new(value)
|
@@ -13,6 +13,8 @@ module Grape
|
|
13
13
|
# @param opts [Hash] options for this scope
|
14
14
|
# @option opts :element [Symbol] the element that contains this scope; for
|
15
15
|
# this to be relevant, @parent must be set
|
16
|
+
# @option opts :element_renamed [Symbol, nil] whenever this scope should
|
17
|
+
# be renamed and to what, given +nil+ no renaming is done
|
16
18
|
# @option opts :parent [ParamsScope] the scope containing this scope
|
17
19
|
# @option opts :api [API] the API endpoint to modify
|
18
20
|
# @option opts :optional [Boolean] whether or not this scope needs to have
|
@@ -23,17 +25,18 @@ module Grape
|
|
23
25
|
# validate if this param is present in the parent scope
|
24
26
|
# @yield the instance context, open for parameter definitions
|
25
27
|
def initialize(opts, &block)
|
26
|
-
@element
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
28
|
+
@element = opts[:element]
|
29
|
+
@element_renamed = opts[:element_renamed]
|
30
|
+
@parent = opts[:parent]
|
31
|
+
@api = opts[:api]
|
32
|
+
@optional = opts[:optional] || false
|
33
|
+
@type = opts[:type]
|
34
|
+
@group = opts[:group] || {}
|
35
|
+
@dependent_on = opts[:dependent_on]
|
33
36
|
@declared_params = []
|
34
37
|
@index = nil
|
35
38
|
|
36
|
-
instance_eval(&block) if
|
39
|
+
instance_eval(&block) if block
|
37
40
|
|
38
41
|
configure_declared_params
|
39
42
|
end
|
@@ -50,15 +53,14 @@ module Grape
|
|
50
53
|
return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
|
51
54
|
return false unless meets_dependency?(scoped_params, parameters)
|
52
55
|
return true if parent.nil?
|
56
|
+
|
53
57
|
parent.should_validate?(parameters)
|
54
58
|
end
|
55
59
|
|
56
60
|
def meets_dependency?(params, request_params)
|
57
61
|
return true unless @dependent_on
|
58
62
|
|
59
|
-
if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
|
60
|
-
return false
|
61
|
-
end
|
63
|
+
return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
|
62
64
|
|
63
65
|
return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array)
|
64
66
|
|
@@ -129,18 +131,35 @@ module Grape
|
|
129
131
|
if lateral?
|
130
132
|
@parent.push_declared_params(attrs, **opts)
|
131
133
|
else
|
132
|
-
|
133
|
-
|
134
|
-
@api.route_setting(:renamed_params) << { attrs.first => opts[:as] }
|
135
|
-
attrs = [opts[:as]]
|
136
|
-
end
|
134
|
+
push_renamed_param(full_path + [attrs.first], opts[:as]) \
|
135
|
+
if opts && opts[:as]
|
137
136
|
|
138
137
|
@declared_params.concat attrs
|
139
138
|
end
|
140
139
|
end
|
141
140
|
|
141
|
+
# Get the full path of the parameter scope in the hierarchy.
|
142
|
+
#
|
143
|
+
# @return [Array<Symbol>] the nesting/path of the current parameter scope
|
144
|
+
def full_path
|
145
|
+
nested? ? @parent.full_path + [@element] : []
|
146
|
+
end
|
147
|
+
|
142
148
|
private
|
143
149
|
|
150
|
+
# Add a new parameter which should be renamed when using the +#declared+
|
151
|
+
# method.
|
152
|
+
#
|
153
|
+
# @param path [Array<String, Symbol>] the full path of the parameter
|
154
|
+
# (including the parameter name as last array element)
|
155
|
+
# @param new_name [String, Symbol] the new name of the parameter (the
|
156
|
+
# renamed name, with the +as: ...+ semantic)
|
157
|
+
def push_renamed_param(path, new_name)
|
158
|
+
base = @api.route_setting(:renamed_params) || {}
|
159
|
+
base[Array(path).map(&:to_s)] = new_name.to_s
|
160
|
+
@api.route_setting(:renamed_params, base)
|
161
|
+
end
|
162
|
+
|
144
163
|
def require_required_and_optional_fields(context, opts)
|
145
164
|
if context == :all
|
146
165
|
optional_fields = Array(opts[:except])
|
@@ -152,6 +171,7 @@ module Grape
|
|
152
171
|
required_fields.each do |field|
|
153
172
|
field_opts = opts[:using][field]
|
154
173
|
raise ArgumentError, "required field not exist: #{field}" unless field_opts
|
174
|
+
|
155
175
|
requires(field, field_opts)
|
156
176
|
end
|
157
177
|
optional_fields.each do |field|
|
@@ -191,11 +211,12 @@ module Grape
|
|
191
211
|
end
|
192
212
|
|
193
213
|
self.class.new(
|
194
|
-
api:
|
195
|
-
element:
|
196
|
-
|
214
|
+
api: @api,
|
215
|
+
element: attrs.first,
|
216
|
+
element_renamed: attrs[1][:as],
|
217
|
+
parent: self,
|
197
218
|
optional: optional,
|
198
|
-
type:
|
219
|
+
type: type || Array,
|
199
220
|
&block
|
200
221
|
)
|
201
222
|
end
|
@@ -209,11 +230,11 @@ module Grape
|
|
209
230
|
# @yield parameter scope
|
210
231
|
def new_lateral_scope(options, &block)
|
211
232
|
self.class.new(
|
212
|
-
api:
|
213
|
-
element:
|
214
|
-
parent:
|
215
|
-
options:
|
216
|
-
type:
|
233
|
+
api: @api,
|
234
|
+
element: nil,
|
235
|
+
parent: self,
|
236
|
+
options: @optional,
|
237
|
+
type: type == Array ? Array : Hash,
|
217
238
|
dependent_on: options[:dependent_on],
|
218
239
|
&block
|
219
240
|
)
|
@@ -226,15 +247,17 @@ module Grape
|
|
226
247
|
# @yield parameter scope
|
227
248
|
def new_group_scope(attrs, &block)
|
228
249
|
self.class.new(
|
229
|
-
api:
|
230
|
-
parent:
|
231
|
-
group:
|
250
|
+
api: @api,
|
251
|
+
parent: self,
|
252
|
+
group: attrs.first,
|
232
253
|
&block
|
233
254
|
)
|
234
255
|
end
|
235
256
|
|
236
257
|
# Pushes declared params to parent or settings
|
237
258
|
def configure_declared_params
|
259
|
+
push_renamed_param(full_path, @element_renamed) if @element_renamed
|
260
|
+
|
238
261
|
if nested?
|
239
262
|
@parent.push_declared_params [element => @declared_params]
|
240
263
|
else
|
@@ -283,16 +306,15 @@ module Grape
|
|
283
306
|
|
284
307
|
doc_attrs[:documentation] = validations.delete(:documentation) if validations.key?(:documentation)
|
285
308
|
|
286
|
-
|
287
|
-
@api.document_attribute(full_attrs, doc_attrs)
|
309
|
+
document_attribute(attrs, doc_attrs)
|
288
310
|
|
289
311
|
opts = derive_validator_options(validations)
|
290
312
|
|
313
|
+
order_specific_validations = Set[:as]
|
314
|
+
|
291
315
|
# Validate for presence before any other validators
|
292
|
-
|
293
|
-
|
294
|
-
validations.delete(:presence)
|
295
|
-
validations.delete(:message) if validations.key?(:message)
|
316
|
+
validates_presence(validations, attrs, doc_attrs, opts) do |validation_type|
|
317
|
+
order_specific_validations << validation_type
|
296
318
|
end
|
297
319
|
|
298
320
|
# Before we run the rest of the validators, let's handle
|
@@ -301,6 +323,8 @@ module Grape
|
|
301
323
|
coerce_type validations, attrs, doc_attrs, opts
|
302
324
|
|
303
325
|
validations.each do |type, options|
|
326
|
+
next if order_specific_validations.include?(type)
|
327
|
+
|
304
328
|
validate(type, options, attrs, doc_attrs, opts)
|
305
329
|
end
|
306
330
|
end
|
@@ -319,9 +343,7 @@ module Grape
|
|
319
343
|
# @return [class-like] type to which the parameter will be coerced
|
320
344
|
# @raise [ArgumentError] if the given type options are invalid
|
321
345
|
def infer_coercion(validations)
|
322
|
-
if validations.key?(:type) && validations.key?(:types)
|
323
|
-
raise ArgumentError, ':type may not be supplied with :types'
|
324
|
-
end
|
346
|
+
raise ArgumentError, ':type may not be supplied with :types' if validations.key?(:type) && validations.key?(:types)
|
325
347
|
|
326
348
|
validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
|
327
349
|
validations[:coerce_message] = (options_key?(:type, :message, validations) ? validations[:type][:message] : nil) if validations.key?(:type)
|
@@ -357,6 +379,7 @@ module Grape
|
|
357
379
|
# but not special JSON types, which
|
358
380
|
# already imply coercion method
|
359
381
|
return unless [JSON, Array[JSON]].include? validations[:coerce]
|
382
|
+
|
360
383
|
raise ArgumentError, 'coerce_with disallowed for type: JSON'
|
361
384
|
end
|
362
385
|
|
@@ -384,6 +407,7 @@ module Grape
|
|
384
407
|
|
385
408
|
def guess_coerce_type(coerce_type, *values_list)
|
386
409
|
return coerce_type unless coerce_type == Array
|
410
|
+
|
387
411
|
values_list.each do |values|
|
388
412
|
next if !values || values.is_a?(Proc)
|
389
413
|
return values.first.class if values.is_a?(Range) || !values.empty?
|
@@ -394,14 +418,11 @@ module Grape
|
|
394
418
|
def check_incompatible_option_values(default, values, except_values, excepts)
|
395
419
|
return unless default && !default.is_a?(Proc)
|
396
420
|
|
397
|
-
if values && !values.is_a?(Proc)
|
398
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) \
|
399
|
-
unless Array(default).all? { |def_val| values.include?(def_val) }
|
400
|
-
end
|
421
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && !Array(default).all? { |def_val| values.include?(def_val) }
|
401
422
|
|
402
|
-
if except_values && !except_values.is_a?(Proc)
|
423
|
+
if except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
|
403
424
|
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values) \
|
404
|
-
|
425
|
+
|
405
426
|
end
|
406
427
|
|
407
428
|
return unless excepts && !excepts.is_a?(Proc)
|
@@ -415,11 +436,11 @@ module Grape
|
|
415
436
|
raise Grape::Exceptions::UnknownValidator.new(type) unless validator_class
|
416
437
|
|
417
438
|
validator_options = {
|
418
|
-
attributes:
|
419
|
-
options:
|
420
|
-
required:
|
421
|
-
params_scope:
|
422
|
-
opts:
|
439
|
+
attributes: attrs,
|
440
|
+
options: options,
|
441
|
+
required: doc_attrs[:required],
|
442
|
+
params_scope: self,
|
443
|
+
opts: opts,
|
423
444
|
validator_class: validator_class
|
424
445
|
}
|
425
446
|
@api.namespace_stackable(:validations, validator_options)
|
@@ -427,21 +448,20 @@ module Grape
|
|
427
448
|
|
428
449
|
def validate_value_coercion(coerce_type, *values_list)
|
429
450
|
return unless coerce_type
|
451
|
+
|
430
452
|
coerce_type = coerce_type.first if coerce_type.is_a?(Array)
|
431
453
|
values_list.each do |values|
|
432
454
|
next if !values || values.is_a?(Proc)
|
455
|
+
|
433
456
|
value_types = values.is_a?(Range) ? [values.begin, values.end] : values
|
434
|
-
if coerce_type == Grape::API::Boolean
|
435
|
-
|
436
|
-
end
|
437
|
-
unless value_types.all? { |v| v.is_a? coerce_type }
|
438
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
|
439
|
-
end
|
457
|
+
value_types = value_types.map { |type| Grape::API::Boolean.build(type) } if coerce_type == Grape::API::Boolean
|
458
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values) unless value_types.all?(coerce_type)
|
440
459
|
end
|
441
460
|
end
|
442
461
|
|
443
462
|
def extract_message_option(attrs)
|
444
463
|
return nil unless attrs.is_a?(Array)
|
464
|
+
|
445
465
|
opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
|
446
466
|
opts.key?(:message) && !opts[:message].nil? ? opts.delete(:message) : nil
|
447
467
|
end
|
@@ -461,9 +481,22 @@ module Grape
|
|
461
481
|
|
462
482
|
{
|
463
483
|
allow_blank: allow_blank.is_a?(Hash) ? allow_blank[:value] : allow_blank,
|
464
|
-
fail_fast:
|
484
|
+
fail_fast: validations.delete(:fail_fast) || false
|
465
485
|
}
|
466
486
|
end
|
487
|
+
|
488
|
+
def validates_presence(validations, attrs, doc_attrs, opts)
|
489
|
+
return unless validations.key?(:presence) && validations[:presence]
|
490
|
+
|
491
|
+
validate(:presence, validations[:presence], attrs, doc_attrs, opts)
|
492
|
+
yield :presence
|
493
|
+
yield :message if validations.key?(:message)
|
494
|
+
end
|
495
|
+
|
496
|
+
def document_attribute(attrs, doc_attrs)
|
497
|
+
full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
|
498
|
+
@api.document_attribute(full_attrs, doc_attrs)
|
499
|
+
end
|
467
500
|
end
|
468
501
|
end
|
469
502
|
end
|
@@ -52,12 +52,11 @@ module Grape
|
|
52
52
|
# this should always be a string.
|
53
53
|
# @return [Object] the coerced result
|
54
54
|
def call(val)
|
55
|
-
return if val.nil?
|
56
|
-
|
57
55
|
coerced_val = @method.call(val)
|
58
56
|
|
59
57
|
return coerced_val if coerced_val.is_a?(InvalidValue)
|
60
58
|
return InvalidValue.new unless coerced?(coerced_val)
|
59
|
+
|
61
60
|
coerced_val
|
62
61
|
end
|
63
62
|
|
@@ -35,7 +35,7 @@ module Grape
|
|
35
35
|
|
36
36
|
# Returns an instance of a coercer for a given type
|
37
37
|
def coercer_instance_for(type, strict = false)
|
38
|
-
return PrimitiveCoercer.new(type, strict) if type.
|
38
|
+
return PrimitiveCoercer.new(type, strict) if type.instance_of?(Class)
|
39
39
|
|
40
40
|
# in case of a collection (Array[Integer]) the type is an instance of a collection,
|
41
41
|
# so we need to figure out the actual type
|
@@ -22,6 +22,7 @@ module Grape
|
|
22
22
|
|
23
23
|
# Allow nulls and blank strings
|
24
24
|
return if input.nil? || input.match?(/^\s*$/)
|
25
|
+
|
25
26
|
JSON.parse(input, symbolize_names: true)
|
26
27
|
end
|
27
28
|
|
@@ -41,7 +42,7 @@ module Grape
|
|
41
42
|
# @param value [Object] result of {#parse}
|
42
43
|
# @return [true,false]
|
43
44
|
def coerced_collection?(value)
|
44
|
-
value.is_a?(::Array) && value.all?
|
45
|
+
value.is_a?(::Array) && value.all?(::Hash)
|
45
46
|
end
|
46
47
|
end
|
47
48
|
end
|
@@ -11,15 +11,15 @@ module Grape
|
|
11
11
|
class PrimitiveCoercer < DryTypeCoercer
|
12
12
|
MAPPING = {
|
13
13
|
Grape::API::Boolean => DryTypes::Params::Bool,
|
14
|
-
BigDecimal
|
14
|
+
BigDecimal => DryTypes::Params::Decimal,
|
15
15
|
|
16
16
|
# unfortunately, a +Params+ scope doesn't contain String
|
17
|
-
String
|
17
|
+
String => DryTypes::Coercible::String
|
18
18
|
}.freeze
|
19
19
|
|
20
20
|
STRICT_MAPPING = {
|
21
21
|
Grape::API::Boolean => DryTypes::Strict::Bool,
|
22
|
-
BigDecimal
|
22
|
+
BigDecimal => DryTypes::Strict::Decimal
|
23
23
|
}.freeze
|
24
24
|
|
25
25
|
def initialize(type, strict = false)
|
@@ -4,11 +4,14 @@ require 'grape/validations/validators/multiple_params_base'
|
|
4
4
|
|
5
5
|
module Grape
|
6
6
|
module Validations
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
module Validators
|
8
|
+
class AllOrNoneOfValidator < MultipleParamsBase
|
9
|
+
def validate_params!(params)
|
10
|
+
keys = keys_in_common(params)
|
11
|
+
return if keys.empty? || keys.length == all_keys.length
|
12
|
+
|
13
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
|
14
|
+
end
|
12
15
|
end
|
13
16
|
end
|
14
17
|
end
|
@@ -2,16 +2,18 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
module Validators
|
6
|
+
class AllowBlankValidator < Base
|
7
|
+
def validate_param!(attr_name, params)
|
8
|
+
return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
value = params[attr_name]
|
11
|
+
value = value.strip if value.respond_to?(:strip)
|
11
12
|
|
12
|
-
|
13
|
+
return if value == false || value.present?
|
13
14
|
|
14
|
-
|
15
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:blank))
|
16
|
+
end
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -2,14 +2,12 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def validate_param!(attr_name, params)
|
12
|
-
params[@renamed_options] = params[attr_name]
|
5
|
+
module Validators
|
6
|
+
class AsValidator < Base
|
7
|
+
# We use a validator for renaming parameters. This is just a marker for
|
8
|
+
# the parameter scope to handle the renaming. No actual validation
|
9
|
+
# happens here.
|
10
|
+
def validate_param!(*); end
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
@@ -4,10 +4,13 @@ require 'grape/validations/validators/multiple_params_base'
|
|
4
4
|
|
5
5
|
module Grape
|
6
6
|
module Validations
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
module Validators
|
8
|
+
class AtLeastOneOfValidator < MultipleParamsBase
|
9
|
+
def validate_params!(params)
|
10
|
+
return unless keys_in_common(params).empty?
|
11
|
+
|
12
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
|
13
|
+
end
|
11
14
|
end
|
12
15
|
end
|
13
16
|
end
|