grape 2.4.0 → 3.0.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 +40 -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 +49 -72
- data/lib/grape/api.rb +24 -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 +2 -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/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,11 @@ 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
|
-
@
|
|
78
|
+
@helpers = build_helpers
|
|
116
79
|
end
|
|
117
80
|
|
|
118
81
|
# Update our settings from a given set of stackable parameters. Used when
|
|
@@ -130,14 +93,6 @@ module Grape
|
|
|
130
93
|
raise Grape::Exceptions::MissingOption.new(key) unless options.key?(key)
|
|
131
94
|
end
|
|
132
95
|
|
|
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
96
|
def routes
|
|
142
97
|
@routes ||= endpoints&.collect(&:routes)&.flatten || to_routes
|
|
143
98
|
end
|
|
@@ -154,7 +109,7 @@ module Grape
|
|
|
154
109
|
reset_routes!
|
|
155
110
|
routes.each do |route|
|
|
156
111
|
router.append(route.apply(self))
|
|
157
|
-
next unless !namespace_inheritable
|
|
112
|
+
next unless !inheritable_setting.namespace_inheritable[:do_not_route_head] && route.request_method == Rack::GET
|
|
158
113
|
|
|
159
114
|
route.dup.then do |head_route|
|
|
160
115
|
head_route.convert_to_head_request!
|
|
@@ -175,7 +130,7 @@ module Grape
|
|
|
175
130
|
end
|
|
176
131
|
|
|
177
132
|
def prepare_routes_requirements
|
|
178
|
-
{}.merge!(*namespace_stackable
|
|
133
|
+
{}.merge!(*inheritable_setting.namespace_stackable[:namespace].map(&:requirements)).tap do |requirements|
|
|
179
134
|
endpoint_requirements = options.dig(:route_options, :requirements)
|
|
180
135
|
requirements.merge!(endpoint_requirements) if endpoint_requirements
|
|
181
136
|
end
|
|
@@ -186,7 +141,7 @@ module Grape
|
|
|
186
141
|
namespace: namespace,
|
|
187
142
|
version: prepare_version,
|
|
188
143
|
requirements: prepare_routes_requirements,
|
|
189
|
-
prefix: namespace_inheritable
|
|
144
|
+
prefix: inheritable_setting.namespace_inheritable[:root_prefix],
|
|
190
145
|
anchor: options[:route_options].fetch(:anchor, true),
|
|
191
146
|
settings: inheritable_setting.route.except(:declared_params, :saved_validations),
|
|
192
147
|
forward_match: options[:forward_match]
|
|
@@ -194,7 +149,7 @@ module Grape
|
|
|
194
149
|
end
|
|
195
150
|
|
|
196
151
|
def prepare_version
|
|
197
|
-
version = namespace_inheritable
|
|
152
|
+
version = inheritable_setting.namespace_inheritable[:version]
|
|
198
153
|
return if version.blank?
|
|
199
154
|
|
|
200
155
|
version.length == 1 ? version.first : version
|
|
@@ -211,7 +166,7 @@ module Grape
|
|
|
211
166
|
end
|
|
212
167
|
|
|
213
168
|
def namespace
|
|
214
|
-
@namespace ||= Namespace.joined_space_path(namespace_stackable
|
|
169
|
+
@namespace ||= Namespace.joined_space_path(inheritable_setting.namespace_stackable[:namespace])
|
|
215
170
|
end
|
|
216
171
|
|
|
217
172
|
def call(env)
|
|
@@ -222,6 +177,8 @@ module Grape
|
|
|
222
177
|
def call!(env)
|
|
223
178
|
env[Grape::Env::API_ENDPOINT] = self
|
|
224
179
|
@env = env
|
|
180
|
+
# this adds the helpers only to the instance
|
|
181
|
+
singleton_class.include(@helpers) if @helpers
|
|
225
182
|
@app.call(env)
|
|
226
183
|
end
|
|
227
184
|
|
|
@@ -248,7 +205,7 @@ module Grape
|
|
|
248
205
|
|
|
249
206
|
def run
|
|
250
207
|
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
|
|
251
|
-
@request = Grape::Request.new(env, build_params_with: namespace_inheritable
|
|
208
|
+
@request = Grape::Request.new(env, build_params_with: inheritable_setting.namespace_inheritable[:build_params_with])
|
|
252
209
|
begin
|
|
253
210
|
self.class.run_before_each(self)
|
|
254
211
|
run_filters befores, :before
|
|
@@ -284,11 +241,14 @@ module Grape
|
|
|
284
241
|
end
|
|
285
242
|
|
|
286
243
|
def execute
|
|
287
|
-
@
|
|
288
|
-
end
|
|
244
|
+
return unless @source
|
|
289
245
|
|
|
290
|
-
|
|
291
|
-
|
|
246
|
+
ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: self) do
|
|
247
|
+
instance_exec(&@source)
|
|
248
|
+
rescue LocalJumpError => e
|
|
249
|
+
Grape.deprecator.warn 'Using `return` in an endpoint has been deprecated. Use `next` instead.'
|
|
250
|
+
return e.exit_value
|
|
251
|
+
end
|
|
292
252
|
end
|
|
293
253
|
|
|
294
254
|
def lazy_initialize!
|
|
@@ -297,9 +257,7 @@ module Grape
|
|
|
297
257
|
@lazy_initialize_lock.synchronize do
|
|
298
258
|
return true if @lazy_initialized
|
|
299
259
|
|
|
300
|
-
@
|
|
301
|
-
@app = options[:app] || build_stack(@helpers)
|
|
302
|
-
|
|
260
|
+
@app = options[:app] || build_stack
|
|
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
|
|
@@ -59,9 +59,9 @@ module Grape
|
|
|
59
59
|
|
|
60
60
|
alias insert_before insert
|
|
61
61
|
|
|
62
|
-
def insert_after(index,
|
|
62
|
+
def insert_after(index, ...)
|
|
63
63
|
index = assert_index(index, :after)
|
|
64
|
-
insert(index + 1,
|
|
64
|
+
insert(index + 1, ...)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
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
|
|
@@ -28,7 +28,7 @@ module Grape
|
|
|
28
28
|
slash_position = path_info.index('/', 1) # omit the first one
|
|
29
29
|
return unless slash_position
|
|
30
30
|
|
|
31
|
-
potential_version = path_info[1..slash_position - 1]
|
|
31
|
+
potential_version = path_info[1..(slash_position - 1)]
|
|
32
32
|
return unless potential_version.match?(pattern)
|
|
33
33
|
|
|
34
34
|
version_not_found! unless potential_version_match?(potential_version)
|