grape 1.5.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +23 -3
  4. data/UPGRADING.md +43 -1
  5. data/grape.gemspec +5 -5
  6. data/lib/grape/api/instance.rb +13 -17
  7. data/lib/grape/api.rb +5 -12
  8. data/lib/grape/cookies.rb +2 -0
  9. data/lib/grape/dsl/desc.rb +3 -5
  10. data/lib/grape/dsl/helpers.rb +6 -4
  11. data/lib/grape/dsl/inside_route.rb +17 -8
  12. data/lib/grape/dsl/middleware.rb +4 -4
  13. data/lib/grape/dsl/parameters.rb +3 -3
  14. data/lib/grape/dsl/request_response.rb +9 -6
  15. data/lib/grape/dsl/routing.rb +2 -2
  16. data/lib/grape/dsl/settings.rb +5 -5
  17. data/lib/grape/endpoint.rb +20 -35
  18. data/lib/grape/error_formatter/json.rb +2 -6
  19. data/lib/grape/error_formatter/xml.rb +2 -6
  20. data/lib/grape/exceptions/validation.rb +1 -2
  21. data/lib/grape/formatter/json.rb +1 -0
  22. data/lib/grape/formatter/serializable_hash.rb +2 -1
  23. data/lib/grape/formatter/xml.rb +1 -0
  24. data/lib/grape/middleware/base.rb +2 -0
  25. data/lib/grape/middleware/formatter.rb +4 -4
  26. data/lib/grape/middleware/stack.rb +2 -2
  27. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  28. data/lib/grape/middleware/versioner/header.rb +6 -4
  29. data/lib/grape/middleware/versioner/param.rb +1 -0
  30. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  31. data/lib/grape/middleware/versioner/path.rb +2 -0
  32. data/lib/grape/path.rb +1 -0
  33. data/lib/grape/request.rb +1 -0
  34. data/lib/grape/router/pattern.rb +1 -1
  35. data/lib/grape/router/route.rb +2 -2
  36. data/lib/grape/router.rb +6 -0
  37. data/lib/grape/util/inheritable_setting.rb +1 -3
  38. data/lib/grape/util/lazy_value.rb +3 -2
  39. data/lib/grape/validations/params_scope.rb +88 -55
  40. data/lib/grape/validations/types/custom_type_coercer.rb +1 -0
  41. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  42. data/lib/grape/validations/types/json.rb +2 -1
  43. data/lib/grape/validations/types/primitive_coercer.rb +3 -3
  44. data/lib/grape/validations/validators/all_or_none.rb +1 -0
  45. data/lib/grape/validations/validators/as.rb +4 -8
  46. data/lib/grape/validations/validators/at_least_one_of.rb +1 -0
  47. data/lib/grape/validations/validators/base.rb +4 -1
  48. data/lib/grape/validations/validators/coerce.rb +1 -5
  49. data/lib/grape/validations/validators/default.rb +1 -0
  50. data/lib/grape/validations/validators/exactly_one_of.rb +1 -0
  51. data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
  52. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -0
  53. data/lib/grape/validations/validators/presence.rb +1 -0
  54. data/lib/grape/validations/validators/regexp.rb +1 -0
  55. data/lib/grape/validations/validators/same_as.rb +1 -0
  56. data/lib/grape/validations/validators/values.rb +3 -0
  57. data/lib/grape/version.rb +1 -1
  58. data/lib/grape.rb +1 -1
  59. data/spec/grape/api/custom_validations_spec.rb +1 -0
  60. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  61. data/spec/grape/api_spec.rb +126 -37
  62. data/spec/grape/dsl/callbacks_spec.rb +1 -1
  63. data/spec/grape/dsl/middleware_spec.rb +1 -1
  64. data/spec/grape/dsl/parameters_spec.rb +1 -0
  65. data/spec/grape/dsl/routing_spec.rb +1 -1
  66. data/spec/grape/endpoint/declared_spec.rb +247 -0
  67. data/spec/grape/endpoint_spec.rb +5 -5
  68. data/spec/grape/entity_spec.rb +9 -9
  69. data/spec/grape/middleware/auth/dsl_spec.rb +1 -1
  70. data/spec/grape/middleware/error_spec.rb +1 -2
  71. data/spec/grape/middleware/formatter_spec.rb +2 -2
  72. data/spec/grape/middleware/stack_spec.rb +3 -1
  73. data/spec/grape/validations/params_scope_spec.rb +37 -3
  74. data/spec/grape/validations/single_attribute_iterator_spec.rb +1 -1
  75. data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
  76. data/spec/grape/validations/validators/coerce_spec.rb +13 -10
  77. data/spec/grape/validations/validators/except_values_spec.rb +2 -2
  78. data/spec/grape/validations/validators/values_spec.rb +15 -11
  79. data/spec/grape/validations_spec.rb +54 -42
  80. data/spec/shared/versioning_examples.rb +2 -2
  81. data/spec/spec_helper.rb +1 -1
  82. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  83. metadata +6 -6
@@ -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 = opts[:element]
27
- @parent = opts[:parent]
28
- @api = opts[:api]
29
- @optional = opts[:optional] || false
30
- @type = opts[:type]
31
- @group = opts[:group] || {}
32
- @dependent_on = opts[:dependent_on]
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 block_given?
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
- if opts && opts[:as]
133
- @api.route_setting(:renamed_params, @api.route_setting(:renamed_params) || [])
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: @api,
195
- element: attrs[1][:as] || attrs.first,
196
- parent: self,
214
+ api: @api,
215
+ element: attrs.first,
216
+ element_renamed: attrs[1][:as],
217
+ parent: self,
197
218
  optional: optional,
198
- type: type || Array,
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: @api,
213
- element: nil,
214
- parent: self,
215
- options: @optional,
216
- type: type == Array ? Array : Hash,
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: @api,
230
- parent: self,
231
- group: attrs.first,
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
- full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
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
- if validations.key?(:presence) && validations[:presence]
293
- validate('presence', validations[:presence], attrs, doc_attrs, opts)
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
- unless Array(default).none? { |def_val| except_values.include?(def_val) }
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: attrs,
419
- options: options,
420
- required: doc_attrs[:required],
421
- params_scope: self,
422
- opts: 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
- value_types = value_types.map { |type| Grape::API::Boolean.build(type) }
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: validations.delete(:fail_fast) || false
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
@@ -56,6 +56,7 @@ module Grape
56
56
 
57
57
  return coerced_val if coerced_val.is_a?(InvalidValue)
58
58
  return InvalidValue.new unless coerced?(coerced_val)
59
+
59
60
  coerced_val
60
61
  end
61
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.class == Class
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? { |i| i.is_a? ::Hash }
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 => DryTypes::Params::Decimal,
14
+ BigDecimal => DryTypes::Params::Decimal,
15
15
 
16
16
  # unfortunately, a +Params+ scope doesn't contain String
17
- String => DryTypes::Coercible::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 => DryTypes::Strict::Decimal
22
+ BigDecimal => DryTypes::Strict::Decimal
23
23
  }.freeze
24
24
 
25
25
  def initialize(type, strict = false)
@@ -8,6 +8,7 @@ module Grape
8
8
  def validate_params!(params)
9
9
  keys = keys_in_common(params)
10
10
  return if keys.empty? || keys.length == all_keys.length
11
+
11
12
  raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
12
13
  end
13
14
  end
@@ -3,14 +3,10 @@
3
3
  module Grape
4
4
  module Validations
5
5
  class AsValidator < Base
6
- def initialize(attrs, options, required, scope, **opts)
7
- @renamed_options = options
8
- super
9
- end
10
-
11
- def validate_param!(attr_name, params)
12
- params[@renamed_options] = params[attr_name]
13
- end
6
+ # We use a validator for renaming parameters. This is just a marker for
7
+ # the parameter scope to handle the renaming. No actual validation
8
+ # happens here.
9
+ def validate_param!(*); end
14
10
  end
15
11
  end
16
12
  end
@@ -7,6 +7,7 @@ module Grape
7
7
  class AtLeastOneOfValidator < MultipleParamsBase
8
8
  def validate_params!(params)
9
9
  return unless keys_in_common(params).empty?
10
+
10
11
  raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
11
12
  end
12
13
  end
@@ -30,6 +30,7 @@ module Grape
30
30
  # @return [void]
31
31
  def validate(request)
32
32
  return unless @scope.should_validate?(request.params)
33
+
33
34
  validate!(request.params)
34
35
  end
35
36
 
@@ -48,8 +49,9 @@ module Grape
48
49
  next if skip_value
49
50
  next if !@scope.required? && empty_val
50
51
  next unless @scope.meets_dependency?(val, params)
52
+
51
53
  begin
52
- validate_param!(attr_name, val) if @required || val.respond_to?(:key?) && val.key?(attr_name)
54
+ validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
53
55
  rescue Grape::Exceptions::Validation => e
54
56
  array_errors << e
55
57
  end
@@ -69,6 +71,7 @@ module Grape
69
71
 
70
72
  def self.inherited(klass)
71
73
  return unless klass.name.present?
74
+
72
75
  Validations.register_validator(convert_to_short_name(klass), klass)
73
76
  end
74
77
 
@@ -27,10 +27,6 @@ module Grape
27
27
  end
28
28
  end
29
29
 
30
- def validate(request)
31
- super
32
- end
33
-
34
30
  def validate_param!(attr_name, params)
35
31
  raise validation_exception(attr_name) unless params.is_a? Hash
36
32
 
@@ -47,7 +43,7 @@ module Grape
47
43
  # h[:list] = list
48
44
  # h
49
45
  # => #<Hashie::Mash list=[1, 2, 3, 4]>
50
- return if params[attr_name].class == new_value.class && params[attr_name] == new_value
46
+ return if params[attr_name].instance_of?(new_value.class) && params[attr_name] == new_value
51
47
 
52
48
  params[attr_name] = new_value
53
49
  end
@@ -22,6 +22,7 @@ module Grape
22
22
  attrs = SingleAttributeIterator.new(self, @scope, params)
23
23
  attrs.each do |resource_params, attr_name|
24
24
  next unless @scope.meets_dependency?(resource_params, params)
25
+
25
26
  validate_param!(attr_name, resource_params) if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
26
27
  end
27
28
  end
@@ -9,6 +9,7 @@ module Grape
9
9
  keys = keys_in_common(params)
10
10
  return if keys.length == 1
11
11
  raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.length.zero?
12
+
12
13
  raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
13
14
  end
14
15
  end
@@ -9,6 +9,7 @@ module Grape
9
9
 
10
10
  attributes.each do |resource_params, skip_value|
11
11
  next if skip_value
12
+
12
13
  begin
13
14
  validate_params!(resource_params)
14
15
  rescue Grape::Exceptions::Validation => e
@@ -23,6 +24,7 @@ module Grape
23
24
 
24
25
  def keys_in_common(resource_params)
25
26
  return [] unless resource_params.is_a?(Hash)
27
+
26
28
  all_keys & resource_params.keys.map! { |attr| @scope.full_name(attr) }
27
29
  end
28
30
 
@@ -8,6 +8,7 @@ module Grape
8
8
  def validate_params!(params)
9
9
  keys = keys_in_common(params)
10
10
  return if keys.length <= 1
11
+
11
12
  raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
12
13
  end
13
14
  end
@@ -5,6 +5,7 @@ module Grape
5
5
  class PresenceValidator < Base
6
6
  def validate_param!(attr_name, params)
7
7
  return if params.respond_to?(:key?) && params.key?(attr_name)
8
+
8
9
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
9
10
  end
10
11
  end
@@ -6,6 +6,7 @@ module Grape
6
6
  def validate_param!(attr_name, params)
7
7
  return unless params.respond_to?(:key?) && params.key?(attr_name)
8
8
  return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.match?((options_key?(:value) ? @option[:value] : @option)) }
9
+
9
10
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
10
11
  end
11
12
  end
@@ -6,6 +6,7 @@ module Grape
6
6
  def validate_param!(attr_name, params)
7
7
  confirmation = options_key?(:value) ? @option[:value] : @option
8
8
  return if params[attr_name] == params[confirmation]
9
+
9
10
  raise Grape::Exceptions::Validation.new(
10
11
  params: [@scope.full_name(attr_name)],
11
12
  message: build_message
@@ -13,6 +13,7 @@ module Grape
13
13
  'Use the except validator instead.' if @excepts
14
14
 
15
15
  raise ArgumentError, 'proc must be a Proc' if @proc && !@proc.is_a?(Proc)
16
+
16
17
  warn '[DEPRECATION] The values validator proc option is deprecated. ' \
17
18
  'The lambda expression can now be assigned directly to values.' if @proc
18
19
  else
@@ -51,6 +52,7 @@ module Grape
51
52
  def check_values(param_array, attr_name)
52
53
  values = @values.is_a?(Proc) && @values.arity.zero? ? @values.call : @values
53
54
  return true if values.nil?
55
+
54
56
  begin
55
57
  return param_array.all? { |param| values.call(param) } if values.is_a? Proc
56
58
  rescue StandardError => e
@@ -63,6 +65,7 @@ module Grape
63
65
  def check_excepts(param_array)
64
66
  excepts = @excepts.is_a?(Proc) ? @excepts.call : @excepts
65
67
  return true if excepts.nil?
68
+
66
69
  param_array.none? { |param| excepts.include?(param) }
67
70
  end
68
71
 
data/lib/grape/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Grape
4
4
  # The current version of Grape.
5
- VERSION = '1.5.3'
5
+ VERSION = '1.6.0'
6
6
  end
data/lib/grape.rb CHANGED
@@ -22,7 +22,7 @@ require 'active_support/dependencies/autoload'
22
22
  require 'active_support/notifications'
23
23
  require 'i18n'
24
24
 
25
- I18n.load_path << File.expand_path('../grape/locale/en.yml', __FILE__)
25
+ I18n.load_path << File.expand_path('grape/locale/en.yml', __dir__)
26
26
 
27
27
  module Grape
28
28
  extend ::ActiveSupport::Autoload
@@ -10,6 +10,7 @@ describe Grape::Validations do
10
10
  def validate_param!(attr_name, params)
11
11
  @option = params[:max].to_i if params.key?(:max)
12
12
  return if params[attr_name].length <= @option
13
+
13
14
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long")
14
15
  end
15
16
  end
@@ -11,7 +11,7 @@ describe Grape::Endpoint do
11
11
 
12
12
  context 'get' do
13
13
  it 'routes to a namespace param with dots' do
14
- subject.namespace ':ns_with_dots', requirements: { ns_with_dots: %r{[^\/]+} } do
14
+ subject.namespace ':ns_with_dots', requirements: { ns_with_dots: %r{[^/]+} } do
15
15
  get '/' do
16
16
  params[:ns_with_dots]
17
17
  end
@@ -23,8 +23,8 @@ describe Grape::Endpoint do
23
23
  end
24
24
 
25
25
  it 'routes to a path with multiple params with dots' do
26
- subject.get ':id_with_dots/:another_id_with_dots', requirements: { id_with_dots: %r{[^\/]+},
27
- another_id_with_dots: %r{[^\/]+} } do
26
+ subject.get ':id_with_dots/:another_id_with_dots', requirements: { id_with_dots: %r{[^/]+},
27
+ another_id_with_dots: %r{[^/]+} } do
28
28
  "#{params[:id_with_dots]}/#{params[:another_id_with_dots]}"
29
29
  end
30
30
 
@@ -34,9 +34,9 @@ describe Grape::Endpoint do
34
34
  end
35
35
 
36
36
  it 'routes to namespace and path params with dots, with overridden requirements' do
37
- subject.namespace ':ns_with_dots', requirements: { ns_with_dots: %r{[^\/]+} } do
38
- get ':another_id_with_dots', requirements: { ns_with_dots: %r{[^\/]+},
39
- another_id_with_dots: %r{[^\/]+} } do
37
+ subject.namespace ':ns_with_dots', requirements: { ns_with_dots: %r{[^/]+} } do
38
+ get ':another_id_with_dots', requirements: { ns_with_dots: %r{[^/]+},
39
+ another_id_with_dots: %r{[^/]+} } do
40
40
  "#{params[:ns_with_dots]}/#{params[:another_id_with_dots]}"
41
41
  end
42
42
  end
@@ -47,8 +47,8 @@ describe Grape::Endpoint do
47
47
  end
48
48
 
49
49
  it 'routes to namespace and path params with dots, with merged requirements' do
50
- subject.namespace ':ns_with_dots', requirements: { ns_with_dots: %r{[^\/]+} } do
51
- get ':another_id_with_dots', requirements: { another_id_with_dots: %r{[^\/]+} } do
50
+ subject.namespace ':ns_with_dots', requirements: { ns_with_dots: %r{[^/]+} } do
51
+ get ':another_id_with_dots', requirements: { another_id_with_dots: %r{[^/]+} } do
52
52
  "#{params[:ns_with_dots]}/#{params[:another_id_with_dots]}"
53
53
  end
54
54
  end