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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +111 -2
- data/CONTRIBUTING.md +2 -1
- data/README.md +135 -23
- data/UPGRADING.md +237 -46
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +34 -42
- data/lib/grape/api.rb +21 -16
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/headers.rb +5 -2
- data/lib/grape/dsl/helpers.rb +8 -5
- data/lib/grape/dsl/inside_route.rb +72 -53
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +11 -7
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +8 -9
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/dsl/validations.rb +18 -1
- data/lib/grape/eager_load.rb +1 -1
- data/lib/grape/endpoint.rb +29 -42
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/validation.rb +2 -3
- data/lib/grape/exceptions/validation_errors.rb +1 -1
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/auth/dsl.rb +7 -1
- data/lib/grape/middleware/base.rb +6 -3
- data/lib/grape/middleware/error.rb +11 -13
- data/lib/grape/middleware/formatter.rb +7 -7
- data/lib/grape/middleware/stack.rb +10 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/parser/json.rb +1 -1
- data/lib/grape/parser/xml.rb +1 -1
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +4 -1
- data/lib/grape/router/attribute_translator.rb +3 -3
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +31 -30
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
- data/lib/grape/util/base_inheritable.rb +2 -2
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +4 -2
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +97 -62
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +4 -5
- data/lib/grape/validations/types.rb +1 -4
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/all_or_none.rb +8 -5
- data/lib/grape/validations/validators/allow_blank.rb +9 -7
- data/lib/grape/validations/validators/as.rb +6 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
- data/lib/grape/validations/validators/base.rb +74 -69
- data/lib/grape/validations/validators/coerce.rb +63 -76
- data/lib/grape/validations/validators/default.rb +36 -34
- data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
- data/lib/grape/validations/validators/except_values.rb +13 -11
- data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
- data/lib/grape/validations/validators/presence.rb +7 -4
- data/lib/grape/validations/validators/regexp.rb +8 -5
- data/lib/grape/validations/validators/same_as.rb +18 -15
- data/lib/grape/validations/validators/values.rb +61 -56
- data/lib/grape/validations.rb +6 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +7 -3
- data/spec/grape/api/custom_validations_spec.rb +77 -45
- data/spec/grape/api/deeply_included_options_spec.rb +3 -3
- data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
- data/spec/grape/api_remount_spec.rb +25 -19
- data/spec/grape/api_spec.rb +576 -211
- data/spec/grape/dsl/callbacks_spec.rb +2 -1
- data/spec/grape/dsl/headers_spec.rb +39 -9
- data/spec/grape/dsl/helpers_spec.rb +3 -2
- data/spec/grape/dsl/inside_route_spec.rb +185 -34
- data/spec/grape/dsl/logger_spec.rb +16 -18
- data/spec/grape/dsl/middleware_spec.rb +2 -1
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +10 -7
- data/spec/grape/endpoint/declared_spec.rb +848 -0
- data/spec/grape/endpoint_spec.rb +77 -589
- data/spec/grape/entity_spec.rb +29 -23
- data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
- data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
- data/spec/grape/exceptions/validation_spec.rb +5 -3
- data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
- data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
- data/spec/grape/loading_spec.rb +8 -8
- data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
- data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
- data/spec/grape/middleware/base_spec.rb +24 -15
- data/spec/grape/middleware/error_spec.rb +3 -3
- data/spec/grape/middleware/exception_spec.rb +111 -161
- data/spec/grape/middleware/formatter_spec.rb +28 -7
- data/spec/grape/middleware/globals_spec.rb +7 -4
- data/spec/grape/middleware/stack_spec.rb +15 -12
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
- data/spec/grape/middleware/versioner/header_spec.rb +14 -13
- data/spec/grape/middleware/versioner/param_spec.rb +7 -1
- data/spec/grape/middleware/versioner/path_spec.rb +5 -1
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/parser_spec.rb +4 -0
- data/spec/grape/path_spec.rb +52 -52
- data/spec/grape/presenters/presenter_spec.rb +7 -6
- data/spec/grape/request_spec.rb +6 -4
- data/spec/grape/util/inheritable_setting_spec.rb +7 -7
- data/spec/grape/util/inheritable_values_spec.rb +3 -2
- data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
- data/spec/grape/util/stackable_values_spec.rb +7 -5
- data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
- data/spec/grape/validations/params_scope_spec.rb +72 -10
- data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
- data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
- data/spec/grape/validations/types_spec.rb +8 -8
- data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
- data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
- data/spec/grape/validations/validators/coerce_spec.rb +248 -33
- data/spec/grape/validations/validators/default_spec.rb +121 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
- data/spec/grape/validations/validators/except_values_spec.rb +4 -3
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
- data/spec/grape/validations/validators/presence_spec.rb +16 -1
- data/spec/grape/validations/validators/regexp_spec.rb +25 -31
- data/spec/grape/validations/validators/same_as_spec.rb +14 -20
- data/spec/grape/validations/validators/values_spec.rb +183 -178
- data/spec/grape/validations_spec.rb +342 -29
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +1 -1
- data/spec/integration/multi_xml/xml_spec.rb +1 -1
- data/spec/shared/versioning_examples.rb +32 -29
- data/spec/spec_helper.rb +12 -12
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- data/spec/support/chunks.rb +14 -0
- data/spec/support/versioned_helpers.rb +4 -6
- metadata +110 -102
@@ -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
|
-
|
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.
|
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?
|
45
|
+
value.is_a?(::Array) && value.all?(::Hash)
|
45
46
|
end
|
46
47
|
end
|
47
48
|
end
|
@@ -11,15 +11,15 @@ module Grape
|
|
11
11
|
class PrimitiveCoercer < DryTypeCoercer
|
12
12
|
MAPPING = {
|
13
13
|
Grape::API::Boolean => DryTypes::Params::Bool,
|
14
|
-
BigDecimal
|
14
|
+
BigDecimal => DryTypes::Params::Decimal,
|
15
15
|
|
16
16
|
# unfortunately, a +Params+ scope doesn't contain String
|
17
|
-
String
|
17
|
+
String => DryTypes::Coercible::String
|
18
18
|
}.freeze
|
19
19
|
|
20
20
|
STRICT_MAPPING = {
|
21
21
|
Grape::API::Boolean => DryTypes::Strict::Bool,
|
22
|
-
BigDecimal
|
22
|
+
BigDecimal => DryTypes::Strict::Decimal
|
23
23
|
}.freeze
|
24
24
|
|
25
25
|
def initialize(type, strict = false)
|
@@ -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
|
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
|
@@ -4,11 +4,14 @@ require 'grape/validations/validators/multiple_params_base'
|
|
4
4
|
|
5
5
|
module Grape
|
6
6
|
module Validations
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
module Validators
|
8
|
+
class AllOrNoneOfValidator < MultipleParamsBase
|
9
|
+
def validate_params!(params)
|
10
|
+
keys = keys_in_common(params)
|
11
|
+
return if keys.empty? || keys.length == all_keys.length
|
12
|
+
|
13
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
|
14
|
+
end
|
12
15
|
end
|
13
16
|
end
|
14
17
|
end
|
@@ -2,16 +2,18 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
module Validators
|
6
|
+
class AllowBlankValidator < Base
|
7
|
+
def validate_param!(attr_name, params)
|
8
|
+
return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
value = params[attr_name]
|
11
|
+
value = value.strip if value.respond_to?(:strip)
|
11
12
|
|
12
|
-
|
13
|
+
return if value == false || value.present?
|
13
14
|
|
14
|
-
|
15
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:blank))
|
16
|
+
end
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -2,14 +2,12 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def validate_param!(attr_name, params)
|
12
|
-
params[@renamed_options] = params[attr_name]
|
5
|
+
module Validators
|
6
|
+
class AsValidator < Base
|
7
|
+
# We use a validator for renaming parameters. This is just a marker for
|
8
|
+
# the parameter scope to handle the renaming. No actual validation
|
9
|
+
# happens here.
|
10
|
+
def validate_param!(*); end
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
@@ -4,10 +4,13 @@ require 'grape/validations/validators/multiple_params_base'
|
|
4
4
|
|
5
5
|
module Grape
|
6
6
|
module Validations
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
module Validators
|
8
|
+
class AtLeastOneOfValidator < MultipleParamsBase
|
9
|
+
def validate_params!(params)
|
10
|
+
return unless keys_in_common(params).empty?
|
11
|
+
|
12
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
|
13
|
+
end
|
11
14
|
end
|
12
15
|
end
|
13
16
|
end
|
@@ -2,88 +2,93 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
5
|
+
module Validators
|
6
|
+
class Base
|
7
|
+
attr_reader :attrs
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
62
|
-
|
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
|
-
|
71
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
86
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
84
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
31
|
+
private
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|