grape 3.0.1 → 3.1.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 +26 -0
- data/CONTRIBUTING.md +2 -2
- data/README.md +2 -2
- data/UPGRADING.md +14 -0
- data/grape.gemspec +2 -2
- data/lib/grape/api/instance.rb +16 -47
- data/lib/grape/api.rb +6 -10
- data/lib/grape/content_types.rb +1 -4
- data/lib/grape/declared_params_handler.rb +118 -0
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/helpers.rb +2 -2
- data/lib/grape/dsl/inside_route.rb +3 -141
- data/lib/grape/dsl/parameters.rb +8 -26
- data/lib/grape/dsl/routing.rb +41 -29
- data/lib/grape/dsl/settings.rb +1 -1
- data/lib/grape/dsl/validations.rb +22 -20
- data/lib/grape/endpoint.rb +84 -83
- data/lib/grape/exceptions/base.rb +1 -1
- data/lib/grape/middleware/auth/dsl.rb +4 -4
- data/lib/grape/middleware/base.rb +4 -0
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/formatter.rb +6 -4
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/base.rb +20 -0
- data/lib/grape/middleware/versioner/header.rb +1 -17
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +5 -9
- data/lib/grape/params_builder.rb +2 -19
- data/lib/grape/router/base_route.rb +14 -5
- data/lib/grape/router/greedy_route.rb +11 -5
- data/lib/grape/router/pattern.rb +6 -20
- data/lib/grape/router/route.rb +7 -11
- data/lib/grape/router.rb +35 -61
- data/lib/grape/util/api_description.rb +10 -8
- data/lib/grape/validations/attributes_iterator.rb +2 -2
- data/lib/grape/validations/params_scope.rb +12 -10
- data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
- data/lib/grape/validations/validators/base.rb +2 -2
- data/lib/grape/validations/validators/except_values_validator.rb +1 -1
- data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +1 -1
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +12 -2
- data/lib/grape/validations/validators/values_validator.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +5 -13
- metadata +11 -14
- data/lib/grape/exceptions/missing_option.rb +0 -11
- data/lib/grape/exceptions/unknown_options.rb +0 -11
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -24
- data/lib/grape/extensions/hash.rb +0 -27
- data/lib/grape/extensions/hashie/mash.rb +0 -24
data/lib/grape/namespace.rb
CHANGED
|
@@ -5,24 +5,19 @@ module Grape
|
|
|
5
5
|
# logical grouping of endpoints as well as sharing common configuration.
|
|
6
6
|
# May also be referred to as group, segment, or resource.
|
|
7
7
|
class Namespace
|
|
8
|
-
attr_reader :space, :options
|
|
8
|
+
attr_reader :space, :requirements, :options
|
|
9
9
|
|
|
10
10
|
# @param space [String] the name of this namespace
|
|
11
11
|
# @param options [Hash] options hash
|
|
12
12
|
# @option options :requirements [Hash] param-regex pairs, all of which must
|
|
13
13
|
# be met by a request's params for all endpoints in this namespace, or
|
|
14
14
|
# validation will fail and return a 422.
|
|
15
|
-
def initialize(space, options)
|
|
15
|
+
def initialize(space, requirements: nil, **options)
|
|
16
16
|
@space = space.to_s
|
|
17
|
+
@requirements = requirements
|
|
17
18
|
@options = options
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
# Retrieves the requirements from the options hash, if given.
|
|
21
|
-
# @return [Hash]
|
|
22
|
-
def requirements
|
|
23
|
-
options[:requirements] || {}
|
|
24
|
-
end
|
|
25
|
-
|
|
26
21
|
# (see ::joined_space_path)
|
|
27
22
|
def self.joined_space(settings)
|
|
28
23
|
settings&.map(&:space)
|
|
@@ -31,12 +26,13 @@ module Grape
|
|
|
31
26
|
def eql?(other)
|
|
32
27
|
other.class == self.class &&
|
|
33
28
|
other.space == space &&
|
|
29
|
+
other.requirements == requirements &&
|
|
34
30
|
other.options == options
|
|
35
31
|
end
|
|
36
32
|
alias == eql?
|
|
37
33
|
|
|
38
34
|
def hash
|
|
39
|
-
[self.class, space, options].hash
|
|
35
|
+
[self.class, space, requirements, options].hash
|
|
40
36
|
end
|
|
41
37
|
|
|
42
38
|
# Join the namespaces from a list of settings to create a path prefix.
|
data/lib/grape/params_builder.rb
CHANGED
|
@@ -4,29 +4,12 @@ module Grape
|
|
|
4
4
|
module ParamsBuilder
|
|
5
5
|
extend Grape::Util::Registry
|
|
6
6
|
|
|
7
|
-
SHORT_NAME_LOOKUP = {
|
|
8
|
-
'Grape::Extensions::Hash::ParamBuilder' => :hash,
|
|
9
|
-
'Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder' => :hash_with_indifferent_access,
|
|
10
|
-
'Grape::Extensions::Hashie::Mash::ParamBuilder' => :hashie_mash
|
|
11
|
-
}.freeze
|
|
12
|
-
|
|
13
7
|
module_function
|
|
14
8
|
|
|
15
9
|
def params_builder_for(short_name)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
raise Grape::Exceptions::UnknownParamsBuilder, verified_short_name unless registry.key?(verified_short_name)
|
|
19
|
-
|
|
20
|
-
registry[verified_short_name]
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def verify_short_name!(short_name)
|
|
24
|
-
return short_name if short_name.is_a?(Symbol)
|
|
10
|
+
raise Grape::Exceptions::UnknownParamsBuilder, short_name unless registry.key?(short_name)
|
|
25
11
|
|
|
26
|
-
|
|
27
|
-
SHORT_NAME_LOOKUP[class_name].tap do |real_short_name|
|
|
28
|
-
Grape.deprecator.warn "#{class_name} has been deprecated. Use short name :#{real_short_name} instead."
|
|
29
|
-
end
|
|
12
|
+
registry[short_name]
|
|
30
13
|
end
|
|
31
14
|
end
|
|
32
15
|
end
|
|
@@ -3,22 +3,31 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
class Router
|
|
5
5
|
class BaseRoute
|
|
6
|
+
extend Forwardable
|
|
7
|
+
|
|
6
8
|
delegate_missing_to :@options
|
|
7
9
|
|
|
8
|
-
attr_reader :
|
|
10
|
+
attr_reader :options, :pattern
|
|
11
|
+
|
|
12
|
+
def_delegators :@pattern, :path, :origin
|
|
13
|
+
def_delegators :@options, :description, :version, :requirements, :prefix, :anchor, :settings, :forward_match, *Grape::Util::ApiDescription::DSL_METHODS
|
|
9
14
|
|
|
10
|
-
def initialize(options)
|
|
15
|
+
def initialize(pattern, options = {})
|
|
16
|
+
@pattern = pattern
|
|
11
17
|
@options = options.is_a?(ActiveSupport::OrderedOptions) ? options : ActiveSupport::OrderedOptions.new.update(options)
|
|
12
18
|
end
|
|
13
19
|
|
|
14
|
-
|
|
20
|
+
# see https://github.com/ruby-grape/grape/issues/1348
|
|
21
|
+
def namespace
|
|
22
|
+
@namespace ||= @options[:namespace]
|
|
23
|
+
end
|
|
15
24
|
|
|
16
25
|
def regexp_capture_index
|
|
17
|
-
CaptureIndexCache[index]
|
|
26
|
+
@regexp_capture_index ||= CaptureIndexCache[@index]
|
|
18
27
|
end
|
|
19
28
|
|
|
20
29
|
def pattern_regexp
|
|
21
|
-
pattern.to_regexp
|
|
30
|
+
@pattern.to_regexp
|
|
22
31
|
end
|
|
23
32
|
|
|
24
33
|
def to_regexp(index)
|
|
@@ -6,14 +6,20 @@
|
|
|
6
6
|
module Grape
|
|
7
7
|
class Router
|
|
8
8
|
class GreedyRoute < BaseRoute
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
extend Forwardable
|
|
10
|
+
|
|
11
|
+
def_delegators :@endpoint, :call
|
|
12
|
+
|
|
13
|
+
attr_reader :endpoint, :allow_header
|
|
14
|
+
|
|
15
|
+
def initialize(pattern, endpoint:, allow_header:)
|
|
16
|
+
super(pattern)
|
|
17
|
+
@endpoint = endpoint
|
|
18
|
+
@allow_header = allow_header
|
|
12
19
|
end
|
|
13
20
|
|
|
14
|
-
# Grape::Router:Route defines params as a function
|
|
15
21
|
def params(_input = nil)
|
|
16
|
-
|
|
22
|
+
nil
|
|
17
23
|
end
|
|
18
24
|
end
|
|
19
25
|
end
|
data/lib/grape/router/pattern.rb
CHANGED
|
@@ -13,10 +13,10 @@ module Grape
|
|
|
13
13
|
def_delegators :to_regexp, :===
|
|
14
14
|
alias match? ===
|
|
15
15
|
|
|
16
|
-
def initialize(origin
|
|
16
|
+
def initialize(origin:, suffix:, anchor:, params:, format:, version:, requirements:)
|
|
17
17
|
@origin = origin
|
|
18
|
-
@path =
|
|
19
|
-
@pattern =
|
|
18
|
+
@path = PatternCache[[build_path_from_pattern(@origin, anchor), suffix]]
|
|
19
|
+
@pattern = Mustermann::Grape.new(@path, uri_decode: true, params: params, capture: extract_capture(format, version, requirements))
|
|
20
20
|
@to_regexp = @pattern.to_regexp
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -28,24 +28,10 @@ module Grape
|
|
|
28
28
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
|
-
def build_pattern(path, params, format, version, requirements)
|
|
32
|
-
Mustermann::Grape.new(
|
|
33
|
-
path,
|
|
34
|
-
uri_decode: true,
|
|
35
|
-
params: params,
|
|
36
|
-
capture: extract_capture(format, version, requirements)
|
|
37
|
-
)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def build_path(pattern, anchor, suffix)
|
|
41
|
-
PatternCache[[build_path_from_pattern(pattern, anchor), suffix]]
|
|
42
|
-
end
|
|
43
|
-
|
|
44
31
|
def extract_capture(format, version, requirements)
|
|
45
|
-
capture = {}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
32
|
+
capture = {}
|
|
33
|
+
capture[:format] = map_str(format) if format.present?
|
|
34
|
+
capture[:version] = map_str(version) if version.present?
|
|
49
35
|
|
|
50
36
|
return capture if requirements.blank?
|
|
51
37
|
|
data/lib/grape/router/route.rb
CHANGED
|
@@ -8,25 +8,21 @@ module Grape
|
|
|
8
8
|
FORWARD_MATCH_METHOD = ->(input, pattern) { input.start_with?(pattern.origin) }
|
|
9
9
|
NON_FORWARD_MATCH_METHOD = ->(input, pattern) { pattern.match?(input) }
|
|
10
10
|
|
|
11
|
-
attr_reader :app, :request_method
|
|
11
|
+
attr_reader :app, :request_method, :index
|
|
12
12
|
|
|
13
|
-
def_delegators
|
|
13
|
+
def_delegators :@app, :call
|
|
14
14
|
|
|
15
|
-
def initialize(
|
|
15
|
+
def initialize(endpoint, method, pattern, options)
|
|
16
|
+
super(pattern, options)
|
|
17
|
+
@app = endpoint
|
|
16
18
|
@request_method = upcase_method(method)
|
|
17
|
-
@pattern = Grape::Router::Pattern.new(origin, path, options)
|
|
18
19
|
@match_function = options[:forward_match] ? FORWARD_MATCH_METHOD : NON_FORWARD_MATCH_METHOD
|
|
19
|
-
super(options)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def convert_to_head_request!
|
|
23
23
|
@request_method = Rack::HEAD
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def exec(env)
|
|
27
|
-
@app.call(env)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
26
|
def apply(app)
|
|
31
27
|
@app = app
|
|
32
28
|
self
|
|
@@ -42,7 +38,7 @@ module Grape
|
|
|
42
38
|
return params_without_input if input.blank?
|
|
43
39
|
|
|
44
40
|
parsed = pattern.params(input)
|
|
45
|
-
return
|
|
41
|
+
return unless parsed
|
|
46
42
|
|
|
47
43
|
parsed.compact.symbolize_keys
|
|
48
44
|
end
|
|
@@ -50,7 +46,7 @@ module Grape
|
|
|
50
46
|
private
|
|
51
47
|
|
|
52
48
|
def params_without_input
|
|
53
|
-
@params_without_input ||= pattern.captures_default.merge(
|
|
49
|
+
@params_without_input ||= pattern.captures_default.merge(options[:params])
|
|
54
50
|
end
|
|
55
51
|
|
|
56
52
|
def upcase_method(method)
|
data/lib/grape/router.rb
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module Grape
|
|
4
4
|
class Router
|
|
5
|
-
attr_reader :map, :compiled
|
|
6
|
-
|
|
7
5
|
# Taken from Rails
|
|
8
6
|
# normalize_path("/foo") # => "/foo"
|
|
9
7
|
# normalize_path("/foo/") # => "/foo"
|
|
@@ -16,7 +14,7 @@ module Grape
|
|
|
16
14
|
return path if path == '/'
|
|
17
15
|
|
|
18
16
|
# Fast path for the overwhelming majority of paths that don't need to be normalized
|
|
19
|
-
return path
|
|
17
|
+
return path if path.start_with?('/') && !(path.end_with?('/') || path.match?(%r{%|//}))
|
|
20
18
|
|
|
21
19
|
# Slow path
|
|
22
20
|
encoding = path.encoding
|
|
@@ -39,14 +37,14 @@ module Grape
|
|
|
39
37
|
end
|
|
40
38
|
|
|
41
39
|
def compile!
|
|
42
|
-
return if compiled
|
|
40
|
+
return if @compiled
|
|
43
41
|
|
|
44
42
|
@union = Regexp.union(@neutral_regexes)
|
|
45
43
|
@neutral_regexes = nil
|
|
46
44
|
(Grape::HTTP_SUPPORTED_METHODS + ['*']).each do |method|
|
|
47
|
-
next unless map.key?(method)
|
|
45
|
+
next unless @map.key?(method)
|
|
48
46
|
|
|
49
|
-
routes = map[method]
|
|
47
|
+
routes = @map[method]
|
|
50
48
|
optimized_map = routes.map.with_index { |route, index| route.to_regexp(index) }
|
|
51
49
|
@optimized_map[method] = Regexp.union(optimized_map)
|
|
52
50
|
end
|
|
@@ -54,20 +52,20 @@ module Grape
|
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
def append(route)
|
|
57
|
-
map[route.request_method] << route
|
|
55
|
+
@map[route.request_method] << route
|
|
58
56
|
end
|
|
59
57
|
|
|
60
|
-
def associate_routes(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@neutral_map << greedy_route
|
|
64
|
-
end
|
|
58
|
+
def associate_routes(greedy_route)
|
|
59
|
+
@neutral_regexes << greedy_route.to_regexp(@neutral_map.length)
|
|
60
|
+
@neutral_map << greedy_route
|
|
65
61
|
end
|
|
66
62
|
|
|
67
63
|
def call(env)
|
|
68
64
|
with_optimization do
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
input = Router.normalize_path(env[Rack::PATH_INFO])
|
|
66
|
+
method = env[Rack::REQUEST_METHOD]
|
|
67
|
+
response, route = identity(input, method, env)
|
|
68
|
+
response || rotation(input, method, env, route)
|
|
71
69
|
end
|
|
72
70
|
end
|
|
73
71
|
|
|
@@ -80,31 +78,28 @@ module Grape
|
|
|
80
78
|
|
|
81
79
|
private
|
|
82
80
|
|
|
83
|
-
def identity(env)
|
|
81
|
+
def identity(input, method, env)
|
|
84
82
|
route = nil
|
|
85
|
-
response = transaction(env) do
|
|
83
|
+
response = transaction(input, method, env) do
|
|
86
84
|
route = match?(input, method)
|
|
87
|
-
process_route(route, env) if route
|
|
85
|
+
process_route(route, input, env) if route
|
|
88
86
|
end
|
|
89
87
|
[response, route]
|
|
90
88
|
end
|
|
91
89
|
|
|
92
|
-
def rotation(env, exact_route
|
|
90
|
+
def rotation(input, method, env, exact_route)
|
|
93
91
|
response = nil
|
|
94
|
-
|
|
95
|
-
map[method].each do |route|
|
|
92
|
+
@map[method].each do |route|
|
|
96
93
|
next if exact_route == route
|
|
97
94
|
next unless route.match?(input)
|
|
98
95
|
|
|
99
|
-
response = process_route(route, env)
|
|
96
|
+
response = process_route(route, input, env)
|
|
100
97
|
break unless cascade?(response)
|
|
101
98
|
end
|
|
102
99
|
response
|
|
103
100
|
end
|
|
104
101
|
|
|
105
|
-
def transaction(env)
|
|
106
|
-
input, method = *extract_input_and_method(env)
|
|
107
|
-
|
|
102
|
+
def transaction(input, method, env)
|
|
108
103
|
# using a Proc is important since `return` will exit the enclosing function
|
|
109
104
|
cascade_or_return_response = proc do |response|
|
|
110
105
|
if response
|
|
@@ -112,47 +107,41 @@ module Grape
|
|
|
112
107
|
return response unless cascade
|
|
113
108
|
|
|
114
109
|
# we need to close the body if possible before dismissing
|
|
115
|
-
response[2].
|
|
110
|
+
response[2].close if response[2].respond_to?(:close)
|
|
116
111
|
end
|
|
117
112
|
end
|
|
118
113
|
end
|
|
119
114
|
|
|
120
|
-
|
|
115
|
+
response = yield
|
|
116
|
+
last_response_cascade = cascade_or_return_response.call(response)
|
|
121
117
|
last_neighbor_route = greedy_match?(input)
|
|
122
118
|
|
|
123
119
|
# If last_neighbor_route exists and request method is OPTIONS,
|
|
124
|
-
# return response by using #
|
|
125
|
-
return
|
|
120
|
+
# return response by using #include_allow_header.
|
|
121
|
+
return process_route(last_neighbor_route, input, env, include_allow_header: true) if !last_response_cascade && method == Rack::OPTIONS && last_neighbor_route
|
|
126
122
|
|
|
127
123
|
route = match?(input, '*')
|
|
128
124
|
|
|
129
|
-
return last_neighbor_route.
|
|
125
|
+
return last_neighbor_route.call(env) if last_neighbor_route && last_response_cascade && route
|
|
130
126
|
|
|
131
|
-
last_response_cascade = cascade_or_return_response.call(process_route(route, env)) if route
|
|
127
|
+
last_response_cascade = cascade_or_return_response.call(process_route(route, input, env)) if route
|
|
132
128
|
|
|
133
|
-
return
|
|
129
|
+
return process_route(last_neighbor_route, input, env, include_allow_header: true) if !last_response_cascade && last_neighbor_route
|
|
134
130
|
|
|
135
131
|
nil
|
|
136
132
|
end
|
|
137
133
|
|
|
138
|
-
def process_route(route, env)
|
|
139
|
-
|
|
140
|
-
route.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
args.merge(route.params(input))
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def extract_input_and_method(env)
|
|
149
|
-
input = string_for(env[Rack::PATH_INFO])
|
|
150
|
-
method = env[Rack::REQUEST_METHOD]
|
|
151
|
-
[input, method]
|
|
134
|
+
def process_route(route, input, env, include_allow_header: false)
|
|
135
|
+
args = env[Grape::Env::GRAPE_ROUTING_ARGS] || { route_info: route }
|
|
136
|
+
route_params = route.params(input)
|
|
137
|
+
routing_args = args.merge(route_params || {})
|
|
138
|
+
env[Grape::Env::GRAPE_ROUTING_ARGS] = routing_args
|
|
139
|
+
env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.allow_header if include_allow_header
|
|
140
|
+
route.call(env)
|
|
152
141
|
end
|
|
153
142
|
|
|
154
143
|
def with_optimization
|
|
155
|
-
compile!
|
|
144
|
+
compile!
|
|
156
145
|
yield || default_response
|
|
157
146
|
end
|
|
158
147
|
|
|
@@ -169,23 +158,8 @@ module Grape
|
|
|
169
158
|
@union.match(input) { |m| @neutral_map.detect { |route| m[route.regexp_capture_index] } }
|
|
170
159
|
end
|
|
171
160
|
|
|
172
|
-
def call_with_allow_headers(env, route)
|
|
173
|
-
prepare_env_from_route(env, route)
|
|
174
|
-
env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.options[:allow_header]
|
|
175
|
-
route.options[:endpoint].call(env)
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def prepare_env_from_route(env, route)
|
|
179
|
-
input, = *extract_input_and_method(env)
|
|
180
|
-
env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(env[Grape::Env::GRAPE_ROUTING_ARGS], route, input)
|
|
181
|
-
end
|
|
182
|
-
|
|
183
161
|
def cascade?(response)
|
|
184
162
|
response && response[1]['X-Cascade'] == 'pass'
|
|
185
163
|
end
|
|
186
|
-
|
|
187
|
-
def string_for(input)
|
|
188
|
-
self.class.normalize_path(input)
|
|
189
|
-
end
|
|
190
164
|
end
|
|
191
165
|
end
|
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module Util
|
|
5
5
|
class ApiDescription
|
|
6
|
-
|
|
7
|
-
@endpoint_configuration = endpoint_configuration
|
|
8
|
-
@attributes = { description: description }
|
|
9
|
-
instance_eval(&block)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
%i[
|
|
6
|
+
DSL_METHODS = %i[
|
|
13
7
|
body_name
|
|
14
8
|
consumes
|
|
15
9
|
default
|
|
@@ -27,7 +21,15 @@ module Grape
|
|
|
27
21
|
security
|
|
28
22
|
summary
|
|
29
23
|
tags
|
|
30
|
-
].
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
def initialize(description, endpoint_configuration, &)
|
|
27
|
+
@endpoint_configuration = endpoint_configuration
|
|
28
|
+
@attributes = { description: description }
|
|
29
|
+
instance_eval(&)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
DSL_METHODS.each do |attribute|
|
|
31
33
|
define_method attribute do |value|
|
|
32
34
|
@attributes[attribute] = value
|
|
33
35
|
end
|
|
@@ -14,8 +14,8 @@ module Grape
|
|
|
14
14
|
@params = Array.wrap(@original_params)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def each(&
|
|
18
|
-
do_each(@params, &
|
|
17
|
+
def each(&)
|
|
18
|
+
do_each(@params, &) # because we need recursion for nested arrays
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
private
|
|
@@ -14,6 +14,8 @@ module Grape
|
|
|
14
14
|
# we maintain a list of them here and skip looking up validators for them.
|
|
15
15
|
RESERVED_DOCUMENTATION_KEYWORDS = %i[as required param_type is_array format example].freeze
|
|
16
16
|
|
|
17
|
+
SPECIAL_JSON = [JSON, Array[JSON]].freeze
|
|
18
|
+
|
|
17
19
|
class Attr
|
|
18
20
|
attr_accessor :key, :scope
|
|
19
21
|
|
|
@@ -78,7 +80,7 @@ module Grape
|
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
def configuration
|
|
81
|
-
@api.configuration.respond_to?(:evaluate)
|
|
83
|
+
(@api.configuration.respond_to?(:evaluate) && @api.configuration.evaluate) || @api.configuration
|
|
82
84
|
end
|
|
83
85
|
|
|
84
86
|
# @return [Boolean] whether or not this entire scope needs to be
|
|
@@ -120,8 +122,8 @@ module Grape
|
|
|
120
122
|
if dependency.is_a?(Hash)
|
|
121
123
|
dependency_key = dependency.keys[0]
|
|
122
124
|
proc = dependency.values[0]
|
|
123
|
-
return false unless proc.call(params
|
|
124
|
-
elsif params
|
|
125
|
+
return false unless proc.call(params[dependency_key])
|
|
126
|
+
elsif params[dependency].blank?
|
|
125
127
|
return false
|
|
126
128
|
end
|
|
127
129
|
end
|
|
@@ -265,7 +267,7 @@ module Grape
|
|
|
265
267
|
# @param optional [Boolean] whether the parameter this are nested under
|
|
266
268
|
# is optional or not (and hence, whether this block's params will be).
|
|
267
269
|
# @yield parameter scope
|
|
268
|
-
def new_scope(attrs, opts, optional = false, &
|
|
270
|
+
def new_scope(attrs, opts, optional = false, &)
|
|
269
271
|
# if required params are grouped and no type or unsupported type is provided, raise an error
|
|
270
272
|
type = opts[:type]
|
|
271
273
|
if attrs.first && !optional
|
|
@@ -281,7 +283,7 @@ module Grape
|
|
|
281
283
|
optional: optional,
|
|
282
284
|
type: type || Array,
|
|
283
285
|
group: @group,
|
|
284
|
-
&
|
|
286
|
+
&
|
|
285
287
|
)
|
|
286
288
|
end
|
|
287
289
|
|
|
@@ -292,7 +294,7 @@ module Grape
|
|
|
292
294
|
# scope should only validate if this parameter from the above scope is
|
|
293
295
|
# present
|
|
294
296
|
# @yield parameter scope
|
|
295
|
-
def new_lateral_scope(options, &
|
|
297
|
+
def new_lateral_scope(options, &)
|
|
296
298
|
self.class.new(
|
|
297
299
|
api: @api,
|
|
298
300
|
element: nil,
|
|
@@ -300,7 +302,7 @@ module Grape
|
|
|
300
302
|
options: @optional,
|
|
301
303
|
type: type == Array ? Array : Hash,
|
|
302
304
|
dependent_on: options[:dependent_on],
|
|
303
|
-
&
|
|
305
|
+
&
|
|
304
306
|
)
|
|
305
307
|
end
|
|
306
308
|
|
|
@@ -309,8 +311,8 @@ module Grape
|
|
|
309
311
|
# @param attrs [Array] the attributes passed to the `requires` or
|
|
310
312
|
# `optional` invocation that opened this scope.
|
|
311
313
|
# @yield parameter scope
|
|
312
|
-
def new_group_scope(attrs, &
|
|
313
|
-
self.class.new(api: @api, parent: self, group: attrs.first, &
|
|
314
|
+
def new_group_scope(attrs, &)
|
|
315
|
+
self.class.new(api: @api, parent: self, group: attrs.first, &)
|
|
314
316
|
end
|
|
315
317
|
|
|
316
318
|
# Pushes declared params to parent or settings
|
|
@@ -414,7 +416,7 @@ module Grape
|
|
|
414
416
|
|
|
415
417
|
# but not special JSON types, which
|
|
416
418
|
# already imply coercion method
|
|
417
|
-
return
|
|
419
|
+
return unless SPECIAL_JSON.include?(validations[:coerce])
|
|
418
420
|
|
|
419
421
|
raise ArgumentError, 'coerce_with disallowed for type: JSON'
|
|
420
422
|
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.scrub if value.respond_to?(:
|
|
11
|
+
value = value.scrub if value.respond_to?(:valid_encoding?) && !value.valid_encoding?
|
|
12
12
|
|
|
13
13
|
return if value == false || value.present?
|
|
14
14
|
|
|
@@ -49,7 +49,7 @@ module Grape
|
|
|
49
49
|
next if !@scope.required? && empty_val
|
|
50
50
|
next unless @scope.meets_dependency?(val, params)
|
|
51
51
|
|
|
52
|
-
validate_param!(attr_name, val) if @required || val.
|
|
52
|
+
validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
|
|
53
53
|
rescue Grape::Exceptions::Validation => e
|
|
54
54
|
array_errors << e
|
|
55
55
|
end
|
|
@@ -69,7 +69,7 @@ module Grape
|
|
|
69
69
|
|
|
70
70
|
def options_key?(key, options = nil)
|
|
71
71
|
options = instance_variable_get(:@option) if options.nil?
|
|
72
|
-
options.
|
|
72
|
+
options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def fail_fast?
|
|
@@ -10,7 +10,7 @@ module Grape
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def validate_param!(attr_name, params)
|
|
13
|
-
return unless params.
|
|
13
|
+
return unless params.respond_to?(:key?) && params.key?(attr_name)
|
|
14
14
|
|
|
15
15
|
excepts = @except.is_a?(Proc) ? @except.call : @except
|
|
16
16
|
return if excepts.nil?
|
|
@@ -5,7 +5,7 @@ module Grape
|
|
|
5
5
|
module Validators
|
|
6
6
|
class PresenceValidator < Base
|
|
7
7
|
def validate_param!(attr_name, params)
|
|
8
|
-
return if params.
|
|
8
|
+
return if params.respond_to?(:key?) && params.key?(attr_name)
|
|
9
9
|
|
|
10
10
|
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
|
|
11
11
|
end
|
|
@@ -5,11 +5,21 @@ module Grape
|
|
|
5
5
|
module Validators
|
|
6
6
|
class RegexpValidator < Base
|
|
7
7
|
def validate_param!(attr_name, params)
|
|
8
|
-
return unless params.
|
|
9
|
-
|
|
8
|
+
return unless params.respond_to?(:key) && params.key?(attr_name)
|
|
9
|
+
|
|
10
|
+
value = options_key?(:value) ? @option[:value] : @option
|
|
11
|
+
return if Array.wrap(params[attr_name]).all? { |param| param.nil? || scrub(param.to_s).match?(value) }
|
|
10
12
|
|
|
11
13
|
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
|
|
12
14
|
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def scrub(param)
|
|
19
|
+
return param if param.valid_encoding?
|
|
20
|
+
|
|
21
|
+
param.scrub
|
|
22
|
+
end
|
|
13
23
|
end
|
|
14
24
|
end
|
|
15
25
|
end
|
|
@@ -16,7 +16,7 @@ module Grape
|
|
|
16
16
|
|
|
17
17
|
return if val.nil? && !required_for_root_scope?
|
|
18
18
|
|
|
19
|
-
val = val.scrub if val.respond_to?(:
|
|
19
|
+
val = val.scrub if val.respond_to?(:valid_encoding?) && !val.valid_encoding?
|
|
20
20
|
|
|
21
21
|
# don't forget that +false.blank?+ is true
|
|
22
22
|
return if val != false && val.blank? && @allow_blank
|
data/lib/grape/version.rb
CHANGED