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.

Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +4 -0
  3. data/CHANGELOG.md +54 -27
  4. data/Dangerfile +80 -0
  5. data/Gemfile +23 -0
  6. data/Gemfile.lock +61 -27
  7. data/README.md +135 -7
  8. data/Rakefile +34 -30
  9. data/UPGRADING.md +21 -0
  10. data/gemfiles/rack_1.5.2.gemfile +21 -0
  11. data/gemfiles/rails_3.gemfile +22 -1
  12. data/gemfiles/rails_4.gemfile +21 -0
  13. data/gemfiles/rails_5.gemfile +34 -0
  14. data/grape.gemspec +0 -14
  15. data/lib/grape.rb +2 -0
  16. data/lib/grape/api.rb +9 -2
  17. data/lib/grape/dsl/headers.rb +1 -1
  18. data/lib/grape/dsl/inside_route.rb +15 -17
  19. data/lib/grape/dsl/middleware.rb +15 -1
  20. data/lib/grape/dsl/parameters.rb +16 -14
  21. data/lib/grape/dsl/request_response.rb +24 -20
  22. data/lib/grape/dsl/routing.rb +11 -10
  23. data/lib/grape/dsl/settings.rb +16 -0
  24. data/lib/grape/endpoint.rb +77 -60
  25. data/lib/grape/exceptions/validation.rb +5 -2
  26. data/lib/grape/exceptions/validation_array_errors.rb +11 -0
  27. data/lib/grape/formatter/xml.rb +1 -1
  28. data/lib/grape/middleware/error.rb +34 -25
  29. data/lib/grape/middleware/formatter.rb +9 -9
  30. data/lib/grape/middleware/stack.rb +110 -0
  31. data/lib/grape/middleware/versioner.rb +1 -1
  32. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  33. data/lib/grape/middleware/versioner/header.rb +3 -3
  34. data/lib/grape/path.rb +10 -2
  35. data/lib/grape/request.rb +1 -1
  36. data/lib/grape/router.rb +10 -19
  37. data/lib/grape/router/pattern.rb +2 -2
  38. data/lib/grape/router/route.rb +3 -3
  39. data/lib/grape/util/content_types.rb +1 -1
  40. data/lib/grape/util/inheritable_setting.rb +7 -2
  41. data/lib/grape/util/reverse_stackable_values.rb +45 -0
  42. data/lib/grape/util/stackable_values.rb +10 -11
  43. data/lib/grape/validations/attributes_iterator.rb +32 -7
  44. data/lib/grape/validations/params_scope.rb +33 -21
  45. data/lib/grape/validations/types.rb +4 -4
  46. data/lib/grape/validations/types/build_coercer.rb +9 -1
  47. data/lib/grape/validations/validators/all_or_none.rb +2 -2
  48. data/lib/grape/validations/validators/allow_blank.rb +10 -11
  49. data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
  50. data/lib/grape/validations/validators/base.rb +16 -6
  51. data/lib/grape/validations/validators/coerce.rb +3 -6
  52. data/lib/grape/validations/validators/default.rb +26 -1
  53. data/lib/grape/validations/validators/exactly_one_of.rb +1 -1
  54. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
  55. data/lib/grape/validations/validators/presence.rb +1 -1
  56. data/lib/grape/validations/validators/regexp.rb +1 -1
  57. data/lib/grape/validations/validators/values.rb +1 -1
  58. data/lib/grape/version.rb +1 -1
  59. data/spec/grape/api/custom_validations_spec.rb +3 -3
  60. data/spec/grape/api/parameters_modification_spec.rb +41 -0
  61. data/spec/grape/api_spec.rb +335 -108
  62. data/spec/grape/dsl/logger_spec.rb +1 -1
  63. data/spec/grape/dsl/middleware_spec.rb +25 -5
  64. data/spec/grape/dsl/request_response_spec.rb +20 -6
  65. data/spec/grape/dsl/validations_spec.rb +1 -1
  66. data/spec/grape/endpoint_spec.rb +166 -23
  67. data/spec/grape/entity_spec.rb +0 -2
  68. data/spec/grape/exceptions/body_parse_errors_spec.rb +37 -0
  69. data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
  70. data/spec/grape/exceptions/validation_spec.rb +10 -0
  71. data/spec/grape/integration/global_namespace_function_spec.rb +1 -1
  72. data/spec/grape/integration/rack_spec.rb +1 -1
  73. data/spec/grape/middleware/base_spec.rb +1 -1
  74. data/spec/grape/middleware/exception_spec.rb +2 -2
  75. data/spec/grape/middleware/formatter_spec.rb +4 -4
  76. data/spec/grape/middleware/stack_spec.rb +123 -0
  77. data/spec/grape/middleware/versioner/header_spec.rb +6 -6
  78. data/spec/grape/request_spec.rb +22 -22
  79. data/spec/grape/util/inheritable_setting_spec.rb +23 -0
  80. data/spec/grape/util/reverse_stackable_values_spec.rb +131 -0
  81. data/spec/grape/validations/params_scope_spec.rb +88 -1
  82. data/spec/grape/validations/validators/allow_blank_spec.rb +5 -0
  83. data/spec/grape/validations/validators/coerce_spec.rb +5 -5
  84. data/spec/grape/validations/validators/default_spec.rb +44 -0
  85. data/spec/grape/validations/validators/values_spec.rb +1 -1
  86. data/spec/grape/validations_spec.rb +36 -17
  87. data/spec/spec_helper.rb +1 -8
  88. data/spec/support/versioned_helpers.rb +3 -3
  89. metadata +13 -188
  90. data/gemfiles/rails_3.gemfile.lock +0 -225
  91. data/pkg/grape-0.16.1.gem +0 -0
  92. data/pkg/patch.diff +0 -24
  93. data/tmp/Gemfile.lock +0 -63
@@ -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*)/.freeze
10
- SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')?$/.freeze
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
- alias_method :attributes, :translator
15
+ alias attributes translator
16
16
 
17
17
  extend Forwardable
18
18
  def_delegators :pattern, :path, :origin
@@ -1,7 +1,7 @@
1
1
  module Grape
2
2
  module ContentTypes
3
3
  # Content types are listed in order of preference.
4
- CONTENT_TYPES = {
4
+ CONTENT_TYPES = { # rubocop:disable Style/MutableConstant
5
5
  xml: 'application/xml',
6
6
  serializable_hash: 'application/json',
7
7
  json: 'application/json',
@@ -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, :namespace_inheritable, :namespace_stackable
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
- attr_reader :new_values
6
- attr_reader :froozen_values
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
- @froozen_values = {}
11
+ @frozen_values = {}
12
12
  end
13
13
 
14
14
  def [](name)
15
- return @froozen_values[name] if @froozen_values.key? name
16
- value = [@inherited_values[name], @new_values[name]]
17
- value.compact!
18
- value.flatten!(1)
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
- fail if @froozen_values.key? name
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
- @froozen_values[key] = self[key].freeze
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
- @params = Array.wrap(scope.params(params))
11
+ @original_params = scope.params(params)
12
+ @params = Array.wrap(@original_params)
12
13
  end
13
14
 
14
- def each
15
- @params.each do |resource_params|
16
- @attrs.each_with_index do |attr_name, index|
17
- if resource_params.is_a?(Hash) && resource_params[attr_name].is_a?(Array)
18
- scope.index = index
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
- yield resource_params, attr_name
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 && params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
39
+ return false if @optional && (params(parameters).blank? ||
40
+ any_element_blank?(parameters))
41
+
39
42
  @dependent_on.each do |dependency|
40
- return false if params(parameters).try(:[], dependency).blank?
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)}#{parent_index}[#{name}]"
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 parent_index
63
- "[#{@parent.index}]" if @parent.present? && @parent.index.present?
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
- fail ArgumentError, "required field not exist: #{field}" unless field_opts
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
- fail Grape::Exceptions::MissingGroupTypeError.new if type.nil?
150
- fail Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
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).concat @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 = (options_key?(:values, :value, validations)) ? validations[:values][:value] : validations[: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
- fail ArgumentError, ':type may not be supplied with :types'
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
- fail ArgumentError, 'must supply type for coerce_with' unless validations.key?(:coerce)
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
- fail ArgumentError, 'coerce_with disallowed for type: JSON'
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
- fail Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values)
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
- if validator_class
334
- value = validator_class.new(attrs, options, doc_attrs[:required], self)
335
- @api.namespace_stackable(:validations, value)
336
- else
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
- fail Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
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 = 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
- fail Grape::Exceptions::Validation, params: all_keys, message: message(:all_or_none)
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).length > 0 && keys_in_common(resource_params).length < attrs.length }
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
- # root scope. validate if it's a required param. if it's optional, validate only if key exists in hash
14
- should_validate = @required || key_exists
15
- else # nested scope
16
- should_validate = # required param, and scope contains some values (if scoping element contains no values, treat as blank)
17
- (@required && params.present?) ||
18
- # optional param but key inside scoping element exists
19
- (!@required && params.key?(attr_name))
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 value == false || value.present?
23
+ return if false == value || value.present?
25
24
 
26
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:blank)
25
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:blank)
27
26
  end
28
27
  end
29
28
  end