grape 1.5.3 → 1.6.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 +13 -0
- data/README.md +23 -3
- data/UPGRADING.md +43 -1
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +13 -17
- data/lib/grape/api.rb +5 -12
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/helpers.rb +6 -4
- data/lib/grape/dsl/inside_route.rb +17 -8
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +3 -3
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +2 -2
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/endpoint.rb +20 -35
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/validation.rb +1 -2
- 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/middleware/base.rb +2 -0
- data/lib/grape/middleware/formatter.rb +4 -4
- data/lib/grape/middleware/stack.rb +2 -2
- 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/path.rb +1 -0
- data/lib/grape/request.rb +1 -0
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +6 -0
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -2
- data/lib/grape/validations/params_scope.rb +88 -55
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -0
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +3 -3
- data/lib/grape/validations/validators/all_or_none.rb +1 -0
- data/lib/grape/validations/validators/as.rb +4 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -0
- data/lib/grape/validations/validators/base.rb +4 -1
- data/lib/grape/validations/validators/coerce.rb +1 -5
- data/lib/grape/validations/validators/default.rb +1 -0
- data/lib/grape/validations/validators/exactly_one_of.rb +1 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -0
- data/lib/grape/validations/validators/presence.rb +1 -0
- data/lib/grape/validations/validators/regexp.rb +1 -0
- data/lib/grape/validations/validators/same_as.rb +1 -0
- data/lib/grape/validations/validators/values.rb +3 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +1 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api_spec.rb +126 -37
- data/spec/grape/dsl/callbacks_spec.rb +1 -1
- data/spec/grape/dsl/middleware_spec.rb +1 -1
- data/spec/grape/dsl/parameters_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/endpoint/declared_spec.rb +247 -0
- data/spec/grape/endpoint_spec.rb +5 -5
- data/spec/grape/entity_spec.rb +9 -9
- data/spec/grape/middleware/auth/dsl_spec.rb +1 -1
- data/spec/grape/middleware/error_spec.rb +1 -2
- data/spec/grape/middleware/formatter_spec.rb +2 -2
- data/spec/grape/middleware/stack_spec.rb +3 -1
- data/spec/grape/validations/params_scope_spec.rb +37 -3
- data/spec/grape/validations/single_attribute_iterator_spec.rb +1 -1
- data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
- data/spec/grape/validations/validators/coerce_spec.rb +13 -10
- data/spec/grape/validations/validators/except_values_spec.rb +2 -2
- data/spec/grape/validations/validators/values_spec.rb +15 -11
- data/spec/grape/validations_spec.rb +54 -42
- data/spec/shared/versioning_examples.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- metadata +6 -6
data/lib/grape/dsl/settings.rb
CHANGED
@@ -103,12 +103,14 @@ module Grape
|
|
103
103
|
def namespace_stackable_with_hash(key)
|
104
104
|
settings = get_or_set :namespace_stackable, key, nil
|
105
105
|
return if settings.blank?
|
106
|
+
|
106
107
|
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
|
107
108
|
end
|
108
109
|
|
109
110
|
def namespace_reverse_stackable_with_hash(key)
|
110
111
|
settings = get_or_set :namespace_reverse_stackable, key, nil
|
111
112
|
return if settings.blank?
|
113
|
+
|
112
114
|
result = {}
|
113
115
|
settings.each do |setting|
|
114
116
|
setting.each do |field, value|
|
@@ -154,10 +156,10 @@ module Grape
|
|
154
156
|
|
155
157
|
# Execute the block within a context where our inheritable settings are forked
|
156
158
|
# to a new copy (see #namespace_start).
|
157
|
-
def within_namespace(&
|
159
|
+
def within_namespace(&block)
|
158
160
|
namespace_start
|
159
161
|
|
160
|
-
result = yield if
|
162
|
+
result = yield if block
|
161
163
|
|
162
164
|
namespace_end
|
163
165
|
reset_validations!
|
@@ -175,9 +177,7 @@ module Grape
|
|
175
177
|
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
|
176
178
|
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
|
177
179
|
# in the chain for every endpoint.
|
178
|
-
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
179
|
-
setting.inherit_from superclass.inheritable_setting
|
180
|
-
end
|
180
|
+
setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
data/lib/grape/endpoint.rb
CHANGED
@@ -20,7 +20,8 @@ module Grape
|
|
20
20
|
def before_each(new_setup = false, &block)
|
21
21
|
@before_each ||= []
|
22
22
|
if new_setup == false
|
23
|
-
return @before_each unless
|
23
|
+
return @before_each unless block
|
24
|
+
|
24
25
|
@before_each << block
|
25
26
|
else
|
26
27
|
@before_each = [new_setup]
|
@@ -46,9 +47,7 @@ module Grape
|
|
46
47
|
# @return [Proc]
|
47
48
|
# @raise [NameError] an instance method with the same name already exists
|
48
49
|
def generate_api_method(method_name, &block)
|
49
|
-
if method_defined?(method_name)
|
50
|
-
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
|
51
|
-
end
|
50
|
+
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name)
|
52
51
|
|
53
52
|
define_method(method_name, &block)
|
54
53
|
method = instance_method(method_name)
|
@@ -106,7 +105,7 @@ module Grape
|
|
106
105
|
@body = nil
|
107
106
|
@proc = nil
|
108
107
|
|
109
|
-
return unless
|
108
|
+
return unless block
|
110
109
|
|
111
110
|
@source = block
|
112
111
|
@block = self.class.generate_api_method(method_name, &block)
|
@@ -118,11 +117,9 @@ module Grape
|
|
118
117
|
inheritable_setting.route[:saved_validations] += namespace_stackable[:validations]
|
119
118
|
parent_declared_params = namespace_stackable[:declared_params]
|
120
119
|
|
121
|
-
if parent_declared_params
|
122
|
-
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten)
|
123
|
-
end
|
120
|
+
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
|
124
121
|
|
125
|
-
endpoints
|
122
|
+
endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
|
126
123
|
end
|
127
124
|
|
128
125
|
def require_option(options, key)
|
@@ -142,7 +139,7 @@ module Grape
|
|
142
139
|
end
|
143
140
|
|
144
141
|
def reset_routes!
|
145
|
-
endpoints
|
142
|
+
endpoints&.each(&:reset_routes!)
|
146
143
|
@namespace = nil
|
147
144
|
@routes = nil
|
148
145
|
end
|
@@ -154,13 +151,9 @@ module Grape
|
|
154
151
|
reset_routes!
|
155
152
|
routes.each do |route|
|
156
153
|
methods = [route.request_method]
|
157
|
-
if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
|
158
|
-
methods << Grape::Http::Headers::HEAD
|
159
|
-
end
|
154
|
+
methods << Grape::Http::Headers::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
|
160
155
|
methods.each do |method|
|
161
|
-
unless route.request_method == method
|
162
|
-
route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h)
|
163
|
-
end
|
156
|
+
route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
|
164
157
|
router.append(route.apply(self))
|
165
158
|
end
|
166
159
|
end
|
@@ -200,6 +193,7 @@ module Grape
|
|
200
193
|
def prepare_version
|
201
194
|
version = namespace_inheritable(:version) || []
|
202
195
|
return if version.empty?
|
196
|
+
|
203
197
|
version.length == 1 ? version.first.to_s : version
|
204
198
|
end
|
205
199
|
|
@@ -234,7 +228,7 @@ module Grape
|
|
234
228
|
# Return the collection of endpoints within this endpoint.
|
235
229
|
# This is the case when an Grape::API mounts another Grape::API.
|
236
230
|
def endpoints
|
237
|
-
options[:app].endpoints if options[:app]
|
231
|
+
options[:app].endpoints if options[:app].respond_to?(:endpoints)
|
238
232
|
end
|
239
233
|
|
240
234
|
def equals?(e)
|
@@ -256,13 +250,13 @@ module Grape
|
|
256
250
|
|
257
251
|
if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
|
258
252
|
raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allowed_methods)) unless options?
|
253
|
+
|
259
254
|
header 'Allow', allowed_methods
|
260
255
|
response_object = ''
|
261
256
|
status 204
|
262
257
|
else
|
263
258
|
run_filters before_validations, :before_validation
|
264
259
|
run_validators validations, request
|
265
|
-
remove_renamed_params
|
266
260
|
run_filters after_validations, :after_validation
|
267
261
|
response_object = execute
|
268
262
|
end
|
@@ -328,14 +322,7 @@ module Grape
|
|
328
322
|
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
329
323
|
end
|
330
324
|
|
331
|
-
|
332
|
-
return unless route_setting(:renamed_params)
|
333
|
-
route_setting(:renamed_params).flat_map(&:keys).each do |renamed_param|
|
334
|
-
@params.delete(renamed_param)
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
private :build_stack, :build_helpers, :remove_renamed_params
|
325
|
+
private :build_stack, :build_helpers
|
339
326
|
|
340
327
|
def execute
|
341
328
|
@block ? @block.call(self) : nil
|
@@ -365,15 +352,13 @@ module Grape
|
|
365
352
|
|
366
353
|
ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
|
367
354
|
validators.each do |validator|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
break if validator.fail_fast?
|
376
|
-
end
|
355
|
+
validator.validate(request)
|
356
|
+
rescue Grape::Exceptions::Validation => e
|
357
|
+
validation_errors << e
|
358
|
+
break if validator.fail_fast?
|
359
|
+
rescue Grape::Exceptions::ValidationArrayErrors => e
|
360
|
+
validation_errors.concat e.errors
|
361
|
+
break if validator.fail_fast?
|
377
362
|
end
|
378
363
|
end
|
379
364
|
|
@@ -10,12 +10,8 @@ module Grape
|
|
10
10
|
result = wrap_message(present(message, env))
|
11
11
|
|
12
12
|
rescue_options = options[:rescue_options] || {}
|
13
|
-
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
14
|
-
|
15
|
-
end
|
16
|
-
if rescue_options[:original_exception] && original_exception
|
17
|
-
result = result.merge(original_exception: original_exception.inspect)
|
18
|
-
end
|
13
|
+
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
14
|
+
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
19
15
|
::Grape::Json.dump(result)
|
20
16
|
end
|
21
17
|
|
@@ -11,12 +11,8 @@ module Grape
|
|
11
11
|
|
12
12
|
result = message.is_a?(Hash) ? message : { message: message }
|
13
13
|
rescue_options = options[:rescue_options] || {}
|
14
|
-
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
15
|
-
|
16
|
-
end
|
17
|
-
if rescue_options[:original_exception] && original_exception
|
18
|
-
result = result.merge(original_exception: original_exception.inspect)
|
19
|
-
end
|
14
|
+
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
15
|
+
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
20
16
|
result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
|
21
17
|
end
|
22
18
|
end
|
@@ -5,8 +5,7 @@ require 'grape/exceptions/base'
|
|
5
5
|
module Grape
|
6
6
|
module Exceptions
|
7
7
|
class Validation < Grape::Exceptions::Base
|
8
|
-
attr_accessor :params
|
9
|
-
attr_accessor :message_key
|
8
|
+
attr_accessor :params, :message_key
|
10
9
|
|
11
10
|
def initialize(params:, message: nil, **args)
|
12
11
|
@params = params
|
data/lib/grape/formatter/json.rb
CHANGED
@@ -8,13 +8,14 @@ module Grape
|
|
8
8
|
return object if object.is_a?(String)
|
9
9
|
return ::Grape::Json.dump(serialize(object)) if serializable?(object)
|
10
10
|
return object.to_json if object.respond_to?(:to_json)
|
11
|
+
|
11
12
|
::Grape::Json.dump(object)
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
15
16
|
|
16
17
|
def serializable?(object)
|
17
|
-
object.respond_to?(:serializable_hash) || object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash } || object.is_a?(Hash)
|
18
|
+
object.respond_to?(:serializable_hash) || (object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }) || object.is_a?(Hash)
|
18
19
|
end
|
19
20
|
|
20
21
|
def serialize(object)
|
data/lib/grape/formatter/xml.rb
CHANGED
@@ -59,6 +59,7 @@ module Grape
|
|
59
59
|
|
60
60
|
def response
|
61
61
|
return @app_response if @app_response.is_a?(Rack::Response)
|
62
|
+
|
62
63
|
Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
|
63
64
|
end
|
64
65
|
|
@@ -84,6 +85,7 @@ module Grape
|
|
84
85
|
|
85
86
|
def merge_headers(response)
|
86
87
|
return unless headers.is_a?(Hash)
|
88
|
+
|
87
89
|
case response
|
88
90
|
when Rack::Response then response.headers.merge!(headers)
|
89
91
|
when Array then response[1].merge!(headers)
|
@@ -22,6 +22,7 @@ module Grape
|
|
22
22
|
|
23
23
|
def after
|
24
24
|
return unless @app_response
|
25
|
+
|
25
26
|
status, headers, bodies = *@app_response
|
26
27
|
|
27
28
|
if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
|
@@ -79,7 +80,7 @@ module Grape
|
|
79
80
|
(request.post? || request.put? || request.patch? || request.delete?) &&
|
80
81
|
(!request.form_data? || !request.media_type) &&
|
81
82
|
!request.parseable_data? &&
|
82
|
-
(request.content_length.to_i
|
83
|
+
(request.content_length.to_i.positive? || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
|
83
84
|
|
84
85
|
return unless (input = env[Grape::Env::RACK_INPUT])
|
85
86
|
|
@@ -96,9 +97,7 @@ module Grape
|
|
96
97
|
def read_rack_input(body)
|
97
98
|
fmt = request.media_type ? mime_types[request.media_type] : options[:default_format]
|
98
99
|
|
99
|
-
unless content_type_for(fmt)
|
100
|
-
throw :error, status: 415, message: "The provided content-type '#{request.media_type}' is not supported."
|
101
|
-
end
|
100
|
+
throw :error, status: 415, message: "The provided content-type '#{request.media_type}' is not supported." unless content_type_for(fmt)
|
102
101
|
parser = Grape::Parser.parser_for fmt, **options
|
103
102
|
if parser
|
104
103
|
begin
|
@@ -145,6 +144,7 @@ module Grape
|
|
145
144
|
fmt = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[Grape::Http::Headers::FORMAT]
|
146
145
|
# avoid symbol memory leak on an unknown format
|
147
146
|
return fmt.to_sym if content_type_for(fmt)
|
147
|
+
|
148
148
|
fmt
|
149
149
|
end
|
150
150
|
|
@@ -22,11 +22,9 @@ module Grape
|
|
22
22
|
def before
|
23
23
|
potential_version = (env[Grape::Http::Headers::HTTP_ACCEPT_VERSION] || '').strip
|
24
24
|
|
25
|
-
if strict?
|
25
|
+
if strict? && potential_version.empty?
|
26
26
|
# If no Accept-Version header:
|
27
|
-
|
28
|
-
throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
|
29
|
-
end
|
27
|
+
throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
|
30
28
|
end
|
31
29
|
|
32
30
|
return if potential_version.empty?
|
@@ -51,7 +49,7 @@ module Grape
|
|
51
49
|
# of routes (see Grape::Router) for more information). To prevent
|
52
50
|
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
53
51
|
def cascade?
|
54
|
-
if options[:version_options]
|
52
|
+
if options[:version_options]&.key?(:cascade)
|
55
53
|
options[:version_options][:cascade]
|
56
54
|
else
|
57
55
|
true
|
@@ -26,10 +26,10 @@ module Grape
|
|
26
26
|
# route.
|
27
27
|
class Header < Base
|
28
28
|
VENDOR_VERSION_HEADER_REGEX =
|
29
|
-
/\Avnd\.([a-z0-9.\-_
|
29
|
+
/\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/.freeze
|
30
30
|
|
31
|
-
HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_
|
32
|
-
HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_
|
31
|
+
HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#{Regexp.last_match(0)}\^]+/.freeze
|
32
|
+
HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))+/.freeze
|
33
33
|
|
34
34
|
def before
|
35
35
|
strict_header_checks if strict?
|
@@ -52,12 +52,14 @@ module Grape
|
|
52
52
|
|
53
53
|
def strict_accept_header_presence_check
|
54
54
|
return unless header.qvalues.empty?
|
55
|
+
|
55
56
|
fail_with_invalid_accept_header!('Accept header must be set.')
|
56
57
|
end
|
57
58
|
|
58
59
|
def strict_version_vendor_accept_header_presence_check
|
59
60
|
return unless versions.present?
|
60
61
|
return if an_accept_header_with_version_and_vendor_is_present?
|
62
|
+
|
61
63
|
fail_with_invalid_accept_header!('API vendor or version not found.')
|
62
64
|
end
|
63
65
|
|
@@ -160,7 +162,7 @@ module Grape
|
|
160
162
|
# information). To prevent # this behavior, and not add the `X-Cascade`
|
161
163
|
# header, one can set the `:cascade` option to `false`.
|
162
164
|
def cascade?
|
163
|
-
if version_options
|
165
|
+
if version_options&.key?(:cascade)
|
164
166
|
version_options[:cascade]
|
165
167
|
else
|
166
168
|
true
|
@@ -32,6 +32,7 @@ module Grape
|
|
32
32
|
def before
|
33
33
|
potential_version = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[paramkey]
|
34
34
|
return if potential_version.nil?
|
35
|
+
|
35
36
|
throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' } if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
36
37
|
env[Grape::Env::API_VERSION] = potential_version
|
37
38
|
env[Grape::Env::RACK_REQUEST_QUERY_HASH].delete(paramkey) if env.key? Grape::Env::RACK_REQUEST_QUERY_HASH
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'English'
|
3
4
|
module Rack
|
4
5
|
module Accept
|
5
6
|
module Header
|
6
|
-
ALLOWED_CHARACTERS = %r{^([a-z*]+)
|
7
|
+
ALLOWED_CHARACTERS = %r{^([a-z*]+)/([a-z0-9*&\^\-_#{$ERROR_INFO}.+]+)(?:;([a-z0-9=;]+))?$}.freeze
|
7
8
|
class << self
|
8
9
|
# Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
|
9
10
|
def parse_media_type(media_type)
|
@@ -37,6 +37,7 @@ module Grape
|
|
37
37
|
pieces = path.split('/')
|
38
38
|
potential_version = pieces[1]
|
39
39
|
return unless potential_version&.match?(options[:pattern])
|
40
|
+
|
40
41
|
throw :error, status: 404, message: '404 API Version Not Found' if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
41
42
|
env[Grape::Env::API_VERSION] = potential_version
|
42
43
|
end
|
@@ -45,6 +46,7 @@ module Grape
|
|
45
46
|
|
46
47
|
def mounted_path?(path)
|
47
48
|
return false unless mount_path && path.start_with?(mount_path)
|
49
|
+
|
48
50
|
rest = path.slice(mount_path.length..-1)
|
49
51
|
rest.start_with?('/') || rest.empty?
|
50
52
|
end
|
data/lib/grape/path.rb
CHANGED
data/lib/grape/request.rb
CHANGED
@@ -37,6 +37,7 @@ module Grape
|
|
37
37
|
Grape::Util::LazyObject.new do
|
38
38
|
env.each_pair.with_object({}) do |(k, v), headers|
|
39
39
|
next unless k.to_s.start_with? HTTP_PREFIX
|
40
|
+
|
40
41
|
transformed_header = Grape::Http::Headers::HTTP_HEADERS[k] || transform_header(k)
|
41
42
|
headers[transformed_header] = v
|
42
43
|
end
|
data/lib/grape/router/pattern.rb
CHANGED
data/lib/grape/router/route.rb
CHANGED
@@ -84,8 +84,8 @@ module Grape
|
|
84
84
|
path, line = *location.scan(SOURCE_LOCATION_REGEXP).first
|
85
85
|
path = File.realpath(path) if Pathname.new(path).relative?
|
86
86
|
expected ||= name
|
87
|
-
warn
|
88
|
-
#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
|
87
|
+
warn <<~WARNING
|
88
|
+
#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
|
89
89
|
WARNING
|
90
90
|
end
|
91
91
|
end
|
data/lib/grape/router.rb
CHANGED
@@ -28,6 +28,7 @@ module Grape
|
|
28
28
|
|
29
29
|
def compile!
|
30
30
|
return if compiled
|
31
|
+
|
31
32
|
@union = Regexp.union(@neutral_regexes)
|
32
33
|
@neutral_regexes = nil
|
33
34
|
self.class.supported_methods.each do |method|
|
@@ -60,6 +61,7 @@ module Grape
|
|
60
61
|
def recognize_path(input)
|
61
62
|
any = with_optimization { greedy_match?(input) }
|
62
63
|
return if any == default_response
|
64
|
+
|
63
65
|
any.endpoint
|
64
66
|
end
|
65
67
|
|
@@ -80,6 +82,7 @@ module Grape
|
|
80
82
|
map[method].each do |route|
|
81
83
|
next if exact_route == route
|
82
84
|
next unless route.match?(input)
|
85
|
+
|
83
86
|
response = process_route(route, env)
|
84
87
|
break unless cascade?(response)
|
85
88
|
end
|
@@ -91,6 +94,7 @@ module Grape
|
|
91
94
|
response = yield(input, method)
|
92
95
|
|
93
96
|
return response if response && !(cascade = cascade?(response))
|
97
|
+
|
94
98
|
last_neighbor_route = greedy_match?(input)
|
95
99
|
|
96
100
|
# If last_neighbor_route exists and request method is OPTIONS,
|
@@ -139,12 +143,14 @@ module Grape
|
|
139
143
|
def match?(input, method)
|
140
144
|
current_regexp = @optimized_map[method]
|
141
145
|
return unless current_regexp.match(input)
|
146
|
+
|
142
147
|
last_match = Regexp.last_match
|
143
148
|
@map[method].detect { |route| last_match["_#{route.index}"] }
|
144
149
|
end
|
145
150
|
|
146
151
|
def greedy_match?(input)
|
147
152
|
return unless @union.match(input)
|
153
|
+
|
148
154
|
last_match = Regexp.last_match
|
149
155
|
@neutral_map.detect { |route| last_match["_#{route.index}"] }
|
150
156
|
end
|
@@ -5,9 +5,7 @@ module Grape
|
|
5
5
|
# A branchable, inheritable settings object which can store both stackable
|
6
6
|
# and inheritable values (see InheritableValues and StackableValues).
|
7
7
|
class InheritableSetting
|
8
|
-
attr_accessor :route, :api_class, :namespace
|
9
|
-
attr_accessor :namespace_inheritable, :namespace_stackable, :namespace_reverse_stackable
|
10
|
-
attr_accessor :parent, :point_in_time_copies
|
8
|
+
attr_accessor :route, :api_class, :namespace, :namespace_inheritable, :namespace_stackable, :namespace_reverse_stackable, :parent, :point_in_time_copies
|
11
9
|
|
12
10
|
# Retrieve global settings.
|
13
11
|
def self.global
|
@@ -49,9 +49,10 @@ module Grape
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def []=(key, value)
|
52
|
-
@value_hash[key] =
|
52
|
+
@value_hash[key] = case value
|
53
|
+
when Hash
|
53
54
|
LazyValueHash.new(value)
|
54
|
-
|
55
|
+
when Array
|
55
56
|
LazyValueArray.new(value)
|
56
57
|
else
|
57
58
|
LazyValue.new(value)
|