grape 2.4.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +51 -0
- data/CONTRIBUTING.md +1 -9
- data/README.md +72 -31
- data/UPGRADING.md +34 -0
- data/grape.gemspec +4 -4
- data/lib/grape/api/instance.rb +50 -72
- data/lib/grape/api.rb +25 -34
- data/lib/grape/dry_types.rb +48 -4
- data/lib/grape/dsl/callbacks.rb +8 -58
- data/lib/grape/dsl/desc.rb +8 -67
- data/lib/grape/dsl/helpers.rb +59 -64
- data/lib/grape/dsl/inside_route.rb +20 -43
- data/lib/grape/dsl/logger.rb +3 -6
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +7 -16
- data/lib/grape/dsl/request_response.rb +136 -139
- data/lib/grape/dsl/routing.rb +229 -201
- data/lib/grape/dsl/settings.rb +22 -134
- data/lib/grape/dsl/validations.rb +37 -45
- data/lib/grape/endpoint.rb +64 -96
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/exceptions/base.rb +1 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/middleware/auth/dsl.rb +5 -6
- data/lib/grape/middleware/error.rb +1 -11
- data/lib/grape/middleware/formatter.rb +4 -2
- data/lib/grape/middleware/stack.rb +3 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/base.rb +24 -42
- data/lib/grape/middleware/versioner/header.rb +1 -1
- data/lib/grape/middleware/versioner/param.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +11 -0
- data/lib/grape/params_builder/base.rb +2 -0
- data/lib/grape/router.rb +4 -3
- data/lib/grape/util/api_description.rb +56 -0
- data/lib/grape/util/base_inheritable.rb +5 -2
- data/lib/grape/util/cache.rb +1 -0
- data/lib/grape/util/inheritable_setting.rb +7 -0
- data/lib/grape/util/media_type.rb +1 -1
- data/lib/grape/util/registry.rb +1 -1
- data/lib/grape/validations/contract_scope.rb +2 -2
- data/lib/grape/validations/params_documentation.rb +50 -0
- data/lib/grape/validations/params_scope.rb +38 -53
- data/lib/grape/validations/types/array_coercer.rb +2 -3
- data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
- data/lib/grape/validations/types/primitive_coercer.rb +1 -28
- data/lib/grape/validations/types.rb +10 -25
- data/lib/grape/validations/validators/base.rb +0 -7
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +7 -10
- metadata +24 -14
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -17
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/strict_hash_configuration.rb +0 -108
- data/lib/grape/validations/attributes_doc.rb +0 -60
|
@@ -3,54 +3,46 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module DSL
|
|
5
5
|
module Validations
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# # whatever
|
|
28
|
-
# end
|
|
29
|
-
def reset_validations!
|
|
30
|
-
unset_namespace_stackable :declared_params
|
|
31
|
-
unset_namespace_stackable :validations
|
|
32
|
-
unset_namespace_stackable :params
|
|
33
|
-
end
|
|
6
|
+
# Clears all defined parameters and validations. The main purpose of it is to clean up
|
|
7
|
+
# settings, so next endpoint won't interfere with previous one.
|
|
8
|
+
#
|
|
9
|
+
# params do
|
|
10
|
+
# # params for the endpoint below this block
|
|
11
|
+
# end
|
|
12
|
+
# post '/current' do
|
|
13
|
+
# # whatever
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # somewhere between them the reset_validations! method gets called
|
|
17
|
+
#
|
|
18
|
+
# params do
|
|
19
|
+
# # params for the endpoint below this block
|
|
20
|
+
# end
|
|
21
|
+
# post '/next' do
|
|
22
|
+
# # whatever
|
|
23
|
+
# end
|
|
24
|
+
def reset_validations!
|
|
25
|
+
inheritable_setting.namespace_stackable.delete(:declared_params, :params, :validations)
|
|
26
|
+
end
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
# Opens a root-level ParamsScope, defining parameter coercions and
|
|
29
|
+
# validations for the endpoint.
|
|
30
|
+
# @yield instance context of the new scope
|
|
31
|
+
def params(&block)
|
|
32
|
+
Grape::Validations::ParamsScope.new(api: self, type: Hash, &block)
|
|
33
|
+
end
|
|
41
34
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
35
|
+
# Declare the contract to be used for the endpoint's parameters.
|
|
36
|
+
# @param contract [Class<Dry::Validation::Contract> | Dry::Schema::Processor]
|
|
37
|
+
# The contract or schema to be used for validation. Optional.
|
|
38
|
+
# @yield a block yielding a new instance of Dry::Schema::Params
|
|
39
|
+
# subclass, allowing to define the schema inline. When the
|
|
40
|
+
# +contract+ parameter is a schema, it will be used as a parent. Optional.
|
|
41
|
+
def contract(contract = nil, &block)
|
|
42
|
+
raise ArgumentError, 'Either contract or block must be provided' unless contract || block
|
|
43
|
+
raise ArgumentError, 'Cannot inherit from contract, only schema' if block && contract.respond_to?(:schema)
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
end
|
|
45
|
+
Grape::Validations::ContractScope.new(self, contract, &block)
|
|
54
46
|
end
|
|
55
47
|
end
|
|
56
48
|
end
|
data/lib/grape/endpoint.rb
CHANGED
|
@@ -8,19 +8,15 @@ module Grape
|
|
|
8
8
|
class Endpoint
|
|
9
9
|
extend Forwardable
|
|
10
10
|
include Grape::DSL::Settings
|
|
11
|
+
include Grape::DSL::Headers
|
|
11
12
|
include Grape::DSL::InsideRoute
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
attr_reader :env, :request
|
|
14
|
+
attr_reader :env, :request, :source, :options
|
|
15
15
|
|
|
16
16
|
def_delegators :request, :params, :headers, :cookies
|
|
17
17
|
def_delegator :cookies, :response_cookies
|
|
18
18
|
|
|
19
19
|
class << self
|
|
20
|
-
def new(...)
|
|
21
|
-
self == Endpoint ? Class.new(Endpoint).new(...) : super
|
|
22
|
-
end
|
|
23
|
-
|
|
24
20
|
def before_each(new_setup = false, &block)
|
|
25
21
|
@before_each ||= []
|
|
26
22
|
if new_setup == false
|
|
@@ -36,33 +32,6 @@ module Grape
|
|
|
36
32
|
superclass.run_before_each(endpoint) unless self == Endpoint
|
|
37
33
|
before_each.each { |blk| blk.try(:call, endpoint) }
|
|
38
34
|
end
|
|
39
|
-
|
|
40
|
-
# @api private
|
|
41
|
-
#
|
|
42
|
-
# Create an UnboundMethod that is appropriate for executing an endpoint
|
|
43
|
-
# route.
|
|
44
|
-
#
|
|
45
|
-
# The unbound method allows explicit calls to +return+ without raising a
|
|
46
|
-
# +LocalJumpError+. The method will be removed, but a +Proc+ reference to
|
|
47
|
-
# it will be returned. The returned +Proc+ expects a single argument: the
|
|
48
|
-
# instance of +Endpoint+ to bind to the method during the call.
|
|
49
|
-
#
|
|
50
|
-
# @param [String, Symbol] method_name
|
|
51
|
-
# @return [Proc]
|
|
52
|
-
# @raise [NameError] an instance method with the same name already exists
|
|
53
|
-
def generate_api_method(method_name, &block)
|
|
54
|
-
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name)
|
|
55
|
-
|
|
56
|
-
define_method(method_name, &block)
|
|
57
|
-
method = instance_method(method_name)
|
|
58
|
-
remove_method(method_name)
|
|
59
|
-
|
|
60
|
-
proc do |endpoint_instance|
|
|
61
|
-
ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do
|
|
62
|
-
method.bind_call(endpoint_instance)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
35
|
end
|
|
67
36
|
|
|
68
37
|
# Create a new endpoint.
|
|
@@ -86,11 +55,11 @@ module Grape
|
|
|
86
55
|
# now +namespace_stackable(:declared_params)+ contains all params defined for
|
|
87
56
|
# this endpoint and its parents, but later it will be cleaned up,
|
|
88
57
|
# see +reset_validations!+ in lib/grape/dsl/validations.rb
|
|
89
|
-
|
|
90
|
-
|
|
58
|
+
inheritable_setting.route[:declared_params] = inheritable_setting.namespace_stackable[:declared_params].flatten
|
|
59
|
+
inheritable_setting.route[:saved_validations] = inheritable_setting.namespace_stackable[:validations]
|
|
91
60
|
|
|
92
|
-
namespace_stackable
|
|
93
|
-
namespace_inheritable
|
|
61
|
+
inheritable_setting.namespace_stackable[:representations] = [] unless inheritable_setting.namespace_stackable[:representations]
|
|
62
|
+
inheritable_setting.namespace_inheritable[:default_error_status] = 500 unless inheritable_setting.namespace_inheritable[:default_error_status]
|
|
94
63
|
|
|
95
64
|
@options = options
|
|
96
65
|
|
|
@@ -102,17 +71,10 @@ module Grape
|
|
|
102
71
|
|
|
103
72
|
@lazy_initialize_lock = Mutex.new
|
|
104
73
|
@lazy_initialized = nil
|
|
105
|
-
@block = nil
|
|
106
|
-
|
|
107
74
|
@status = nil
|
|
108
75
|
@stream = nil
|
|
109
76
|
@body = nil
|
|
110
|
-
@proc = nil
|
|
111
|
-
|
|
112
|
-
return unless block
|
|
113
|
-
|
|
114
77
|
@source = block
|
|
115
|
-
@block = self.class.generate_api_method(method_name, &block)
|
|
116
78
|
end
|
|
117
79
|
|
|
118
80
|
# Update our settings from a given set of stackable parameters. Used when
|
|
@@ -130,14 +92,6 @@ module Grape
|
|
|
130
92
|
raise Grape::Exceptions::MissingOption.new(key) unless options.key?(key)
|
|
131
93
|
end
|
|
132
94
|
|
|
133
|
-
def method_name
|
|
134
|
-
[options[:method],
|
|
135
|
-
Namespace.joined_space(namespace_stackable(:namespace)),
|
|
136
|
-
(namespace_stackable(:mount_path) || []).join('/'),
|
|
137
|
-
options[:path].join('/')]
|
|
138
|
-
.join(' ')
|
|
139
|
-
end
|
|
140
|
-
|
|
141
95
|
def routes
|
|
142
96
|
@routes ||= endpoints&.collect(&:routes)&.flatten || to_routes
|
|
143
97
|
end
|
|
@@ -154,7 +108,7 @@ module Grape
|
|
|
154
108
|
reset_routes!
|
|
155
109
|
routes.each do |route|
|
|
156
110
|
router.append(route.apply(self))
|
|
157
|
-
next unless !namespace_inheritable
|
|
111
|
+
next unless !inheritable_setting.namespace_inheritable[:do_not_route_head] && route.request_method == Rack::GET
|
|
158
112
|
|
|
159
113
|
route.dup.then do |head_route|
|
|
160
114
|
head_route.convert_to_head_request!
|
|
@@ -175,7 +129,7 @@ module Grape
|
|
|
175
129
|
end
|
|
176
130
|
|
|
177
131
|
def prepare_routes_requirements
|
|
178
|
-
{}.merge!(*namespace_stackable
|
|
132
|
+
{}.merge!(*inheritable_setting.namespace_stackable[:namespace].map(&:requirements)).tap do |requirements|
|
|
179
133
|
endpoint_requirements = options.dig(:route_options, :requirements)
|
|
180
134
|
requirements.merge!(endpoint_requirements) if endpoint_requirements
|
|
181
135
|
end
|
|
@@ -186,7 +140,7 @@ module Grape
|
|
|
186
140
|
namespace: namespace,
|
|
187
141
|
version: prepare_version,
|
|
188
142
|
requirements: prepare_routes_requirements,
|
|
189
|
-
prefix: namespace_inheritable
|
|
143
|
+
prefix: inheritable_setting.namespace_inheritable[:root_prefix],
|
|
190
144
|
anchor: options[:route_options].fetch(:anchor, true),
|
|
191
145
|
settings: inheritable_setting.route.except(:declared_params, :saved_validations),
|
|
192
146
|
forward_match: options[:forward_match]
|
|
@@ -194,7 +148,7 @@ module Grape
|
|
|
194
148
|
end
|
|
195
149
|
|
|
196
150
|
def prepare_version
|
|
197
|
-
version = namespace_inheritable
|
|
151
|
+
version = inheritable_setting.namespace_inheritable[:version]
|
|
198
152
|
return if version.blank?
|
|
199
153
|
|
|
200
154
|
version.length == 1 ? version.first : version
|
|
@@ -211,7 +165,7 @@ module Grape
|
|
|
211
165
|
end
|
|
212
166
|
|
|
213
167
|
def namespace
|
|
214
|
-
@namespace ||= Namespace.joined_space_path(namespace_stackable
|
|
168
|
+
@namespace ||= Namespace.joined_space_path(inheritable_setting.namespace_stackable[:namespace])
|
|
215
169
|
end
|
|
216
170
|
|
|
217
171
|
def call(env)
|
|
@@ -222,6 +176,8 @@ module Grape
|
|
|
222
176
|
def call!(env)
|
|
223
177
|
env[Grape::Env::API_ENDPOINT] = self
|
|
224
178
|
@env = env
|
|
179
|
+
# this adds the helpers only to the instance
|
|
180
|
+
singleton_class.include(@helpers) if @helpers
|
|
225
181
|
@app.call(env)
|
|
226
182
|
end
|
|
227
183
|
|
|
@@ -248,7 +204,7 @@ module Grape
|
|
|
248
204
|
|
|
249
205
|
def run
|
|
250
206
|
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
|
|
251
|
-
@request = Grape::Request.new(env, build_params_with: namespace_inheritable
|
|
207
|
+
@request = Grape::Request.new(env, build_params_with: inheritable_setting.namespace_inheritable[:build_params_with])
|
|
252
208
|
begin
|
|
253
209
|
self.class.run_before_each(self)
|
|
254
210
|
run_filters befores, :before
|
|
@@ -284,11 +240,14 @@ module Grape
|
|
|
284
240
|
end
|
|
285
241
|
|
|
286
242
|
def execute
|
|
287
|
-
@
|
|
288
|
-
end
|
|
243
|
+
return unless @source
|
|
289
244
|
|
|
290
|
-
|
|
291
|
-
|
|
245
|
+
ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: self) do
|
|
246
|
+
instance_exec(&@source)
|
|
247
|
+
rescue LocalJumpError => e
|
|
248
|
+
Grape.deprecator.warn 'Using `return` in an endpoint has been deprecated. Use `next` instead.'
|
|
249
|
+
return e.exit_value
|
|
250
|
+
end
|
|
292
251
|
end
|
|
293
252
|
|
|
294
253
|
def lazy_initialize!
|
|
@@ -297,9 +256,8 @@ module Grape
|
|
|
297
256
|
@lazy_initialize_lock.synchronize do
|
|
298
257
|
return true if @lazy_initialized
|
|
299
258
|
|
|
300
|
-
@
|
|
301
|
-
@
|
|
302
|
-
|
|
259
|
+
@app = options[:app] || build_stack
|
|
260
|
+
@helpers = build_helpers
|
|
303
261
|
@lazy_initialized = true
|
|
304
262
|
end
|
|
305
263
|
end
|
|
@@ -332,14 +290,16 @@ module Grape
|
|
|
332
290
|
|
|
333
291
|
%i[befores before_validations after_validations afters finallies].each do |method|
|
|
334
292
|
define_method method do
|
|
335
|
-
namespace_stackable
|
|
293
|
+
inheritable_setting.namespace_stackable[method]
|
|
336
294
|
end
|
|
337
295
|
end
|
|
338
296
|
|
|
339
297
|
def validations
|
|
298
|
+
saved_validations = inheritable_setting.route[:saved_validations]
|
|
299
|
+
return if saved_validations.nil?
|
|
340
300
|
return enum_for(:validations) unless block_given?
|
|
341
301
|
|
|
342
|
-
|
|
302
|
+
saved_validations.each do |saved_validation|
|
|
343
303
|
yield Grape::Validations::ValidatorFactory.create_validator(saved_validation)
|
|
344
304
|
end
|
|
345
305
|
end
|
|
@@ -351,45 +311,44 @@ module Grape
|
|
|
351
311
|
|
|
352
312
|
private
|
|
353
313
|
|
|
354
|
-
def build_stack
|
|
314
|
+
def build_stack
|
|
355
315
|
stack = Grape::Middleware::Stack.new
|
|
356
316
|
|
|
357
|
-
content_types = namespace_stackable_with_hash(:content_types)
|
|
358
|
-
format = namespace_inheritable
|
|
317
|
+
content_types = inheritable_setting.namespace_stackable_with_hash(:content_types)
|
|
318
|
+
format = inheritable_setting.namespace_inheritable[:format]
|
|
359
319
|
|
|
360
320
|
stack.use Rack::Head
|
|
361
321
|
stack.use Rack::Lint if lint?
|
|
362
|
-
stack.use
|
|
363
|
-
helpers: helpers,
|
|
322
|
+
stack.use Grape::Middleware::Error,
|
|
364
323
|
format: format,
|
|
365
324
|
content_types: content_types,
|
|
366
|
-
default_status: namespace_inheritable
|
|
367
|
-
rescue_all: namespace_inheritable
|
|
368
|
-
rescue_grape_exceptions: namespace_inheritable
|
|
369
|
-
default_error_formatter: namespace_inheritable
|
|
370
|
-
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
|
371
|
-
rescue_options: namespace_stackable_with_hash(:rescue_options),
|
|
372
|
-
rescue_handlers:
|
|
373
|
-
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers),
|
|
374
|
-
all_rescue_handler: namespace_inheritable
|
|
375
|
-
grape_exceptions_rescue_handler: namespace_inheritable
|
|
376
|
-
|
|
377
|
-
stack.concat namespace_stackable
|
|
378
|
-
|
|
379
|
-
if namespace_inheritable
|
|
380
|
-
stack.use Grape::Middleware::Versioner.using(namespace_inheritable
|
|
381
|
-
versions: namespace_inheritable
|
|
382
|
-
version_options: namespace_inheritable
|
|
383
|
-
prefix: namespace_inheritable
|
|
384
|
-
mount_path: namespace_stackable
|
|
325
|
+
default_status: inheritable_setting.namespace_inheritable[:default_error_status],
|
|
326
|
+
rescue_all: inheritable_setting.namespace_inheritable[:rescue_all],
|
|
327
|
+
rescue_grape_exceptions: inheritable_setting.namespace_inheritable[:rescue_grape_exceptions],
|
|
328
|
+
default_error_formatter: inheritable_setting.namespace_inheritable[:default_error_formatter],
|
|
329
|
+
error_formatters: inheritable_setting.namespace_stackable_with_hash(:error_formatters),
|
|
330
|
+
rescue_options: inheritable_setting.namespace_stackable_with_hash(:rescue_options),
|
|
331
|
+
rescue_handlers: rescue_handlers,
|
|
332
|
+
base_only_rescue_handlers: inheritable_setting.namespace_stackable_with_hash(:base_only_rescue_handlers),
|
|
333
|
+
all_rescue_handler: inheritable_setting.namespace_inheritable[:all_rescue_handler],
|
|
334
|
+
grape_exceptions_rescue_handler: inheritable_setting.namespace_inheritable[:grape_exceptions_rescue_handler]
|
|
335
|
+
|
|
336
|
+
stack.concat inheritable_setting.namespace_stackable[:middleware]
|
|
337
|
+
|
|
338
|
+
if inheritable_setting.namespace_inheritable[:version].present?
|
|
339
|
+
stack.use Grape::Middleware::Versioner.using(inheritable_setting.namespace_inheritable[:version_options][:using]),
|
|
340
|
+
versions: inheritable_setting.namespace_inheritable[:version].flatten,
|
|
341
|
+
version_options: inheritable_setting.namespace_inheritable[:version_options],
|
|
342
|
+
prefix: inheritable_setting.namespace_inheritable[:root_prefix],
|
|
343
|
+
mount_path: inheritable_setting.namespace_stackable[:mount_path].first
|
|
385
344
|
end
|
|
386
345
|
|
|
387
346
|
stack.use Grape::Middleware::Formatter,
|
|
388
347
|
format: format,
|
|
389
|
-
default_format: namespace_inheritable
|
|
348
|
+
default_format: inheritable_setting.namespace_inheritable[:default_format] || :txt,
|
|
390
349
|
content_types: content_types,
|
|
391
|
-
formatters: namespace_stackable_with_hash(:formatters),
|
|
392
|
-
parsers: namespace_stackable_with_hash(:parsers)
|
|
350
|
+
formatters: inheritable_setting.namespace_stackable_with_hash(:formatters),
|
|
351
|
+
parsers: inheritable_setting.namespace_stackable_with_hash(:parsers)
|
|
393
352
|
|
|
394
353
|
builder = stack.build
|
|
395
354
|
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
|
@@ -397,7 +356,7 @@ module Grape
|
|
|
397
356
|
end
|
|
398
357
|
|
|
399
358
|
def build_helpers
|
|
400
|
-
helpers = namespace_stackable
|
|
359
|
+
helpers = inheritable_setting.namespace_stackable[:helpers]
|
|
401
360
|
return if helpers.empty?
|
|
402
361
|
|
|
403
362
|
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
|
@@ -411,7 +370,16 @@ module Grape
|
|
|
411
370
|
end
|
|
412
371
|
|
|
413
372
|
def lint?
|
|
414
|
-
namespace_inheritable
|
|
373
|
+
inheritable_setting.namespace_inheritable[:lint] || Grape.config.lint
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def rescue_handlers
|
|
377
|
+
rescue_handlers = inheritable_setting.namespace_reverse_stackable[:rescue_handlers]
|
|
378
|
+
return if rescue_handlers.blank?
|
|
379
|
+
|
|
380
|
+
rescue_handlers.each_with_object({}) do |rescue_handler, result|
|
|
381
|
+
result.merge!(rescue_handler) { |_k, s1, _s2| s1 }
|
|
382
|
+
end
|
|
415
383
|
end
|
|
416
384
|
end
|
|
417
385
|
end
|
|
@@ -5,12 +5,11 @@ module Grape
|
|
|
5
5
|
module Auth
|
|
6
6
|
module DSL
|
|
7
7
|
def auth(type = nil, options = {}, &block)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
end
|
|
8
|
+
namespace_inheritable = inheritable_setting.namespace_inheritable
|
|
9
|
+
return namespace_inheritable[:auth] unless type
|
|
10
|
+
|
|
11
|
+
namespace_inheritable[:auth] = options.reverse_merge(type: type.to_sym, proc: block)
|
|
12
|
+
use Grape::Middleware::Auth::Base, namespace_inheritable[:auth]
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
# Add HTTP Basic authorization to the API.
|
|
@@ -16,11 +16,6 @@ module Grape
|
|
|
16
16
|
}.freeze
|
|
17
17
|
}.freeze
|
|
18
18
|
|
|
19
|
-
def initialize(app, **options)
|
|
20
|
-
super
|
|
21
|
-
self.class.include(options[:helpers]) if options[:helpers]
|
|
22
|
-
end
|
|
23
|
-
|
|
24
19
|
def call!(env)
|
|
25
20
|
@env = env
|
|
26
21
|
error_response(catch(:error) { return @app.call(@env) })
|
|
@@ -103,12 +98,7 @@ module Grape
|
|
|
103
98
|
end
|
|
104
99
|
|
|
105
100
|
def run_rescue_handler(handler, error, endpoint)
|
|
106
|
-
if handler.instance_of?(Symbol)
|
|
107
|
-
raise NoMethodError, "undefined method '#{handler}'" unless respond_to?(handler)
|
|
108
|
-
|
|
109
|
-
handler = public_method(handler)
|
|
110
|
-
end
|
|
111
|
-
|
|
101
|
+
handler = endpoint.public_method(handler) if handler.instance_of?(Symbol)
|
|
112
102
|
response = catch(:error) do
|
|
113
103
|
handler.arity.zero? ? endpoint.instance_exec(&handler) : endpoint.instance_exec(error, &handler)
|
|
114
104
|
end
|
|
@@ -7,6 +7,8 @@ module Grape
|
|
|
7
7
|
default_format: :txt
|
|
8
8
|
}.freeze
|
|
9
9
|
|
|
10
|
+
ALL_MEDIA_TYPES = '*/*'
|
|
11
|
+
|
|
10
12
|
def before
|
|
11
13
|
negotiate_content_type
|
|
12
14
|
read_body_input
|
|
@@ -132,13 +134,13 @@ module Grape
|
|
|
132
134
|
dot_pos = request_path.rindex('.')
|
|
133
135
|
return unless dot_pos
|
|
134
136
|
|
|
135
|
-
extension = request_path[dot_pos + 1..]
|
|
137
|
+
extension = request_path[(dot_pos + 1)..]
|
|
136
138
|
extension if content_type_for(extension)
|
|
137
139
|
end
|
|
138
140
|
|
|
139
141
|
def format_from_header
|
|
140
142
|
accept_header = env['HTTP_ACCEPT'].try(:scrub)
|
|
141
|
-
return if accept_header.blank?
|
|
143
|
+
return if accept_header.blank? || accept_header == ALL_MEDIA_TYPES
|
|
142
144
|
|
|
143
145
|
media_type = Rack::Utils.best_q_match(accept_header, mime_types.keys)
|
|
144
146
|
mime_types[media_type] if media_type
|
|
@@ -6,6 +6,7 @@ module Grape
|
|
|
6
6
|
# It allows to insert and insert after
|
|
7
7
|
class Stack
|
|
8
8
|
extend Forwardable
|
|
9
|
+
|
|
9
10
|
class Middleware
|
|
10
11
|
attr_reader :args, :block, :klass
|
|
11
12
|
|
|
@@ -59,9 +60,9 @@ module Grape
|
|
|
59
60
|
|
|
60
61
|
alias insert_before insert
|
|
61
62
|
|
|
62
|
-
def insert_after(index,
|
|
63
|
+
def insert_after(index, ...)
|
|
63
64
|
index = assert_index(index, :after)
|
|
64
|
-
insert(index + 1,
|
|
65
|
+
insert(index + 1, ...)
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
def use(klass, *args, &block)
|
|
@@ -19,7 +19,7 @@ module Grape
|
|
|
19
19
|
class AcceptVersionHeader < Base
|
|
20
20
|
def before
|
|
21
21
|
potential_version = env['HTTP_ACCEPT_VERSION'].try(:scrub)
|
|
22
|
-
not_acceptable!('Accept-Version header must be set.') if strict
|
|
22
|
+
not_acceptable!('Accept-Version header must be set.') if strict && potential_version.blank?
|
|
23
23
|
|
|
24
24
|
return if potential_version.blank?
|
|
25
25
|
|
|
@@ -5,68 +5,50 @@ module Grape
|
|
|
5
5
|
module Versioner
|
|
6
6
|
class Base < Grape::Middleware::Base
|
|
7
7
|
DEFAULT_OPTIONS = {
|
|
8
|
-
pattern: /.*/i
|
|
8
|
+
pattern: /.*/i,
|
|
9
|
+
prefix: nil,
|
|
10
|
+
mount_path: nil,
|
|
9
11
|
version_options: {
|
|
10
12
|
strict: false,
|
|
11
13
|
cascade: true,
|
|
12
|
-
parameter: 'apiver'
|
|
14
|
+
parameter: 'apiver',
|
|
15
|
+
vendor: nil
|
|
13
16
|
}.freeze
|
|
14
17
|
}.freeze
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
super
|
|
18
|
-
Versioner.register(klass)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def versions
|
|
22
|
-
options[:versions]
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def prefix
|
|
26
|
-
options[:prefix]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def mount_path
|
|
30
|
-
options[:mount_path]
|
|
31
|
-
end
|
|
19
|
+
CASCADE_PASS_HEADER = { 'X-Cascade' => 'pass' }.freeze
|
|
32
20
|
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
DEFAULT_OPTIONS.each_key do |key|
|
|
22
|
+
define_method key do
|
|
23
|
+
options[key]
|
|
24
|
+
end
|
|
35
25
|
end
|
|
36
26
|
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
DEFAULT_OPTIONS[:version_options].each_key do |key|
|
|
28
|
+
define_method key do
|
|
29
|
+
options[:version_options][key]
|
|
30
|
+
end
|
|
39
31
|
end
|
|
40
32
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking
|
|
46
|
-
# of routes (see Grape::Router) for more information). To prevent
|
|
47
|
-
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
|
48
|
-
def cascade?
|
|
49
|
-
version_options[:cascade]
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def parameter_key
|
|
53
|
-
version_options[:parameter]
|
|
33
|
+
def self.inherited(klass)
|
|
34
|
+
super
|
|
35
|
+
Versioner.register(klass)
|
|
54
36
|
end
|
|
55
37
|
|
|
56
|
-
|
|
57
|
-
version_options[:vendor]
|
|
58
|
-
end
|
|
38
|
+
attr_reader :error_headers, :versions
|
|
59
39
|
|
|
60
|
-
def
|
|
61
|
-
|
|
40
|
+
def initialize(app, **options)
|
|
41
|
+
super
|
|
42
|
+
@error_headers = cascade ? CASCADE_PASS_HEADER : {}
|
|
43
|
+
@versions = options[:versions]&.map(&:to_s) # making sure versions are strings to ease potential match
|
|
62
44
|
end
|
|
63
45
|
|
|
64
46
|
def potential_version_match?(potential_version)
|
|
65
|
-
versions.blank? || versions.
|
|
47
|
+
versions.blank? || versions.include?(potential_version)
|
|
66
48
|
end
|
|
67
49
|
|
|
68
50
|
def version_not_found!
|
|
69
|
-
throw :error, status: 404, message: '404 API Version Not Found', headers:
|
|
51
|
+
throw :error, status: 404, message: '404 API Version Not Found', headers: CASCADE_PASS_HEADER
|
|
70
52
|
end
|
|
71
53
|
end
|
|
72
54
|
end
|
|
@@ -20,11 +20,11 @@ module Grape
|
|
|
20
20
|
# env['api.version'] => 'v1'
|
|
21
21
|
class Param < Base
|
|
22
22
|
def before
|
|
23
|
-
potential_version = query_params[
|
|
23
|
+
potential_version = query_params[parameter]
|
|
24
24
|
return if potential_version.blank?
|
|
25
25
|
|
|
26
26
|
version_not_found! unless potential_version_match?(potential_version)
|
|
27
|
-
env[Grape::Env::API_VERSION] = env[Rack::RACK_REQUEST_QUERY_HASH].delete(
|
|
27
|
+
env[Grape::Env::API_VERSION] = env[Rack::RACK_REQUEST_QUERY_HASH].delete(parameter)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|