grape 1.5.1 → 1.6.1

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -3
  3. data/CONTRIBUTING.md +1 -0
  4. data/README.md +50 -8
  5. data/UPGRADING.md +69 -0
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +13 -17
  8. data/lib/grape/api.rb +19 -14
  9. data/lib/grape/cookies.rb +2 -0
  10. data/lib/grape/dry_types.rb +12 -0
  11. data/lib/grape/dsl/callbacks.rb +1 -1
  12. data/lib/grape/dsl/desc.rb +3 -5
  13. data/lib/grape/dsl/headers.rb +5 -2
  14. data/lib/grape/dsl/helpers.rb +7 -5
  15. data/lib/grape/dsl/inside_route.rb +18 -9
  16. data/lib/grape/dsl/middleware.rb +4 -4
  17. data/lib/grape/dsl/parameters.rb +4 -4
  18. data/lib/grape/dsl/request_response.rb +9 -6
  19. data/lib/grape/dsl/routing.rb +7 -6
  20. data/lib/grape/dsl/settings.rb +5 -5
  21. data/lib/grape/endpoint.rb +21 -36
  22. data/lib/grape/error_formatter/json.rb +2 -6
  23. data/lib/grape/error_formatter/xml.rb +2 -6
  24. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  25. data/lib/grape/exceptions/validation.rb +2 -3
  26. data/lib/grape/exceptions/validation_errors.rb +1 -1
  27. data/lib/grape/formatter/json.rb +1 -0
  28. data/lib/grape/formatter/serializable_hash.rb +2 -1
  29. data/lib/grape/formatter/xml.rb +1 -0
  30. data/lib/grape/locale/en.yml +1 -1
  31. data/lib/grape/middleware/auth/base.rb +3 -3
  32. data/lib/grape/middleware/auth/dsl.rb +7 -1
  33. data/lib/grape/middleware/base.rb +5 -3
  34. data/lib/grape/middleware/error.rb +1 -1
  35. data/lib/grape/middleware/formatter.rb +4 -4
  36. data/lib/grape/middleware/stack.rb +14 -20
  37. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  38. data/lib/grape/middleware/versioner/header.rb +6 -4
  39. data/lib/grape/middleware/versioner/param.rb +1 -0
  40. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  41. data/lib/grape/middleware/versioner/path.rb +2 -0
  42. data/lib/grape/parser/json.rb +1 -1
  43. data/lib/grape/parser/xml.rb +1 -1
  44. data/lib/grape/path.rb +1 -0
  45. data/lib/grape/request.rb +3 -0
  46. data/lib/grape/router/pattern.rb +1 -1
  47. data/lib/grape/router/route.rb +2 -2
  48. data/lib/grape/router.rb +6 -0
  49. data/lib/grape/util/inheritable_setting.rb +1 -3
  50. data/lib/grape/util/json.rb +2 -0
  51. data/lib/grape/util/lazy_value.rb +3 -2
  52. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  53. data/lib/grape/validations/params_scope.rb +89 -56
  54. data/lib/grape/validations/types/array_coercer.rb +0 -2
  55. data/lib/grape/validations/types/custom_type_coercer.rb +3 -2
  56. data/lib/grape/validations/types/dry_type_coercer.rb +2 -11
  57. data/lib/grape/validations/types/invalid_value.rb +24 -0
  58. data/lib/grape/validations/types/json.rb +2 -3
  59. data/lib/grape/validations/types/primitive_coercer.rb +5 -7
  60. data/lib/grape/validations/types/set_coercer.rb +0 -3
  61. data/lib/grape/validations/types.rb +83 -12
  62. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  63. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  64. data/lib/grape/validations/validators/as_validator.rb +14 -0
  65. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  66. data/lib/grape/validations/validators/base.rb +75 -69
  67. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  68. data/lib/grape/validations/validators/default_validator.rb +51 -0
  69. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  70. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  71. data/lib/grape/validations/validators/multiple_params_base.rb +24 -20
  72. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  73. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  74. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  75. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  76. data/lib/grape/validations/validators/values_validator.rb +88 -0
  77. data/lib/grape/version.rb +1 -1
  78. data/lib/grape.rb +61 -24
  79. data/spec/grape/api/custom_validations_spec.rb +77 -45
  80. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  81. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  82. data/spec/grape/api/invalid_format_spec.rb +2 -0
  83. data/spec/grape/api/recognize_path_spec.rb +1 -1
  84. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  85. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  86. data/spec/grape/api_remount_spec.rb +25 -19
  87. data/spec/grape/api_spec.rb +510 -220
  88. data/spec/grape/dsl/callbacks_spec.rb +2 -1
  89. data/spec/grape/dsl/headers_spec.rb +39 -9
  90. data/spec/grape/dsl/helpers_spec.rb +3 -2
  91. data/spec/grape/dsl/inside_route_spec.rb +6 -4
  92. data/spec/grape/dsl/logger_spec.rb +16 -18
  93. data/spec/grape/dsl/middleware_spec.rb +2 -1
  94. data/spec/grape/dsl/parameters_spec.rb +2 -0
  95. data/spec/grape/dsl/request_response_spec.rb +1 -0
  96. data/spec/grape/dsl/routing_spec.rb +10 -7
  97. data/spec/grape/endpoint/declared_spec.rb +259 -12
  98. data/spec/grape/endpoint_spec.rb +77 -55
  99. data/spec/grape/entity_spec.rb +23 -23
  100. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  101. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  102. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  103. data/spec/grape/exceptions/validation_spec.rb +5 -3
  104. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  105. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  106. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  107. data/spec/grape/integration/rack_sendfile_spec.rb +1 -1
  108. data/spec/grape/loading_spec.rb +8 -8
  109. data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
  110. data/spec/grape/middleware/auth/strategies_spec.rb +60 -20
  111. data/spec/grape/middleware/base_spec.rb +24 -15
  112. data/spec/grape/middleware/error_spec.rb +2 -2
  113. data/spec/grape/middleware/exception_spec.rb +111 -161
  114. data/spec/grape/middleware/formatter_spec.rb +27 -6
  115. data/spec/grape/middleware/globals_spec.rb +7 -4
  116. data/spec/grape/middleware/stack_spec.rb +15 -14
  117. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  118. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  119. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  120. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  121. data/spec/grape/middleware/versioner_spec.rb +1 -1
  122. data/spec/grape/parser_spec.rb +4 -0
  123. data/spec/grape/path_spec.rb +52 -52
  124. data/spec/grape/presenters/presenter_spec.rb +7 -6
  125. data/spec/grape/request_spec.rb +6 -4
  126. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  127. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  128. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  129. data/spec/grape/util/stackable_values_spec.rb +7 -5
  130. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  131. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -0
  132. data/spec/grape/validations/params_scope_spec.rb +46 -10
  133. data/spec/grape/validations/single_attribute_iterator_spec.rb +2 -1
  134. data/spec/grape/validations/types/primitive_coercer_spec.rb +4 -4
  135. data/spec/grape/validations/types_spec.rb +8 -8
  136. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  137. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  138. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  139. data/spec/grape/validations/validators/coerce_spec.rb +139 -34
  140. data/spec/grape/validations/validators/default_spec.rb +72 -78
  141. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  142. data/spec/grape/validations/validators/except_values_spec.rb +3 -3
  143. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  144. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  145. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  146. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  147. data/spec/grape/validations/validators/values_spec.rb +183 -178
  148. data/spec/grape/validations_spec.rb +99 -58
  149. data/spec/integration/eager_load/eager_load_spec.rb +2 -2
  150. data/spec/integration/multi_json/json_spec.rb +1 -1
  151. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  152. data/spec/shared/versioning_examples.rb +32 -29
  153. data/spec/spec_helper.rb +12 -2
  154. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  155. data/spec/support/versioned_helpers.rb +1 -1
  156. metadata +117 -115
  157. data/lib/grape/validations/types/build_coercer.rb +0 -94
  158. data/lib/grape/validations/validators/all_or_none.rb +0 -15
  159. data/lib/grape/validations/validators/allow_blank.rb +0 -18
  160. data/lib/grape/validations/validators/as.rb +0 -16
  161. data/lib/grape/validations/validators/at_least_one_of.rb +0 -14
  162. data/lib/grape/validations/validators/coerce.rb +0 -88
  163. data/lib/grape/validations/validators/default.rb +0 -48
  164. data/lib/grape/validations/validators/exactly_one_of.rb +0 -16
  165. data/lib/grape/validations/validators/except_values.rb +0 -22
  166. data/lib/grape/validations/validators/mutual_exclusion.rb +0 -15
  167. data/lib/grape/validations/validators/presence.rb +0 -12
  168. data/lib/grape/validations/validators/regexp.rb +0 -13
  169. data/lib/grape/validations/validators/same_as.rb +0 -26
  170. data/lib/grape/validations/validators/values.rb +0 -83
@@ -2,87 +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, skip_value|
47
- next if skip_value
48
- next if !@scope.required? && empty_val
49
- next unless @scope.meets_dependency?(val, params)
50
- begin
51
- validate_param!(attr_name, val) if @required || val.respond_to?(:key?) && val.key?(attr_name)
52
- rescue Grape::Exceptions::Validation => e
53
- array_errors << e
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
58
+ end
54
59
  end
60
+
61
+ raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
55
62
  end
56
63
 
57
- raise Grape::Exceptions::ValidationArrayErrors, array_errors if array_errors.any?
58
- 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
59
72
 
60
- def self.convert_to_short_name(klass)
61
- ret = klass.name.gsub(/::/, '/')
62
- ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
63
- ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
64
- ret.tr!('-', '_')
65
- ret.downcase!
66
- File.basename(ret, '_validator')
67
- end
73
+ def self.inherited(klass)
74
+ return unless klass.name.present?
68
75
 
69
- def self.inherited(klass)
70
- return unless klass.name.present?
71
- Validations.register_validator(convert_to_short_name(klass), klass)
72
- end
76
+ Validations.register_validator(convert_to_short_name(klass), klass)
77
+ end
73
78
 
74
- def message(default_key = nil)
75
- options = instance_variable_get(:@option)
76
- options_key?(:message) ? options[:message] : default_key
77
- end
79
+ def message(default_key = nil)
80
+ options = instance_variable_get(:@option)
81
+ options_key?(:message) ? options[:message] : default_key
82
+ end
78
83
 
79
- def options_key?(key, options = nil)
80
- options = instance_variable_get(:@option) if options.nil?
81
- options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
82
- 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
83
88
 
84
- def fail_fast?
85
- @fail_fast
89
+ def fail_fast?
90
+ @fail_fast
91
+ end
86
92
  end
87
93
  end
88
94
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
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.
42
+ #
43
+ # See {Types.build_coercer}
44
+ #
45
+ # @return [Object]
46
+ attr_reader :converter
47
+
48
+ def valid_type?(val)
49
+ !val.is_a?(Types::InvalidValue)
50
+ end
51
+
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
58
+
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
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class DefaultValidator < Base
7
+ def initialize(attrs, options, required, scope, **opts)
8
+ @default = options
9
+ super
10
+ end
11
+
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)
26
+
27
+ validate_param!(attr_name, resource_params) if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
28
+ end
29
+ end
30
+
31
+ private
32
+
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
41
+
42
+ # make a best effort to dup the object
43
+ def duplicate(obj)
44
+ obj.dup
45
+ rescue TypeError
46
+ obj
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class ExactlyOneOfValidator < MultipleParamsBase
7
+ def validate_params!(params)
8
+ keys = keys_in_common(params)
9
+ return if keys.length == 1
10
+ raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.length.zero?
11
+
12
+ raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class ExceptValuesValidator < Base
7
+ def initialize(attrs, options, required, scope, **opts)
8
+ @except = options.is_a?(Hash) ? options[:value] : options
9
+ super
10
+ end
11
+
12
+ def validate_param!(attr_name, params)
13
+ return unless params.respond_to?(:key?) && params.key?(attr_name)
14
+
15
+ excepts = @except.is_a?(Proc) ? @except.call : @except
16
+ return if excepts.nil?
17
+
18
+ param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
19
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:except_values)) if param_array.any? { |param| excepts.include?(param) }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -2,32 +2,36 @@
2
2
 
3
3
  module Grape
4
4
  module Validations
5
- class MultipleParamsBase < Base
6
- def validate!(params)
7
- attributes = MultipleAttributesIterator.new(self, @scope, params)
8
- array_errors = []
9
-
10
- attributes.each do |resource_params, skip_value|
11
- next if skip_value
12
- begin
13
- validate_params!(resource_params)
14
- rescue Grape::Exceptions::Validation => e
15
- array_errors << e
5
+ module Validators
6
+ class MultipleParamsBase < Base
7
+ def validate!(params)
8
+ attributes = MultipleAttributesIterator.new(self, @scope, params)
9
+ array_errors = []
10
+
11
+ attributes.each do |resource_params, skip_value|
12
+ next if skip_value
13
+
14
+ begin
15
+ validate_params!(resource_params)
16
+ rescue Grape::Exceptions::Validation => e
17
+ array_errors << e
18
+ end
16
19
  end
20
+
21
+ raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
17
22
  end
18
23
 
19
- raise Grape::Exceptions::ValidationArrayErrors, array_errors if array_errors.any?
20
- end
24
+ private
21
25
 
22
- private
26
+ def keys_in_common(resource_params)
27
+ return [] unless resource_params.is_a?(Hash)
23
28
 
24
- def keys_in_common(resource_params)
25
- return [] unless resource_params.is_a?(Hash)
26
- all_keys & resource_params.keys.map! { |attr| @scope.full_name(attr) }
27
- end
29
+ all_keys & resource_params.keys.map! { |attr| @scope.full_name(attr) }
30
+ end
28
31
 
29
- def all_keys
30
- attrs.map { |attr| @scope.full_name(attr) }
32
+ def all_keys
33
+ attrs.map { |attr| @scope.full_name(attr) }
34
+ end
31
35
  end
32
36
  end
33
37
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class MutualExclusionValidator < MultipleParamsBase
7
+ def validate_params!(params)
8
+ keys = keys_in_common(params)
9
+ return if keys.length <= 1
10
+
11
+ raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class PresenceValidator < Base
7
+ def validate_param!(attr_name, params)
8
+ return if params.respond_to?(:key?) && params.key?(attr_name)
9
+
10
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class RegexpValidator < Base
7
+ def validate_param!(attr_name, params)
8
+ return unless params.respond_to?(:key?) && params.key?(attr_name)
9
+ return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.match?((options_key?(:value) ? @option[:value] : @option)) }
10
+
11
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class SameAsValidator < Base
7
+ def validate_param!(attr_name, params)
8
+ confirmation = options_key?(:value) ? @option[:value] : @option
9
+ return if params[attr_name] == params[confirmation]
10
+
11
+ raise Grape::Exceptions::Validation.new(
12
+ params: [@scope.full_name(attr_name)],
13
+ message: build_message
14
+ )
15
+ end
16
+
17
+ private
18
+
19
+ def build_message
20
+ if options_key?(:message)
21
+ @option[:message]
22
+ else
23
+ format I18n.t(:same_as, scope: 'grape.errors.messages'), parameter: @option
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class ValuesValidator < Base
7
+ def initialize(attrs, options, required, scope, **opts)
8
+ if options.is_a?(Hash)
9
+ @excepts = options[:except]
10
+ @values = options[:value]
11
+ @proc = options[:proc]
12
+
13
+ warn '[DEPRECATION] The values validator except option is deprecated. ' \
14
+ 'Use the except validator instead.' if @excepts
15
+
16
+ raise ArgumentError, 'proc must be a Proc' if @proc && !@proc.is_a?(Proc)
17
+
18
+ warn '[DEPRECATION] The values validator proc option is deprecated. ' \
19
+ 'The lambda expression can now be assigned directly to values.' if @proc
20
+ else
21
+ @excepts = nil
22
+ @values = nil
23
+ @proc = nil
24
+ @values = options
25
+ end
26
+ super
27
+ end
28
+
29
+ def validate_param!(attr_name, params)
30
+ return unless params.is_a?(Hash)
31
+
32
+ val = params[attr_name]
33
+
34
+ return if val.nil? && !required_for_root_scope?
35
+
36
+ # don't forget that +false.blank?+ is true
37
+ return if val != false && val.blank? && @allow_blank
38
+
39
+ param_array = val.nil? ? [nil] : Array.wrap(val)
40
+
41
+ raise validation_exception(attr_name, except_message) \
42
+ unless check_excepts(param_array)
43
+
44
+ raise validation_exception(attr_name, message(:values)) \
45
+ unless check_values(param_array, attr_name)
46
+
47
+ raise validation_exception(attr_name, message(:values)) \
48
+ if @proc && !param_array.all? { |param| @proc.call(param) }
49
+ end
50
+
51
+ private
52
+
53
+ def check_values(param_array, attr_name)
54
+ values = @values.is_a?(Proc) && @values.arity.zero? ? @values.call : @values
55
+ return true if values.nil?
56
+
57
+ begin
58
+ return param_array.all? { |param| values.call(param) } if values.is_a? Proc
59
+ rescue StandardError => e
60
+ warn "Error '#{e}' raised while validating attribute '#{attr_name}'"
61
+ return false
62
+ end
63
+ param_array.all? { |param| values.include?(param) }
64
+ end
65
+
66
+ def check_excepts(param_array)
67
+ excepts = @excepts.is_a?(Proc) ? @excepts.call : @excepts
68
+ return true if excepts.nil?
69
+
70
+ param_array.none? { |param| excepts.include?(param) }
71
+ end
72
+
73
+ def except_message
74
+ options = instance_variable_get(:@option)
75
+ options_key?(:except_message) ? options[:except_message] : message(:except_values)
76
+ end
77
+
78
+ def required_for_root_scope?
79
+ @required && @scope.root?
80
+ end
81
+
82
+ def validation_exception(attr_name, message)
83
+ Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
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.1'
5
+ VERSION = '1.6.1'
6
6
  end