grape 2.1.3 → 2.3.0
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 +45 -0
- data/README.md +9 -7
- data/UPGRADING.md +27 -0
- data/grape.gemspec +7 -6
- data/lib/grape/api/instance.rb +22 -58
- data/lib/grape/api.rb +2 -11
- data/lib/grape/content_types.rb +13 -8
- data/lib/grape/dsl/desc.rb +27 -24
- data/lib/grape/dsl/helpers.rb +7 -3
- data/lib/grape/dsl/inside_route.rb +18 -24
- data/lib/grape/dsl/parameters.rb +2 -2
- data/lib/grape/dsl/request_response.rb +14 -18
- data/lib/grape/dsl/routing.rb +5 -12
- data/lib/grape/endpoint.rb +90 -82
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -15
- data/lib/grape/error_formatter/jsonapi.rb +7 -0
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +13 -20
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +5 -25
- data/lib/grape/exceptions/base.rb +18 -30
- data/lib/grape/exceptions/validation.rb +5 -4
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +4 -6
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +7 -25
- data/lib/grape/http/headers.rb +1 -0
- data/lib/grape/locale/en.yml +1 -0
- data/lib/grape/middleware/base.rb +14 -13
- data/lib/grape/middleware/error.rb +13 -9
- data/lib/grape/middleware/formatter.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +7 -30
- data/lib/grape/middleware/versioner/base.rb +82 -0
- data/lib/grape/middleware/versioner/header.rb +89 -10
- data/lib/grape/middleware/versioner/param.rb +4 -22
- data/lib/grape/middleware/versioner/path.rb +10 -32
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/jsonapi.rb +7 -0
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +39 -56
- data/lib/grape/request.rb +2 -2
- data/lib/grape/router/base_route.rb +2 -2
- data/lib/grape/router/greedy_route.rb +2 -2
- data/lib/grape/router/pattern.rb +23 -18
- data/lib/grape/router/route.rb +13 -5
- data/lib/grape/router.rb +5 -5
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/validations/contract_scope.rb +2 -39
- data/lib/grape/validations/params_scope.rb +7 -11
- data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
- data/lib/grape/validations/validator_factory.rb +2 -2
- data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
- data/lib/grape/validations/validators/base.rb +5 -9
- data/lib/grape/validations/validators/coerce_validator.rb +1 -1
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +1 -1
- data/lib/grape/validations/validators/except_values_validator.rb +1 -1
- data/lib/grape/validations/validators/length_validator.rb +11 -4
- data/lib/grape/validations/validators/regexp_validator.rb +1 -1
- data/lib/grape/validations/validators/values_validator.rb +15 -57
- data/lib/grape/validations.rb +8 -17
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +1 -1
- metadata +15 -12
- data/lib/grape/util/accept_header_handler.rb +0 -105
- data/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/validations/types/build_coercer.rb +0 -92
data/lib/grape/parser/json.rb
CHANGED
@@ -2,14 +2,12 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Parser
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
rescue
|
10
|
-
|
11
|
-
raise Grape::Exceptions::InvalidMessageBody.new('application/json')
|
12
|
-
end
|
5
|
+
class Json < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
::Grape::Json.load(object)
|
8
|
+
rescue ::Grape::Json::ParseError
|
9
|
+
# handle JSON parsing errors via the rescue handlers or provide error message
|
10
|
+
raise Grape::Exceptions::InvalidMessageBody.new('application/json')
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
data/lib/grape/parser/xml.rb
CHANGED
@@ -2,14 +2,12 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Parser
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
rescue
|
10
|
-
|
11
|
-
raise Grape::Exceptions::InvalidMessageBody.new('application/xml')
|
12
|
-
end
|
5
|
+
class Xml < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
::Grape::Xml.parse(object)
|
8
|
+
rescue ::Grape::Xml::ParseError
|
9
|
+
# handle XML parsing errors via the rescue handlers or provide error message
|
10
|
+
raise Grape::Exceptions::InvalidMessageBody.new('application/xml')
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
data/lib/grape/parser.rb
CHANGED
@@ -2,32 +2,14 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Parser
|
5
|
-
extend Util::
|
5
|
+
extend Grape::Util::Registry
|
6
6
|
|
7
|
-
|
8
|
-
def builtin_parsers
|
9
|
-
@builtin_parsers ||= {
|
10
|
-
json: Grape::Parser::Json,
|
11
|
-
jsonapi: Grape::Parser::Json,
|
12
|
-
xml: Grape::Parser::Xml
|
13
|
-
}
|
14
|
-
end
|
7
|
+
module_function
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
9
|
+
def parser_for(format, parsers = nil)
|
10
|
+
return parsers[format] if parsers&.key?(format)
|
19
11
|
|
20
|
-
|
21
|
-
spec = parsers(**options)[api_format]
|
22
|
-
case spec
|
23
|
-
when nil
|
24
|
-
nil
|
25
|
-
when Symbol
|
26
|
-
method(spec)
|
27
|
-
else
|
28
|
-
spec
|
29
|
-
end
|
30
|
-
end
|
12
|
+
registry[format]
|
31
13
|
end
|
32
14
|
end
|
33
15
|
end
|
data/lib/grape/path.rb
CHANGED
@@ -3,65 +3,66 @@
|
|
3
3
|
module Grape
|
4
4
|
# Represents a path to an endpoint.
|
5
5
|
class Path
|
6
|
-
|
6
|
+
DEFAULT_FORMAT_SEGMENT = '(/.:format)'
|
7
|
+
NO_VERSIONING_WITH_VALID_PATH_FORMAT_SEGMENT = '(.:format)'
|
8
|
+
VERSION_SEGMENT = ':version'
|
7
9
|
|
8
|
-
|
9
|
-
@raw_path = raw_path
|
10
|
-
@namespace = namespace
|
11
|
-
@settings = settings
|
12
|
-
end
|
10
|
+
attr_reader :origin, :suffix
|
13
11
|
|
14
|
-
def
|
15
|
-
settings
|
12
|
+
def initialize(raw_path, raw_namespace, settings)
|
13
|
+
@origin = PartsCache[build_parts(raw_path, raw_namespace, settings)]
|
14
|
+
@suffix = build_suffix(raw_path, raw_namespace, settings)
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
19
|
-
|
17
|
+
def to_s
|
18
|
+
"#{origin}#{suffix}"
|
20
19
|
end
|
21
20
|
|
22
|
-
|
23
|
-
return false unless settings.key?(:format) && settings.key?(:content_types)
|
21
|
+
private
|
24
22
|
|
25
|
-
|
23
|
+
def build_suffix(raw_path, raw_namespace, settings)
|
24
|
+
if uses_specific_format?(settings)
|
25
|
+
"(.#{settings[:format]})"
|
26
|
+
elsif !uses_path_versioning?(settings) || (valid_part?(raw_namespace) || valid_part?(raw_path))
|
27
|
+
NO_VERSIONING_WITH_VALID_PATH_FORMAT_SEGMENT
|
28
|
+
else
|
29
|
+
DEFAULT_FORMAT_SEGMENT
|
30
|
+
end
|
26
31
|
end
|
27
32
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
def build_parts(raw_path, raw_namespace, settings)
|
34
|
+
[].tap do |parts|
|
35
|
+
add_part(parts, settings[:mount_path])
|
36
|
+
add_part(parts, settings[:root_prefix])
|
37
|
+
parts << VERSION_SEGMENT if uses_path_versioning?(settings)
|
38
|
+
add_part(parts, raw_namespace)
|
39
|
+
add_part(parts, raw_path)
|
40
|
+
end
|
32
41
|
end
|
33
42
|
|
34
|
-
def
|
35
|
-
|
43
|
+
def add_part(parts, value)
|
44
|
+
parts << value if value && not_slash?(value)
|
36
45
|
end
|
37
46
|
|
38
|
-
def
|
39
|
-
|
47
|
+
def not_slash?(value)
|
48
|
+
value != '/'
|
40
49
|
end
|
41
50
|
|
42
|
-
def
|
43
|
-
|
44
|
-
"(.#{settings[:format]})"
|
45
|
-
elsif !uses_path_versioning? || (namespace? || path?)
|
46
|
-
'(.:format)'
|
47
|
-
else
|
48
|
-
'(/.:format)'
|
49
|
-
end
|
50
|
-
end
|
51
|
+
def uses_specific_format?(settings)
|
52
|
+
return false unless settings.key?(:format) && settings.key?(:content_types)
|
51
53
|
|
52
|
-
|
53
|
-
PartsCache[parts]
|
54
|
+
settings[:format] && Array(settings[:content_types]).size == 1
|
54
55
|
end
|
55
56
|
|
56
|
-
def
|
57
|
-
|
58
|
-
end
|
57
|
+
def uses_path_versioning?(settings)
|
58
|
+
return false unless settings.key?(:version) && settings[:version_options]&.key?(:using)
|
59
59
|
|
60
|
-
|
61
|
-
path_with_suffix
|
60
|
+
settings[:version] && settings[:version_options][:using] == :path
|
62
61
|
end
|
63
62
|
|
64
|
-
|
63
|
+
def valid_part?(part)
|
64
|
+
part&.match?(/^\S/) && not_slash?(part)
|
65
|
+
end
|
65
66
|
|
66
67
|
class PartsCache < Grape::Util::Cache
|
67
68
|
def initialize
|
@@ -71,23 +72,5 @@ module Grape
|
|
71
72
|
end
|
72
73
|
end
|
73
74
|
end
|
74
|
-
|
75
|
-
def parts
|
76
|
-
[].tap do |parts|
|
77
|
-
add_part(parts, mount_path)
|
78
|
-
add_part(parts, root_prefix)
|
79
|
-
parts << ':version' if uses_path_versioning?
|
80
|
-
add_part(parts, namespace)
|
81
|
-
add_part(parts, raw_path)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def add_part(parts, value)
|
86
|
-
parts << value if value && not_slash?(value)
|
87
|
-
end
|
88
|
-
|
89
|
-
def not_slash?(value)
|
90
|
-
value != '/'
|
91
|
-
end
|
92
75
|
end
|
93
76
|
end
|
data/lib/grape/request.rb
CHANGED
@@ -6,8 +6,8 @@ module Grape
|
|
6
6
|
|
7
7
|
alias rack_params params
|
8
8
|
|
9
|
-
def initialize(env,
|
10
|
-
extend
|
9
|
+
def initialize(env, build_params_with: nil)
|
10
|
+
extend build_params_with || Grape.config.param_builder
|
11
11
|
super(env)
|
12
12
|
end
|
13
13
|
|
@@ -7,8 +7,8 @@ module Grape
|
|
7
7
|
|
8
8
|
attr_reader :index, :pattern, :options
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@options = ActiveSupport::OrderedOptions.new.update(options)
|
10
|
+
def initialize(options)
|
11
|
+
@options = options.is_a?(ActiveSupport::OrderedOptions) ? options : ActiveSupport::OrderedOptions.new.update(options)
|
12
12
|
end
|
13
13
|
|
14
14
|
alias attributes options
|
@@ -6,9 +6,9 @@
|
|
6
6
|
module Grape
|
7
7
|
class Router
|
8
8
|
class GreedyRoute < BaseRoute
|
9
|
-
def initialize(pattern
|
9
|
+
def initialize(pattern, options)
|
10
10
|
@pattern = pattern
|
11
|
-
super(
|
11
|
+
super(options)
|
12
12
|
end
|
13
13
|
|
14
14
|
# Grape::Router:Route defines params as a function
|
data/lib/grape/router/pattern.rb
CHANGED
@@ -9,14 +9,14 @@ module Grape
|
|
9
9
|
|
10
10
|
attr_reader :origin, :path, :pattern, :to_regexp
|
11
11
|
|
12
|
-
def_delegators :pattern, :
|
12
|
+
def_delegators :pattern, :params
|
13
13
|
def_delegators :to_regexp, :===
|
14
14
|
alias match? ===
|
15
15
|
|
16
|
-
def initialize(
|
17
|
-
@origin =
|
18
|
-
@path = build_path(
|
19
|
-
@pattern = build_pattern(@path, options)
|
16
|
+
def initialize(origin, suffix, options)
|
17
|
+
@origin = origin
|
18
|
+
@path = build_path(origin, options[:anchor], suffix)
|
19
|
+
@pattern = build_pattern(@path, options[:params], options[:format], options[:version], options[:requirements])
|
20
20
|
@to_regexp = @pattern.to_regexp
|
21
21
|
end
|
22
22
|
|
@@ -28,30 +28,31 @@ module Grape
|
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
|
-
def build_pattern(path,
|
31
|
+
def build_pattern(path, params, format, version, requirements)
|
32
32
|
Mustermann::Grape.new(
|
33
33
|
path,
|
34
34
|
uri_decode: true,
|
35
|
-
params:
|
36
|
-
capture: extract_capture(
|
35
|
+
params: params,
|
36
|
+
capture: extract_capture(format, version, requirements)
|
37
37
|
)
|
38
38
|
end
|
39
39
|
|
40
|
-
def build_path(pattern, anchor
|
41
|
-
PatternCache[[build_path_from_pattern(pattern, anchor
|
40
|
+
def build_path(pattern, anchor, suffix)
|
41
|
+
PatternCache[[build_path_from_pattern(pattern, anchor), suffix]]
|
42
42
|
end
|
43
43
|
|
44
|
-
def extract_capture(
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
def extract_capture(format, version, requirements)
|
45
|
+
capture = {}.tap do |h|
|
46
|
+
h[:format] = map_str(format) if format.present?
|
47
|
+
h[:version] = map_str(version) if version.present?
|
48
|
+
end
|
49
|
+
|
50
|
+
return capture if requirements.blank?
|
50
51
|
|
51
|
-
|
52
|
+
requirements.merge(capture)
|
52
53
|
end
|
53
54
|
|
54
|
-
def build_path_from_pattern(pattern, anchor
|
55
|
+
def build_path_from_pattern(pattern, anchor)
|
55
56
|
if pattern.end_with?('*path')
|
56
57
|
pattern.dup.insert(pattern.rindex('/') + 1, '?')
|
57
58
|
elsif anchor
|
@@ -63,6 +64,10 @@ module Grape
|
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
67
|
+
def map_str(value)
|
68
|
+
Array.wrap(value).map(&:to_s)
|
69
|
+
end
|
70
|
+
|
66
71
|
class PatternCache < Grape::Util::Cache
|
67
72
|
def initialize
|
68
73
|
super
|
data/lib/grape/router/route.rb
CHANGED
@@ -5,14 +5,22 @@ module Grape
|
|
5
5
|
class Route < BaseRoute
|
6
6
|
extend Forwardable
|
7
7
|
|
8
|
+
FORWARD_MATCH_METHOD = ->(input, pattern) { input.start_with?(pattern.origin) }
|
9
|
+
NON_FORWARD_MATCH_METHOD = ->(input, pattern) { pattern.match?(input) }
|
10
|
+
|
8
11
|
attr_reader :app, :request_method
|
9
12
|
|
10
13
|
def_delegators :pattern, :path, :origin
|
11
14
|
|
12
|
-
def initialize(method,
|
15
|
+
def initialize(method, origin, path, options)
|
13
16
|
@request_method = upcase_method(method)
|
14
|
-
@pattern = Grape::Router::Pattern.new(
|
15
|
-
|
17
|
+
@pattern = Grape::Router::Pattern.new(origin, path, options)
|
18
|
+
@match_function = options[:forward_match] ? FORWARD_MATCH_METHOD : NON_FORWARD_MATCH_METHOD
|
19
|
+
super(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def convert_to_head_request!
|
23
|
+
@request_method = Rack::HEAD
|
16
24
|
end
|
17
25
|
|
18
26
|
def exec(env)
|
@@ -27,7 +35,7 @@ module Grape
|
|
27
35
|
def match?(input)
|
28
36
|
return false if input.blank?
|
29
37
|
|
30
|
-
|
38
|
+
@match_function.call(input, pattern)
|
31
39
|
end
|
32
40
|
|
33
41
|
def params(input = nil)
|
@@ -42,7 +50,7 @@ module Grape
|
|
42
50
|
private
|
43
51
|
|
44
52
|
def params_without_input
|
45
|
-
pattern.captures_default.merge(attributes.params)
|
53
|
+
@params_without_input ||= pattern.captures_default.merge(attributes.params)
|
46
54
|
end
|
47
55
|
|
48
56
|
def upcase_method(method)
|
data/lib/grape/router.rb
CHANGED
@@ -38,8 +38,8 @@ module Grape
|
|
38
38
|
map[route.request_method] << route
|
39
39
|
end
|
40
40
|
|
41
|
-
def associate_routes(pattern,
|
42
|
-
Grape::Router::GreedyRoute.new(pattern
|
41
|
+
def associate_routes(pattern, options)
|
42
|
+
Grape::Router::GreedyRoute.new(pattern, options).then do |greedy_route|
|
43
43
|
@neutral_regexes << greedy_route.to_regexp(@neutral_map.length)
|
44
44
|
@neutral_map << greedy_route
|
45
45
|
end
|
@@ -107,7 +107,7 @@ module Grape
|
|
107
107
|
|
108
108
|
route = match?(input, '*')
|
109
109
|
|
110
|
-
return last_neighbor_route.endpoint.call(env) if last_neighbor_route && last_response_cascade && route
|
110
|
+
return last_neighbor_route.options[:endpoint].call(env) if last_neighbor_route && last_response_cascade && route
|
111
111
|
|
112
112
|
last_response_cascade = cascade_or_return_response.call(process_route(route, env)) if route
|
113
113
|
|
@@ -152,8 +152,8 @@ module Grape
|
|
152
152
|
|
153
153
|
def call_with_allow_headers(env, route)
|
154
154
|
prepare_env_from_route(env, route)
|
155
|
-
env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.allow_header
|
156
|
-
route.endpoint.call(env)
|
155
|
+
env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.options[:allow_header]
|
156
|
+
route.options[:endpoint].call(env)
|
157
157
|
end
|
158
158
|
|
159
159
|
def prepare_env_from_route(env, route)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Util
|
5
|
+
module Registry
|
6
|
+
def register(klass)
|
7
|
+
short_name = build_short_name(klass)
|
8
|
+
return if short_name.nil?
|
9
|
+
|
10
|
+
warn "#{short_name} is already registered with class #{klass}" if registry.key?(short_name)
|
11
|
+
registry[short_name] = klass
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_short_name(klass)
|
17
|
+
return if klass.name.blank?
|
18
|
+
|
19
|
+
klass.name.demodulize.underscore
|
20
|
+
end
|
21
|
+
|
22
|
+
def registry
|
23
|
+
@registry ||= {}.with_indifferent_access
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -23,49 +23,12 @@ module Grape
|
|
23
23
|
api.namespace_stackable(:contract_key_map, key_map)
|
24
24
|
|
25
25
|
validator_options = {
|
26
|
-
validator_class:
|
27
|
-
opts: { schema: contract }
|
26
|
+
validator_class: Grape::Validations.require_validator(:contract_scope),
|
27
|
+
opts: { schema: contract, fail_fast: false }
|
28
28
|
}
|
29
29
|
|
30
30
|
api.namespace_stackable(:validations, validator_options)
|
31
31
|
end
|
32
|
-
|
33
|
-
class Validator
|
34
|
-
attr_reader :schema
|
35
|
-
|
36
|
-
def initialize(*_args, schema:)
|
37
|
-
@schema = schema
|
38
|
-
end
|
39
|
-
|
40
|
-
# Validates a given request.
|
41
|
-
# @param request [Grape::Request] the request currently being handled
|
42
|
-
# @raise [Grape::Exceptions::ValidationArrayErrors] if validation failed
|
43
|
-
# @return [void]
|
44
|
-
def validate(request)
|
45
|
-
res = schema.call(request.params)
|
46
|
-
|
47
|
-
if res.success?
|
48
|
-
request.params.deep_merge!(res.to_h)
|
49
|
-
return
|
50
|
-
end
|
51
|
-
|
52
|
-
errors = []
|
53
|
-
|
54
|
-
res.errors.messages.each do |message|
|
55
|
-
full_name = message.path.first.to_s
|
56
|
-
|
57
|
-
full_name += "[#{message.path[1..].join('][')}]" if message.path.size > 1
|
58
|
-
|
59
|
-
errors << Grape::Exceptions::Validation.new(params: [full_name], message: message.text)
|
60
|
-
end
|
61
|
-
|
62
|
-
raise Grape::Exceptions::ValidationArrayErrors.new(errors)
|
63
|
-
end
|
64
|
-
|
65
|
-
def fail_fast?
|
66
|
-
false
|
67
|
-
end
|
68
|
-
end
|
69
32
|
end
|
70
33
|
end
|
71
34
|
end
|
@@ -174,16 +174,12 @@ module Grape
|
|
174
174
|
|
175
175
|
# Adds a parameter declaration to our list of validations.
|
176
176
|
# @param attrs [Array] (see Grape::DSL::Parameters#requires)
|
177
|
-
def push_declared_params(attrs,
|
178
|
-
opts =
|
179
|
-
if lateral?
|
180
|
-
@parent.push_declared_params(attrs, **opts)
|
181
|
-
else
|
182
|
-
push_renamed_param(full_path + [attrs.first], opts[:as]) \
|
183
|
-
if opts && opts[:as]
|
177
|
+
def push_declared_params(attrs, opts = {})
|
178
|
+
opts[:declared_params_scope] = self unless opts.key?(:declared_params_scope)
|
179
|
+
return @parent.push_declared_params(attrs, opts) if lateral?
|
184
180
|
|
185
|
-
|
186
|
-
|
181
|
+
push_renamed_param(full_path + [attrs.first], opts[:as]) if opts[:as]
|
182
|
+
@declared_params.concat(attrs.map { |attr| ::Grape::Validations::ParamsScope::Attr.new(attr, opts[:declared_params_scope]) })
|
187
183
|
end
|
188
184
|
|
189
185
|
# Get the full path of the parameter scope in the hierarchy.
|
@@ -490,7 +486,7 @@ module Grape
|
|
490
486
|
def validate_value_coercion(coerce_type, *values_list)
|
491
487
|
return unless coerce_type
|
492
488
|
|
493
|
-
coerce_type = coerce_type.first if coerce_type.is_a?(
|
489
|
+
coerce_type = coerce_type.first if coerce_type.is_a?(Enumerable)
|
494
490
|
values_list.each do |values|
|
495
491
|
next if !values || values.is_a?(Proc)
|
496
492
|
|
@@ -529,7 +525,7 @@ module Grape
|
|
529
525
|
def validates_presence(validations, attrs, doc, opts)
|
530
526
|
return unless validations.key?(:presence) && validations[:presence]
|
531
527
|
|
532
|
-
validate(
|
528
|
+
validate('presence', validations.delete(:presence), attrs, doc, opts)
|
533
529
|
validations.delete(:message) if validations.key?(:message)
|
534
530
|
end
|
535
531
|
end
|
@@ -22,16 +22,20 @@ module Grape
|
|
22
22
|
# collection_coercer_for(Array)
|
23
23
|
# #=> Grape::Validations::Types::ArrayCoercer
|
24
24
|
def collection_coercer_for(type)
|
25
|
-
|
25
|
+
case type
|
26
|
+
when Array
|
27
|
+
ArrayCoercer
|
28
|
+
when Set
|
29
|
+
SetCoercer
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Unknown type: #{type}"
|
32
|
+
end
|
26
33
|
end
|
27
34
|
|
28
35
|
# Returns an instance of a coercer for a given type
|
29
36
|
def coercer_instance_for(type, strict = false)
|
30
|
-
|
31
|
-
|
32
|
-
# in case of a collection (Array[Integer]) the type is an instance of a collection,
|
33
|
-
# so we need to figure out the actual type
|
34
|
-
collection_coercer_for(type.class).new(type, strict)
|
37
|
+
klass = type.instance_of?(Class) ? PrimitiveCoercer : collection_coercer_for(type)
|
38
|
+
klass.new(type, strict)
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
@@ -3,12 +3,12 @@
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
5
|
class ValidatorFactory
|
6
|
-
def self.create_validator(
|
6
|
+
def self.create_validator(options)
|
7
7
|
options[:validator_class].new(options[:attributes],
|
8
8
|
options[:options],
|
9
9
|
options[:required],
|
10
10
|
options[:params_scope],
|
11
|
-
|
11
|
+
options[:opts])
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -8,7 +8,7 @@ module Grape
|
|
8
8
|
return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
|
9
9
|
|
10
10
|
value = params[attr_name]
|
11
|
-
value = value.
|
11
|
+
value = value.scrub if value.respond_to?(:scrub)
|
12
12
|
|
13
13
|
return if value == false || value.present?
|
14
14
|
|
@@ -13,15 +13,14 @@ module Grape
|
|
13
13
|
# @param options [Object] implementation-dependent Validator options
|
14
14
|
# @param required [Boolean] attribute(s) are required or optional
|
15
15
|
# @param scope [ParamsScope] parent scope for this Validator
|
16
|
-
# @param opts [
|
17
|
-
def initialize(attrs, options, required, scope,
|
16
|
+
# @param opts [Hash] additional validation options
|
17
|
+
def initialize(attrs, options, required, scope, opts)
|
18
18
|
@attrs = Array(attrs)
|
19
19
|
@option = options
|
20
20
|
@required = required
|
21
21
|
@scope = scope
|
22
|
-
|
23
|
-
@
|
24
|
-
@allow_blank = opts.fetch(:allow_blank, false)
|
22
|
+
@fail_fast = opts[:fail_fast]
|
23
|
+
@allow_blank = opts[:allow_blank]
|
25
24
|
end
|
26
25
|
|
27
26
|
# Validates a given request.
|
@@ -60,10 +59,7 @@ module Grape
|
|
60
59
|
|
61
60
|
def self.inherited(klass)
|
62
61
|
super
|
63
|
-
|
64
|
-
|
65
|
-
short_validator_name = klass.name.demodulize.underscore.delete_suffix('_validator')
|
66
|
-
Validations.register_validator(short_validator_name, klass)
|
62
|
+
Validations.register(klass)
|
67
63
|
end
|
68
64
|
|
69
65
|
def message(default_key = nil)
|
@@ -4,7 +4,7 @@ module Grape
|
|
4
4
|
module Validations
|
5
5
|
module Validators
|
6
6
|
class CoerceValidator < Base
|
7
|
-
def initialize(attrs, options, required, scope,
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
8
8
|
super
|
9
9
|
|
10
10
|
@converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Validators
|
6
|
+
class ContractScopeValidator < Base
|
7
|
+
attr_reader :schema
|
8
|
+
|
9
|
+
def initialize(_attrs, _options, _required, _scope, opts)
|
10
|
+
super
|
11
|
+
@schema = opts.fetch(:schema)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Validates a given request.
|
15
|
+
# @param request [Grape::Request] the request currently being handled
|
16
|
+
# @raise [Grape::Exceptions::ValidationArrayErrors] if validation failed
|
17
|
+
# @return [void]
|
18
|
+
def validate(request)
|
19
|
+
res = schema.call(request.params)
|
20
|
+
|
21
|
+
if res.success?
|
22
|
+
request.params.deep_merge!(res.to_h)
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(build_errors_from_messages(res.errors.messages))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def build_errors_from_messages(messages)
|
32
|
+
messages.map do |message|
|
33
|
+
full_name = message.path.first.to_s
|
34
|
+
full_name << "[#{message.path[1..].join('][')}]" if message.path.size > 1
|
35
|
+
Grape::Exceptions::Validation.new(params: [full_name], message: message.text)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|