grape 1.3.3 → 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.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -2
  3. data/CONTRIBUTING.md +2 -1
  4. data/README.md +135 -23
  5. data/UPGRADING.md +237 -46
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +34 -42
  8. data/lib/grape/api.rb +21 -16
  9. data/lib/grape/cookies.rb +2 -0
  10. data/lib/grape/dsl/callbacks.rb +1 -1
  11. data/lib/grape/dsl/desc.rb +3 -5
  12. data/lib/grape/dsl/headers.rb +5 -2
  13. data/lib/grape/dsl/helpers.rb +8 -5
  14. data/lib/grape/dsl/inside_route.rb +72 -53
  15. data/lib/grape/dsl/middleware.rb +4 -4
  16. data/lib/grape/dsl/parameters.rb +11 -7
  17. data/lib/grape/dsl/request_response.rb +9 -6
  18. data/lib/grape/dsl/routing.rb +8 -9
  19. data/lib/grape/dsl/settings.rb +5 -5
  20. data/lib/grape/dsl/validations.rb +18 -1
  21. data/lib/grape/eager_load.rb +1 -1
  22. data/lib/grape/endpoint.rb +29 -42
  23. data/lib/grape/error_formatter/json.rb +2 -6
  24. data/lib/grape/error_formatter/xml.rb +2 -6
  25. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  26. data/lib/grape/exceptions/validation.rb +2 -3
  27. data/lib/grape/exceptions/validation_errors.rb +1 -1
  28. data/lib/grape/formatter/json.rb +1 -0
  29. data/lib/grape/formatter/serializable_hash.rb +2 -1
  30. data/lib/grape/formatter/xml.rb +1 -0
  31. data/lib/grape/locale/en.yml +1 -1
  32. data/lib/grape/middleware/auth/base.rb +3 -3
  33. data/lib/grape/middleware/auth/dsl.rb +7 -1
  34. data/lib/grape/middleware/base.rb +6 -3
  35. data/lib/grape/middleware/error.rb +11 -13
  36. data/lib/grape/middleware/formatter.rb +7 -7
  37. data/lib/grape/middleware/stack.rb +10 -3
  38. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  39. data/lib/grape/middleware/versioner/header.rb +6 -4
  40. data/lib/grape/middleware/versioner/param.rb +1 -0
  41. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  42. data/lib/grape/middleware/versioner/path.rb +2 -0
  43. data/lib/grape/parser/json.rb +1 -1
  44. data/lib/grape/parser/xml.rb +1 -1
  45. data/lib/grape/path.rb +1 -0
  46. data/lib/grape/request.rb +4 -1
  47. data/lib/grape/router/attribute_translator.rb +3 -3
  48. data/lib/grape/router/pattern.rb +1 -1
  49. data/lib/grape/router/route.rb +2 -2
  50. data/lib/grape/router.rb +31 -30
  51. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  52. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  53. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  54. data/lib/grape/util/base_inheritable.rb +2 -2
  55. data/lib/grape/util/inheritable_setting.rb +1 -3
  56. data/lib/grape/util/lazy_value.rb +4 -2
  57. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  58. data/lib/grape/validations/attributes_iterator.rb +8 -0
  59. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  60. data/lib/grape/validations/params_scope.rb +97 -62
  61. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  62. data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
  63. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  64. data/lib/grape/validations/types/invalid_value.rb +24 -0
  65. data/lib/grape/validations/types/json.rb +2 -1
  66. data/lib/grape/validations/types/primitive_coercer.rb +4 -5
  67. data/lib/grape/validations/types.rb +1 -4
  68. data/lib/grape/validations/validator_factory.rb +1 -1
  69. data/lib/grape/validations/validators/all_or_none.rb +8 -5
  70. data/lib/grape/validations/validators/allow_blank.rb +9 -7
  71. data/lib/grape/validations/validators/as.rb +6 -8
  72. data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
  73. data/lib/grape/validations/validators/base.rb +74 -69
  74. data/lib/grape/validations/validators/coerce.rb +63 -76
  75. data/lib/grape/validations/validators/default.rb +36 -34
  76. data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
  77. data/lib/grape/validations/validators/except_values.rb +13 -11
  78. data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
  79. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
  80. data/lib/grape/validations/validators/presence.rb +7 -4
  81. data/lib/grape/validations/validators/regexp.rb +8 -5
  82. data/lib/grape/validations/validators/same_as.rb +18 -15
  83. data/lib/grape/validations/validators/values.rb +61 -56
  84. data/lib/grape/validations.rb +6 -0
  85. data/lib/grape/version.rb +1 -1
  86. data/lib/grape.rb +7 -3
  87. data/spec/grape/api/custom_validations_spec.rb +77 -45
  88. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  89. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  90. data/spec/grape/api/invalid_format_spec.rb +2 -0
  91. data/spec/grape/api/recognize_path_spec.rb +1 -1
  92. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  93. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  94. data/spec/grape/api_remount_spec.rb +25 -19
  95. data/spec/grape/api_spec.rb +576 -211
  96. data/spec/grape/dsl/callbacks_spec.rb +2 -1
  97. data/spec/grape/dsl/headers_spec.rb +39 -9
  98. data/spec/grape/dsl/helpers_spec.rb +3 -2
  99. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  100. data/spec/grape/dsl/logger_spec.rb +16 -18
  101. data/spec/grape/dsl/middleware_spec.rb +2 -1
  102. data/spec/grape/dsl/parameters_spec.rb +2 -0
  103. data/spec/grape/dsl/request_response_spec.rb +1 -0
  104. data/spec/grape/dsl/routing_spec.rb +10 -7
  105. data/spec/grape/endpoint/declared_spec.rb +848 -0
  106. data/spec/grape/endpoint_spec.rb +77 -589
  107. data/spec/grape/entity_spec.rb +29 -23
  108. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  109. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  110. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  111. data/spec/grape/exceptions/validation_spec.rb +5 -3
  112. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  113. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  114. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  115. data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
  116. data/spec/grape/loading_spec.rb +8 -8
  117. data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
  118. data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
  119. data/spec/grape/middleware/base_spec.rb +24 -15
  120. data/spec/grape/middleware/error_spec.rb +3 -3
  121. data/spec/grape/middleware/exception_spec.rb +111 -161
  122. data/spec/grape/middleware/formatter_spec.rb +28 -7
  123. data/spec/grape/middleware/globals_spec.rb +7 -4
  124. data/spec/grape/middleware/stack_spec.rb +15 -12
  125. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  126. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  127. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  128. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  129. data/spec/grape/middleware/versioner_spec.rb +1 -1
  130. data/spec/grape/parser_spec.rb +4 -0
  131. data/spec/grape/path_spec.rb +52 -52
  132. data/spec/grape/presenters/presenter_spec.rb +7 -6
  133. data/spec/grape/request_spec.rb +6 -4
  134. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  135. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  136. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  137. data/spec/grape/util/stackable_values_spec.rb +7 -5
  138. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  139. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
  140. data/spec/grape/validations/params_scope_spec.rb +72 -10
  141. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
  142. data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
  143. data/spec/grape/validations/types_spec.rb +8 -8
  144. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  145. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  146. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  147. data/spec/grape/validations/validators/coerce_spec.rb +248 -33
  148. data/spec/grape/validations/validators/default_spec.rb +121 -78
  149. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  150. data/spec/grape/validations/validators/except_values_spec.rb +4 -3
  151. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  152. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  153. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  154. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  155. data/spec/grape/validations/validators/values_spec.rb +183 -178
  156. data/spec/grape/validations_spec.rb +342 -29
  157. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  158. data/spec/integration/multi_json/json_spec.rb +1 -1
  159. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  160. data/spec/shared/versioning_examples.rb +32 -29
  161. data/spec/spec_helper.rb +12 -12
  162. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  163. data/spec/support/chunks.rb +14 -0
  164. data/spec/support/versioned_helpers.rb +4 -6
  165. metadata +110 -102
@@ -7,7 +7,7 @@ module Grape
7
7
 
8
8
  def yield_attributes(val, attrs)
9
9
  attrs.each do |attr_name|
10
- yield val, attr_name, empty?(val)
10
+ yield val, attr_name, empty?(val), skip?(val)
11
11
  end
12
12
  end
13
13
 
@@ -52,10 +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)
56
+
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
 
@@ -103,13 +104,25 @@ module Grape
103
104
  # passed, or if the type also implements a parse() method.
104
105
  type
105
106
  elsif type.is_a?(Enumerable)
106
- ->(value) { value.respond_to?(:all?) && value.all? { |item| item.is_a? type[0] } }
107
+ lambda do |value|
108
+ value.is_a?(Enumerable) && value.all? do |val|
109
+ recursive_type_check(type.first, val)
110
+ end
111
+ end
107
112
  else
108
113
  # By default, do a simple type check
109
114
  ->(value) { value.is_a? type }
110
115
  end
111
116
  end
112
117
 
118
+ def recursive_type_check(type, value)
119
+ if type.is_a?(Enumerable) && value.is_a?(Enumerable)
120
+ value.all? { |val| recursive_type_check(type.first, val) }
121
+ else
122
+ !type.is_a?(Enumerable) && value.is_a?(type)
123
+ end
124
+ end
125
+
113
126
  # Enforce symbolized keys for complex types
114
127
  # by wrapping the coercion method such that
115
128
  # any Hash objects in the immediate heirarchy
@@ -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
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Types
6
+ # Instances of this class may be used as tokens to denote that a parameter value could not be
7
+ # coerced. The given message will be used as a validation error.
8
+ class InvalidValue
9
+ attr_reader :message
10
+
11
+ def initialize(message = nil)
12
+ @message = message
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # only exists to make it shorter for external use
20
+ module Grape
21
+ module Types
22
+ InvalidValue = Class.new(Grape::Validations::Types::InvalidValue)
23
+ end
24
+ end
@@ -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)
@@ -37,7 +37,6 @@ module Grape
37
37
  def call(val)
38
38
  return InvalidValue.new if reject?(val)
39
39
  return nil if val.nil? || treat_as_nil?(val)
40
- return '' if val == ''
41
40
 
42
41
  super
43
42
  end
@@ -60,7 +59,7 @@ module Grape
60
59
  # absence of a value and coerces it into nil. See a discussion there
61
60
  # https://github.com/ruby-grape/grape/pull/2045
62
61
  def treat_as_nil?(val)
63
- val == '' && type == Grape::API::Boolean
62
+ val == '' && type != String
64
63
  end
65
64
  end
66
65
  end
@@ -7,6 +7,7 @@ require_relative 'types/multiple_type_coercer'
7
7
  require_relative 'types/variant_collection_coercer'
8
8
  require_relative 'types/json'
9
9
  require_relative 'types/file'
10
+ require_relative 'types/invalid_value'
10
11
 
11
12
  module Grape
12
13
  module Validations
@@ -21,10 +22,6 @@ module Grape
21
22
  # and {Grape::Dsl::Parameters#optional}. The main
22
23
  # entry point for this process is {Types.build_coercer}.
23
24
  module Types
24
- # Instances of this class may be used as tokens to denote that
25
- # a parameter value could not be coerced.
26
- class InvalidValue; end
27
-
28
25
  # Types representing a single value, which are coerced.
29
26
  PRIMITIVES = [
30
27
  # Numerical
@@ -8,7 +8,7 @@ module Grape
8
8
  options[:options],
9
9
  options[:required],
10
10
  options[:params_scope],
11
- options[:opts])
11
+ **options[:opts])
12
12
  end
13
13
  end
14
14
  end
@@ -4,11 +4,14 @@ require 'grape/validations/validators/multiple_params_base'
4
4
 
5
5
  module Grape
6
6
  module Validations
7
- class AllOrNoneOfValidator < MultipleParamsBase
8
- def validate_params!(params)
9
- keys = keys_in_common(params)
10
- return if keys.empty? || keys.length == all_keys.length
11
- raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
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
- class AllowBlankValidator < Base
6
- def validate_param!(attr_name, params)
7
- return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
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
- value = params[attr_name]
10
- value = value.strip if value.respond_to?(:strip)
10
+ value = params[attr_name]
11
+ value = value.strip if value.respond_to?(:strip)
11
12
 
12
- return if value == false || value.present?
13
+ return if value == false || value.present?
13
14
 
14
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:blank))
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
- 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]
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
- class AtLeastOneOfValidator < MultipleParamsBase
8
- def validate_params!(params)
9
- return unless keys_in_common(params).empty?
10
- raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
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
@@ -2,88 +2,93 @@
2
2
 
3
3
  module Grape
4
4
  module Validations
5
- class Base
6
- attr_reader :attrs
5
+ module Validators
6
+ class Base
7
+ attr_reader :attrs
7
8
 
8
- # Creates a new Validator from options specified
9
- # by a +requires+ or +optional+ directive during
10
- # parameter definition.
11
- # @param attrs [Array] names of attributes to which the Validator applies
12
- # @param options [Object] implementation-dependent Validator options
13
- # @param required [Boolean] attribute(s) are required or optional
14
- # @param scope [ParamsScope] parent scope for this Validator
15
- # @param opts [Hash] additional validation options
16
- def initialize(attrs, options, required, scope, opts = {})
17
- @attrs = Array(attrs)
18
- @option = options
19
- @required = required
20
- @scope = scope
21
- @fail_fast = opts[:fail_fast] || false
22
- @allow_blank = opts[:allow_blank] || false
23
- end
9
+ # Creates a new Validator from options specified
10
+ # by a +requires+ or +optional+ directive during
11
+ # parameter definition.
12
+ # @param attrs [Array] names of attributes to which the Validator applies
13
+ # @param options [Object] implementation-dependent Validator options
14
+ # @param required [Boolean] attribute(s) are required or optional
15
+ # @param scope [ParamsScope] parent scope for this Validator
16
+ # @param opts [Array] additional validation options
17
+ def initialize(attrs, options, required, scope, *opts)
18
+ @attrs = Array(attrs)
19
+ @option = options
20
+ @required = required
21
+ @scope = scope
22
+ opts = opts.any? ? opts.shift : {}
23
+ @fail_fast = opts.fetch(:fail_fast, false)
24
+ @allow_blank = opts.fetch(:allow_blank, false)
25
+ end
24
26
 
25
- # Validates a given request.
26
- # @note Override #validate! unless you need to access the entire request.
27
- # @param request [Grape::Request] the request currently being handled
28
- # @raise [Grape::Exceptions::Validation] if validation failed
29
- # @return [void]
30
- def validate(request)
31
- return unless @scope.should_validate?(request.params)
32
- validate!(request.params)
33
- end
27
+ # Validates a given request.
28
+ # @note Override #validate! unless you need to access the entire request.
29
+ # @param request [Grape::Request] the request currently being handled
30
+ # @raise [Grape::Exceptions::Validation] if validation failed
31
+ # @return [void]
32
+ def validate(request)
33
+ return unless @scope.should_validate?(request.params)
34
+
35
+ validate!(request.params)
36
+ end
34
37
 
35
- # Validates a given parameter hash.
36
- # @note Override #validate if you need to access the entire request.
37
- # @param params [Hash] parameters to validate
38
- # @raise [Grape::Exceptions::Validation] if validation failed
39
- # @return [void]
40
- def validate!(params)
41
- attributes = SingleAttributeIterator.new(self, @scope, params)
42
- # we collect errors inside array because
43
- # there may be more than one error per field
44
- array_errors = []
38
+ # Validates a given parameter hash.
39
+ # @note Override #validate if you need to access the entire request.
40
+ # @param params [Hash] parameters to validate
41
+ # @raise [Grape::Exceptions::Validation] if validation failed
42
+ # @return [void]
43
+ def validate!(params)
44
+ attributes = SingleAttributeIterator.new(self, @scope, params)
45
+ # we collect errors inside array because
46
+ # there may be more than one error per field
47
+ array_errors = []
45
48
 
46
- attributes.each do |val, attr_name, empty_val|
47
- next if !@scope.required? && empty_val
48
- next unless @scope.meets_dependency?(val, params)
49
- begin
50
- if @required || val.respond_to?(:key?) && val.key?(attr_name)
51
- validate_param!(attr_name, val)
49
+ attributes.each do |val, attr_name, empty_val, skip_value|
50
+ next if skip_value
51
+ next if !@scope.required? && empty_val
52
+ next unless @scope.meets_dependency?(val, params)
53
+
54
+ begin
55
+ validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
56
+ rescue Grape::Exceptions::Validation => e
57
+ array_errors << e
52
58
  end
53
- rescue Grape::Exceptions::Validation => e
54
- array_errors << e
55
59
  end
60
+
61
+ raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
56
62
  end
57
63
 
58
- raise Grape::Exceptions::ValidationArrayErrors, array_errors if array_errors.any?
59
- end
64
+ def self.convert_to_short_name(klass)
65
+ ret = klass.name.gsub(/::/, '/')
66
+ ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
67
+ ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
68
+ ret.tr!('-', '_')
69
+ ret.downcase!
70
+ File.basename(ret, '_validator')
71
+ end
60
72
 
61
- def self.convert_to_short_name(klass)
62
- ret = klass.name.gsub(/::/, '/')
63
- ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
64
- ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
65
- ret.tr!('-', '_')
66
- ret.downcase!
67
- File.basename(ret, '_validator')
68
- end
73
+ def self.inherited(klass)
74
+ return unless klass.name.present?
69
75
 
70
- def self.inherited(klass)
71
- return unless klass.name.present?
72
- Validations.register_validator(convert_to_short_name(klass), klass)
73
- end
76
+ Validations.register_validator(convert_to_short_name(klass), klass)
77
+ end
74
78
 
75
- def message(default_key = nil)
76
- options = instance_variable_get(:@option)
77
- options_key?(:message) ? options[:message] : default_key
78
- end
79
+ def message(default_key = nil)
80
+ options = instance_variable_get(:@option)
81
+ options_key?(:message) ? options[:message] : default_key
82
+ end
79
83
 
80
- def options_key?(key, options = nil)
81
- options = instance_variable_get(:@option) if options.nil?
82
- options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
83
- end
84
+ def options_key?(key, options = nil)
85
+ options = instance_variable_get(:@option) if options.nil?
86
+ options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
87
+ end
84
88
 
85
- def fail_fast?
86
- @fail_fast
89
+ def fail_fast?
90
+ @fail_fast
91
+ end
87
92
  end
88
93
  end
89
94
  end
@@ -1,87 +1,74 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
- class API
5
- class Boolean
6
- def self.build(val)
7
- return nil if val != true && val != false
8
-
9
- new
10
- end
11
- end
12
-
13
- class Instance
14
- Boolean = Grape::API::Boolean
15
- end
16
- end
17
-
18
4
  module Validations
19
- class CoerceValidator < Base
20
- def initialize(*_args)
21
- super
22
-
23
- @converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
24
- type
25
- else
26
- Types.build_coercer(type, method: @option[:method])
27
- end
28
- end
29
-
30
- def validate(request)
31
- super
32
- end
33
-
34
- def validate_param!(attr_name, params)
35
- raise validation_exception(attr_name) unless params.is_a? Hash
36
-
37
- new_value = coerce_value(params[attr_name])
38
-
39
- raise validation_exception(attr_name) unless valid_type?(new_value)
40
-
41
- # Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
42
- # which looses wrappers for hashes and arrays after reassigning values
5
+ module Validators
6
+ class CoerceValidator < Base
7
+ def initialize(attrs, options, required, scope, **opts)
8
+ super
9
+
10
+ @converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
11
+ type
12
+ else
13
+ Types.build_coercer(type, method: @option[:method])
14
+ end
15
+ end
16
+
17
+ def validate_param!(attr_name, params)
18
+ raise validation_exception(attr_name) unless params.is_a? Hash
19
+
20
+ new_value = coerce_value(params[attr_name])
21
+
22
+ raise validation_exception(attr_name, new_value.message) unless valid_type?(new_value)
23
+
24
+ # Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
25
+ # which looses wrappers for hashes and arrays after reassigning values
26
+ #
27
+ # h = Hashie::Mash.new(list: [1, 2, 3, 4])
28
+ # => #<Hashie::Mash list=#<Hashie::Array [1, 2, 3, 4]>>
29
+ # list = h.list
30
+ # h[:list] = list
31
+ # h
32
+ # => #<Hashie::Mash list=[1, 2, 3, 4]>
33
+ return if params[attr_name].instance_of?(new_value.class) && params[attr_name] == new_value
34
+
35
+ params[attr_name] = new_value
36
+ end
37
+
38
+ private
39
+
40
+ # @!attribute [r] converter
41
+ # Object that will be used for parameter coercion and type checking.
43
42
  #
44
- # h = Hashie::Mash.new(list: [1, 2, 3, 4])
45
- # => #<Hashie::Mash list=#<Hashie::Array [1, 2, 3, 4]>>
46
- # list = h.list
47
- # h[:list] = list
48
- # h
49
- # => #<Hashie::Mash list=[1, 2, 3, 4]>
50
- return if params[attr_name].class == new_value.class && params[attr_name] == new_value
51
-
52
- params[attr_name] = new_value
53
- end
54
-
55
- private
56
-
57
- # @!attribute [r] converter
58
- # Object that will be used for parameter coercion and type checking.
59
- #
60
- # See {Types.build_coercer}
61
- #
62
- # @return [Object]
63
- attr_reader :converter
64
-
65
- def valid_type?(val)
66
- !val.is_a?(Types::InvalidValue)
67
- end
43
+ # See {Types.build_coercer}
44
+ #
45
+ # @return [Object]
46
+ attr_reader :converter
68
47
 
69
- def coerce_value(val)
70
- converter.call(val)
71
- # Some custom types might fail, so it should be treated as an invalid value
72
- rescue StandardError
73
- Types::InvalidValue.new
74
- end
48
+ def valid_type?(val)
49
+ !val.is_a?(Types::InvalidValue)
50
+ end
75
51
 
76
- # Type to which the parameter will be coerced.
77
- #
78
- # @return [Class]
79
- def type
80
- @option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
81
- end
52
+ def coerce_value(val)
53
+ converter.call(val)
54
+ # Some custom types might fail, so it should be treated as an invalid value
55
+ rescue StandardError
56
+ Types::InvalidValue.new
57
+ end
82
58
 
83
- def validation_exception(attr_name)
84
- Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:coerce))
59
+ # Type to which the parameter will be coerced.
60
+ #
61
+ # @return [Class]
62
+ def type
63
+ @option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
64
+ end
65
+
66
+ def validation_exception(attr_name, custom_msg = nil)
67
+ Grape::Exceptions::Validation.new(
68
+ params: [@scope.full_name(attr_name)],
69
+ message: custom_msg || message(:coerce)
70
+ )
71
+ end
85
72
  end
86
73
  end
87
74
  end
@@ -2,47 +2,49 @@
2
2
 
3
3
  module Grape
4
4
  module Validations
5
- class DefaultValidator < Base
6
- def initialize(attrs, options, required, scope, opts = {})
7
- @default = options
8
- super
9
- end
5
+ module Validators
6
+ class DefaultValidator < Base
7
+ def initialize(attrs, options, required, scope, **opts)
8
+ @default = options
9
+ super
10
+ end
10
11
 
11
- def validate_param!(attr_name, params)
12
- params[attr_name] = if @default.is_a? Proc
13
- @default.call
14
- elsif @default.frozen? || !duplicatable?(@default)
15
- @default
16
- else
17
- duplicate(@default)
18
- end
19
- end
12
+ def validate_param!(attr_name, params)
13
+ params[attr_name] = if @default.is_a? Proc
14
+ @default.call
15
+ elsif @default.frozen? || !duplicatable?(@default)
16
+ @default
17
+ else
18
+ duplicate(@default)
19
+ end
20
+ end
21
+
22
+ def validate!(params)
23
+ attrs = SingleAttributeIterator.new(self, @scope, params)
24
+ attrs.each do |resource_params, attr_name|
25
+ next unless @scope.meets_dependency?(resource_params, params)
20
26
 
21
- def validate!(params)
22
- attrs = SingleAttributeIterator.new(self, @scope, params)
23
- attrs.each do |resource_params, attr_name|
24
- if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
25
- validate_param!(attr_name, resource_params)
27
+ validate_param!(attr_name, resource_params) if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
26
28
  end
27
29
  end
28
- end
29
30
 
30
- private
31
+ private
31
32
 
32
- # return true if we might be able to dup this object
33
- def duplicatable?(obj)
34
- !obj.nil? &&
35
- obj != true &&
36
- obj != false &&
37
- !obj.is_a?(Symbol) &&
38
- !obj.is_a?(Numeric)
39
- end
33
+ # return true if we might be able to dup this object
34
+ def duplicatable?(obj)
35
+ !obj.nil? &&
36
+ obj != true &&
37
+ obj != false &&
38
+ !obj.is_a?(Symbol) &&
39
+ !obj.is_a?(Numeric)
40
+ end
40
41
 
41
- # make a best effort to dup the object
42
- def duplicate(obj)
43
- obj.dup
44
- rescue TypeError
45
- obj
42
+ # make a best effort to dup the object
43
+ def duplicate(obj)
44
+ obj.dup
45
+ rescue TypeError
46
+ obj
47
+ end
46
48
  end
47
49
  end
48
50
  end