grape 1.3.0 → 1.5.2
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 +119 -1
- data/LICENSE +1 -1
- data/README.md +123 -29
- data/UPGRADING.md +265 -39
- data/lib/grape/api/instance.rb +32 -31
- data/lib/grape/api.rb +5 -5
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/helpers.rb +2 -1
- data/lib/grape/dsl/inside_route.rb +77 -43
- data/lib/grape/dsl/parameters.rb +12 -8
- data/lib/grape/dsl/routing.rb +12 -11
- data/lib/grape/dsl/validations.rb +18 -1
- data/lib/grape/eager_load.rb +1 -1
- data/lib/grape/endpoint.rb +8 -6
- data/lib/grape/exceptions/base.rb +0 -4
- data/lib/grape/exceptions/validation.rb +1 -1
- data/lib/grape/exceptions/validation_errors.rb +12 -13
- data/lib/grape/http/headers.rb +26 -0
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/base.rb +4 -5
- data/lib/grape/middleware/error.rb +11 -13
- data/lib/grape/middleware/formatter.rb +3 -3
- data/lib/grape/middleware/stack.rb +10 -2
- data/lib/grape/middleware/versioner/header.rb +4 -4
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +12 -2
- data/lib/grape/path.rb +13 -3
- data/lib/grape/request.rb +13 -8
- data/lib/grape/router/attribute_translator.rb +26 -5
- data/lib/grape/router/pattern.rb +17 -16
- data/lib/grape/router/route.rb +5 -24
- data/lib/grape/router.rb +26 -30
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
- data/lib/grape/util/base_inheritable.rb +15 -8
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +1 -0
- data/lib/grape/util/reverse_stackable_values.rb +2 -0
- data/lib/grape/util/stackable_values.rb +7 -20
- data/lib/grape/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +10 -8
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types/array_coercer.rb +14 -5
- data/lib/grape/validations/types/build_coercer.rb +5 -8
- data/lib/grape/validations/types/custom_type_coercer.rb +16 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +36 -1
- data/lib/grape/validations/types/file.rb +15 -12
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +40 -36
- data/lib/grape/validations/types/primitive_coercer.rb +15 -6
- data/lib/grape/validations/types/set_coercer.rb +6 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +1 -1
- data/lib/grape/validations/types.rb +7 -9
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/as.rb +1 -1
- data/lib/grape/validations/validators/base.rb +8 -8
- data/lib/grape/validations/validators/coerce.rb +11 -15
- data/lib/grape/validations/validators/default.rb +3 -5
- data/lib/grape/validations/validators/exactly_one_of.rb +4 -2
- data/lib/grape/validations/validators/except_values.rb +1 -1
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/validations/validators/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +5 -5
- data/spec/grape/api/instance_spec.rb +50 -0
- data/spec/grape/api_remount_spec.rb +9 -4
- data/spec/grape/api_spec.rb +82 -6
- data/spec/grape/dsl/inside_route_spec.rb +182 -33
- data/spec/grape/endpoint/declared_spec.rb +601 -0
- data/spec/grape/endpoint_spec.rb +0 -521
- data/spec/grape/entity_spec.rb +7 -1
- data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
- data/spec/grape/integration/rack_sendfile_spec.rb +12 -8
- data/spec/grape/middleware/auth/strategies_spec.rb +1 -1
- data/spec/grape/middleware/error_spec.rb +1 -1
- data/spec/grape/middleware/formatter_spec.rb +3 -3
- data/spec/grape/middleware/stack_spec.rb +10 -0
- data/spec/grape/path_spec.rb +4 -4
- data/spec/grape/request_spec.rb +1 -1
- data/spec/grape/validations/instance_behaivour_spec.rb +1 -1
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
- data/spec/grape/validations/params_scope_spec.rb +26 -0
- data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +1 -1
- data/spec/grape/validations/validators/coerce_spec.rb +366 -86
- data/spec/grape/validations/validators/default_spec.rb +170 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +12 -12
- data/spec/grape/validations/validators/except_values_spec.rb +1 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +298 -30
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/shared/versioning_examples.rb +20 -20
- data/spec/spec_helper.rb +3 -10
- data/spec/support/chunks.rb +14 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/versioned_helpers.rb +4 -6
- metadata +27 -10
- data/lib/grape/util/content_types.rb +0 -28
@@ -28,36 +28,38 @@ module Grape
|
|
28
28
|
# Methods which should not be available in filters until the before filter
|
29
29
|
# has completed
|
30
30
|
module PostBeforeFilter
|
31
|
-
def declared(passed_params, options = {}, declared_params = nil)
|
31
|
+
def declared(passed_params, options = {}, declared_params = nil, params_nested_path = [])
|
32
32
|
options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
|
33
33
|
declared_params ||= optioned_declared_params(**options)
|
34
34
|
|
35
35
|
if passed_params.is_a?(Array)
|
36
|
-
declared_array(passed_params, options, declared_params)
|
36
|
+
declared_array(passed_params, options, declared_params, params_nested_path)
|
37
37
|
else
|
38
|
-
declared_hash(passed_params, options, declared_params)
|
38
|
+
declared_hash(passed_params, options, declared_params, params_nested_path)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
43
43
|
|
44
|
-
def declared_array(passed_params, options, declared_params)
|
44
|
+
def declared_array(passed_params, options, declared_params, params_nested_path)
|
45
45
|
passed_params.map do |passed_param|
|
46
|
-
declared(passed_param || {}, options, declared_params)
|
46
|
+
declared(passed_param || {}, options, declared_params, params_nested_path)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
def declared_hash(passed_params, options, declared_params)
|
50
|
+
def declared_hash(passed_params, options, declared_params, params_nested_path)
|
51
51
|
declared_params.each_with_object(passed_params.class.new) do |declared_param, memo|
|
52
52
|
if declared_param.is_a?(Hash)
|
53
53
|
declared_param.each_pair do |declared_parent_param, declared_children_params|
|
54
|
+
params_nested_path_dup = params_nested_path.dup
|
55
|
+
params_nested_path_dup << declared_parent_param.to_s
|
54
56
|
next unless options[:include_missing] || passed_params.key?(declared_parent_param)
|
55
57
|
|
56
58
|
passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
|
57
59
|
memo_key = optioned_param_key(declared_parent_param, options)
|
58
60
|
|
59
|
-
memo[memo_key] = handle_passed_param(
|
60
|
-
declared(passed_children_params, options, declared_children_params)
|
61
|
+
memo[memo_key] = handle_passed_param(params_nested_path_dup, passed_children_params.any?) do
|
62
|
+
declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
|
61
63
|
end
|
62
64
|
end
|
63
65
|
else
|
@@ -68,30 +70,37 @@ module Grape
|
|
68
70
|
|
69
71
|
next unless options[:include_missing] || passed_params.key?(declared_param) || (param_renaming && passed_params.key?(param_renaming))
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
73
|
+
memo_key = optioned_param_key(param_renaming || declared_param, options)
|
74
|
+
passed_param = passed_params[param_renaming || declared_param]
|
75
|
+
|
76
|
+
params_nested_path_dup = params_nested_path.dup
|
77
|
+
params_nested_path_dup << declared_param.to_s
|
78
|
+
memo[memo_key] = passed_param || handle_passed_param(params_nested_path_dup) do
|
79
|
+
passed_param
|
75
80
|
end
|
76
81
|
end
|
77
82
|
end
|
78
83
|
end
|
79
84
|
|
80
|
-
def handle_passed_param(
|
81
|
-
|
82
|
-
end
|
85
|
+
def handle_passed_param(params_nested_path, has_passed_children = false, &_block)
|
86
|
+
return yield if has_passed_children
|
83
87
|
|
84
|
-
|
85
|
-
|
86
|
-
end
|
88
|
+
key = params_nested_path[0]
|
89
|
+
key += '[' + params_nested_path[1..-1].join('][') + ']' if params_nested_path.size > 1
|
87
90
|
|
88
|
-
|
89
|
-
|
90
|
-
route_options_params
|
91
|
-
end
|
91
|
+
route_options_params = options[:route_options][:params] || {}
|
92
|
+
type = route_options_params.dig(key, :type)
|
93
|
+
has_children = route_options_params.keys.any? { |k| k != key && k.start_with?(key) }
|
92
94
|
|
93
|
-
|
94
|
-
|
95
|
+
if type == 'Hash' && !has_children
|
96
|
+
{}
|
97
|
+
elsif type == 'Array' || type&.start_with?('[') && !type&.include?(',')
|
98
|
+
[]
|
99
|
+
elsif type == 'Set' || type&.start_with?('#<Set')
|
100
|
+
Set.new
|
101
|
+
else
|
102
|
+
yield
|
103
|
+
end
|
95
104
|
end
|
96
105
|
|
97
106
|
def optioned_param_key(declared_param, options)
|
@@ -101,10 +110,10 @@ module Grape
|
|
101
110
|
def optioned_declared_params(**options)
|
102
111
|
declared_params = if options[:include_parent_namespaces]
|
103
112
|
# Declared params including parent namespaces
|
104
|
-
route_setting(:
|
113
|
+
route_setting(:declared_params)
|
105
114
|
else
|
106
115
|
# Declared params at current namespace
|
107
|
-
|
116
|
+
namespace_stackable(:declared_params).last || []
|
108
117
|
end
|
109
118
|
|
110
119
|
raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
|
@@ -177,17 +186,17 @@ module Grape
|
|
177
186
|
def status(status = nil)
|
178
187
|
case status
|
179
188
|
when Symbol
|
180
|
-
raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.
|
189
|
+
raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(status)
|
181
190
|
@status = Rack::Utils.status_code(status)
|
182
191
|
when Integer
|
183
192
|
@status = status
|
184
193
|
when nil
|
185
|
-
return @status if @status
|
194
|
+
return @status if instance_variable_defined?(:@status) && @status
|
186
195
|
case request.request_method.to_s.upcase
|
187
196
|
when Grape::Http::Headers::POST
|
188
197
|
201
|
189
198
|
when Grape::Http::Headers::DELETE
|
190
|
-
if @body.present?
|
199
|
+
if instance_variable_defined?(:@body) && @body.present?
|
191
200
|
200
|
192
201
|
else
|
193
202
|
204
|
@@ -238,7 +247,7 @@ module Grape
|
|
238
247
|
@body = ''
|
239
248
|
status 204
|
240
249
|
else
|
241
|
-
@body
|
250
|
+
instance_variable_defined?(:@body) ? @body : nil
|
242
251
|
end
|
243
252
|
end
|
244
253
|
|
@@ -256,23 +265,36 @@ module Grape
|
|
256
265
|
body false
|
257
266
|
end
|
258
267
|
|
259
|
-
#
|
268
|
+
# Deprecated method to send files to the client. Use `sendfile` or `stream`
|
269
|
+
def file(value = nil)
|
270
|
+
if value.is_a?(String)
|
271
|
+
warn '[DEPRECATION] Use sendfile or stream to send files.'
|
272
|
+
sendfile(value)
|
273
|
+
elsif !value.is_a?(NilClass)
|
274
|
+
warn '[DEPRECATION] Use stream to use a Stream object.'
|
275
|
+
stream(value)
|
276
|
+
else
|
277
|
+
warn '[DEPRECATION] Use sendfile or stream to send files.'
|
278
|
+
sendfile
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Allows you to send a file to the client via sendfile.
|
260
283
|
#
|
261
284
|
# @example
|
262
285
|
# get '/file' do
|
263
|
-
#
|
286
|
+
# sendfile FileStreamer.new(...)
|
264
287
|
# end
|
265
288
|
#
|
266
289
|
# GET /file # => "contents of file"
|
267
|
-
def
|
290
|
+
def sendfile(value = nil)
|
268
291
|
if value.is_a?(String)
|
269
|
-
file_body = Grape::
|
270
|
-
@
|
292
|
+
file_body = Grape::ServeStream::FileBody.new(value)
|
293
|
+
@stream = Grape::ServeStream::StreamResponse.new(file_body)
|
271
294
|
elsif !value.is_a?(NilClass)
|
272
|
-
|
273
|
-
@file = Grape::ServeFile::FileResponse.new(value)
|
295
|
+
raise ArgumentError, 'Argument must be a file path'
|
274
296
|
else
|
275
|
-
|
297
|
+
stream
|
276
298
|
end
|
277
299
|
end
|
278
300
|
|
@@ -292,10 +314,21 @@ module Grape
|
|
292
314
|
# * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/chunked.rb
|
293
315
|
# * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/etag.rb
|
294
316
|
def stream(value = nil)
|
317
|
+
return if value.nil? && @stream.nil?
|
318
|
+
|
295
319
|
header 'Content-Length', nil
|
296
320
|
header 'Transfer-Encoding', nil
|
297
321
|
header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front)
|
298
|
-
|
322
|
+
if value.is_a?(String)
|
323
|
+
file_body = Grape::ServeStream::FileBody.new(value)
|
324
|
+
@stream = Grape::ServeStream::StreamResponse.new(file_body)
|
325
|
+
elsif value.respond_to?(:each)
|
326
|
+
@stream = Grape::ServeStream::StreamResponse.new(value)
|
327
|
+
elsif !value.is_a?(NilClass)
|
328
|
+
raise ArgumentError, 'Stream object must respond to :each.'
|
329
|
+
else
|
330
|
+
@stream
|
331
|
+
end
|
299
332
|
end
|
300
333
|
|
301
334
|
# Allows you to make use of Grape Entities by setting
|
@@ -331,11 +364,12 @@ module Grape
|
|
331
364
|
end
|
332
365
|
|
333
366
|
representation = { root => representation } if root
|
367
|
+
|
334
368
|
if key
|
335
|
-
representation = (
|
336
|
-
elsif entity_class.present? &&
|
369
|
+
representation = (body || {}).merge(key => representation)
|
370
|
+
elsif entity_class.present? && body
|
337
371
|
raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
|
338
|
-
representation =
|
372
|
+
representation = body.merge(representation)
|
339
373
|
end
|
340
374
|
|
341
375
|
body representation
|
@@ -365,7 +399,7 @@ module Grape
|
|
365
399
|
entity_class = options.delete(:with)
|
366
400
|
|
367
401
|
if entity_class.nil?
|
368
|
-
# entity class not
|
402
|
+
# entity class not explicitly defined, auto-detect from relation#klass or first object in the collection
|
369
403
|
object_class = if object.respond_to?(:klass)
|
370
404
|
object.klass
|
371
405
|
else
|
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -72,7 +72,7 @@ module Grape
|
|
72
72
|
|
73
73
|
# Require one or more parameters for the current endpoint.
|
74
74
|
#
|
75
|
-
# @param attrs list of
|
75
|
+
# @param attrs list of parameters names, or, if :using is
|
76
76
|
# passed as an option, which keys to include (:all or :none) from
|
77
77
|
# the :using hash. The last key can be a hash, which specifies
|
78
78
|
# options for the parameters
|
@@ -127,7 +127,7 @@ module Grape
|
|
127
127
|
|
128
128
|
opts = attrs.extract_options!.clone
|
129
129
|
opts[:presence] = { value: true, message: opts[:message] }
|
130
|
-
opts = @group.merge(opts) if @group
|
130
|
+
opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
|
131
131
|
|
132
132
|
if opts[:using]
|
133
133
|
require_required_and_optional_fields(attrs.first, opts)
|
@@ -146,7 +146,7 @@ module Grape
|
|
146
146
|
|
147
147
|
opts = attrs.extract_options!.clone
|
148
148
|
type = opts[:type]
|
149
|
-
opts = @group.merge(opts) if @group
|
149
|
+
opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
|
150
150
|
|
151
151
|
# check type for optional parameter group
|
152
152
|
if attrs && block_given?
|
@@ -227,13 +227,17 @@ module Grape
|
|
227
227
|
|
228
228
|
alias group requires
|
229
229
|
|
230
|
-
|
230
|
+
class EmptyOptionalValue; end
|
231
|
+
|
232
|
+
def map_params(params, element, is_array = false)
|
231
233
|
if params.is_a?(Array)
|
232
234
|
params.map do |el|
|
233
|
-
map_params(el, element)
|
235
|
+
map_params(el, element, true)
|
234
236
|
end
|
235
237
|
elsif params.is_a?(Hash)
|
236
|
-
params[element] || {}
|
238
|
+
params[element] || (@optional && is_array ? EmptyOptionalValue : {})
|
239
|
+
elsif params == EmptyOptionalValue
|
240
|
+
EmptyOptionalValue
|
237
241
|
else
|
238
242
|
{}
|
239
243
|
end
|
@@ -243,8 +247,8 @@ module Grape
|
|
243
247
|
# @return hash of parameters relevant for the current scope
|
244
248
|
# @api private
|
245
249
|
def params(params)
|
246
|
-
params = @parent.params(params) if @parent
|
247
|
-
params = map_params(params, @element) if @element
|
250
|
+
params = @parent.params(params) if instance_variable_defined?(:@parent) && @parent
|
251
|
+
params = map_params(params, @element) if instance_variable_defined?(:@element) && @element
|
248
252
|
params
|
249
253
|
end
|
250
254
|
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -51,7 +51,7 @@ module Grape
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
@versions.last
|
54
|
+
@versions.last if instance_variable_defined?(:@versions) && @versions
|
55
55
|
end
|
56
56
|
|
57
57
|
# Define a root URL prefix for your entire API.
|
@@ -79,11 +79,12 @@ module Grape
|
|
79
79
|
namespace_inheritable(:do_not_route_options, true)
|
80
80
|
end
|
81
81
|
|
82
|
-
def mount(mounts, opts
|
82
|
+
def mount(mounts, *opts)
|
83
83
|
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
84
84
|
mounts.each_pair do |app, path|
|
85
85
|
if app.respond_to?(:mount_instance)
|
86
|
-
|
86
|
+
opts_with = opts.any? ? opts.shift[:with] : {}
|
87
|
+
mount({ app.mount_instance(configuration: opts_with) => path })
|
87
88
|
next
|
88
89
|
end
|
89
90
|
in_setting = inheritable_setting
|
@@ -142,16 +143,16 @@ module Grape
|
|
142
143
|
reset_validations!
|
143
144
|
end
|
144
145
|
|
145
|
-
|
146
|
-
define_method
|
146
|
+
Grape::Http::Headers::SUPPORTED_METHODS.each do |supported_method|
|
147
|
+
define_method supported_method.downcase do |*args, &block|
|
147
148
|
options = args.extract_options!
|
148
149
|
paths = args.first || ['/']
|
149
|
-
route(
|
150
|
+
route(supported_method, paths, options, &block)
|
150
151
|
end
|
151
152
|
end
|
152
153
|
|
153
154
|
# Declare a "namespace", which prefixes all subordinate routes with its
|
154
|
-
# name. Any endpoints within a namespace,
|
155
|
+
# name. Any endpoints within a namespace, group, resource or segment,
|
155
156
|
# etc., will share their parent context as well as any configuration
|
156
157
|
# done in the namespace context.
|
157
158
|
#
|
@@ -163,14 +164,14 @@ module Grape
|
|
163
164
|
# end
|
164
165
|
# end
|
165
166
|
def namespace(space = nil, options = {}, &block)
|
167
|
+
@namespace_description = nil unless instance_variable_defined?(:@namespace_description) && @namespace_description
|
168
|
+
|
166
169
|
if space || block_given?
|
167
170
|
within_namespace do
|
168
171
|
previous_namespace_description = @namespace_description
|
169
172
|
@namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
|
170
173
|
nest(block) do
|
171
|
-
if space
|
172
|
-
namespace_stackable(:namespace, Namespace.new(space, **options))
|
173
|
-
end
|
174
|
+
namespace_stackable(:namespace, Namespace.new(space, **options)) if space
|
174
175
|
end
|
175
176
|
@namespace_description = previous_namespace_description
|
176
177
|
end
|
@@ -199,7 +200,7 @@ module Grape
|
|
199
200
|
@endpoints = []
|
200
201
|
end
|
201
202
|
|
202
|
-
#
|
203
|
+
# This method allows you to quickly define a parameter route segment
|
203
204
|
# in your API.
|
204
205
|
#
|
205
206
|
# @param param [Symbol] The name of the parameter you wish to declare.
|
@@ -10,7 +10,24 @@ module Grape
|
|
10
10
|
include Grape::DSL::Configuration
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
-
# Clears all defined parameters and validations.
|
13
|
+
# Clears all defined parameters and validations. The main purpose of it is to clean up
|
14
|
+
# settings, so next endpoint won't interfere with previous one.
|
15
|
+
#
|
16
|
+
# params do
|
17
|
+
# # params for the endpoint below this block
|
18
|
+
# end
|
19
|
+
# post '/current' do
|
20
|
+
# # whatever
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # somewhere between them the reset_validations! method gets called
|
24
|
+
#
|
25
|
+
# params do
|
26
|
+
# # params for the endpoint below this block
|
27
|
+
# end
|
28
|
+
# post '/next' do
|
29
|
+
# # whatever
|
30
|
+
# end
|
14
31
|
def reset_validations!
|
15
32
|
unset_namespace_stackable :declared_params
|
16
33
|
unset_namespace_stackable :validations
|
data/lib/grape/eager_load.rb
CHANGED
data/lib/grape/endpoint.rb
CHANGED
@@ -80,7 +80,10 @@ module Grape
|
|
80
80
|
|
81
81
|
self.inheritable_setting = new_settings.point_in_time_copy
|
82
82
|
|
83
|
-
|
83
|
+
# now +namespace_stackable(:declared_params)+ contains all params defined for
|
84
|
+
# this endpoint and its parents, but later it will be cleaned up,
|
85
|
+
# see +reset_validations!+ in lib/grape/dsl/validations.rb
|
86
|
+
route_setting(:declared_params, namespace_stackable(:declared_params).flatten)
|
84
87
|
route_setting(:saved_validations, namespace_stackable(:validations))
|
85
88
|
|
86
89
|
namespace_stackable(:representations, []) unless namespace_stackable(:representations)
|
@@ -99,7 +102,7 @@ module Grape
|
|
99
102
|
@block = nil
|
100
103
|
|
101
104
|
@status = nil
|
102
|
-
@
|
105
|
+
@stream = nil
|
103
106
|
@body = nil
|
104
107
|
@proc = nil
|
105
108
|
|
@@ -116,7 +119,6 @@ module Grape
|
|
116
119
|
parent_declared_params = namespace_stackable[:declared_params]
|
117
120
|
|
118
121
|
if parent_declared_params
|
119
|
-
inheritable_setting.route[:declared_params] ||= []
|
120
122
|
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten)
|
121
123
|
end
|
122
124
|
|
@@ -190,7 +192,7 @@ module Grape
|
|
190
192
|
requirements: prepare_routes_requirements,
|
191
193
|
prefix: namespace_inheritable(:root_prefix),
|
192
194
|
anchor: options[:route_options].fetch(:anchor, true),
|
193
|
-
settings: inheritable_setting.route.except(:
|
195
|
+
settings: inheritable_setting.route.except(:declared_params, :saved_validations),
|
194
196
|
forward_match: options[:forward_match]
|
195
197
|
}
|
196
198
|
end
|
@@ -271,8 +273,8 @@ module Grape
|
|
271
273
|
# status verifies body presence when DELETE
|
272
274
|
@body ||= response_object
|
273
275
|
|
274
|
-
# The body commonly is an Array of Strings, the application instance itself, or a
|
275
|
-
response_object =
|
276
|
+
# The body commonly is an Array of Strings, the application instance itself, or a Stream-like object
|
277
|
+
response_object = stream || [body]
|
276
278
|
|
277
279
|
[status, header, response_object]
|
278
280
|
ensure
|
@@ -17,7 +17,7 @@ module Grape
|
|
17
17
|
super(**args)
|
18
18
|
end
|
19
19
|
|
20
|
-
#
|
20
|
+
# Remove all the unnecessary stuff from Grape::Exceptions::Base like status
|
21
21
|
# and headers when converting a validation error to json or string
|
22
22
|
def as_json(*_args)
|
23
23
|
to_s
|
@@ -5,6 +5,9 @@ require 'grape/exceptions/base'
|
|
5
5
|
module Grape
|
6
6
|
module Exceptions
|
7
7
|
class ValidationErrors < Grape::Exceptions::Base
|
8
|
+
ERRORS_FORMAT_KEY = 'grape.errors.format'
|
9
|
+
DEFAULT_ERRORS_FORMAT = '%{attributes} %{message}'
|
10
|
+
|
8
11
|
include Enumerable
|
9
12
|
|
10
13
|
attr_reader :errors
|
@@ -36,26 +39,22 @@ module Grape
|
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
39
|
-
def to_json(
|
42
|
+
def to_json(*_opts)
|
40
43
|
as_json.to_json
|
41
44
|
end
|
42
45
|
|
43
46
|
def full_messages
|
44
|
-
messages = map
|
47
|
+
messages = map do |attributes, error|
|
48
|
+
I18n.t(
|
49
|
+
ERRORS_FORMAT_KEY,
|
50
|
+
default: DEFAULT_ERRORS_FORMAT,
|
51
|
+
attributes: translate_attributes(attributes),
|
52
|
+
message: error.message
|
53
|
+
)
|
54
|
+
end
|
45
55
|
messages.uniq!
|
46
56
|
messages
|
47
57
|
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def full_message(attributes, error)
|
52
|
-
I18n.t(
|
53
|
-
'grape.errors.format',
|
54
|
-
default: '%{attributes} %{message}',
|
55
|
-
attributes: attributes.count == 1 ? translate_attribute(attributes.first) : translate_attributes(attributes),
|
56
|
-
message: error.message
|
57
|
-
)
|
58
|
-
end
|
59
58
|
end
|
60
59
|
end
|
61
60
|
end
|
data/lib/grape/http/headers.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'grape/util/lazy_object'
|
4
|
+
|
3
5
|
module Grape
|
4
6
|
module Http
|
5
7
|
module Headers
|
@@ -19,6 +21,7 @@ module Grape
|
|
19
21
|
OPTIONS = 'OPTIONS'
|
20
22
|
|
21
23
|
SUPPORTED_METHODS = [GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS].freeze
|
24
|
+
SUPPORTED_METHODS_WITHOUT_OPTIONS = Grape::Util::LazyObject.new { [GET, POST, PUT, PATCH, DELETE, HEAD].freeze }
|
22
25
|
|
23
26
|
HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'
|
24
27
|
X_CASCADE = 'X-Cascade'
|
@@ -27,6 +30,29 @@ module Grape
|
|
27
30
|
|
28
31
|
FORMAT = 'format'
|
29
32
|
|
33
|
+
HTTP_HEADERS = Grape::Util::LazyObject.new do
|
34
|
+
common_http_headers = %w[
|
35
|
+
Version
|
36
|
+
Host
|
37
|
+
Connection
|
38
|
+
Cache-Control
|
39
|
+
Dnt
|
40
|
+
Upgrade-Insecure-Requests
|
41
|
+
User-Agent
|
42
|
+
Sec-Fetch-Dest
|
43
|
+
Accept
|
44
|
+
Sec-Fetch-Site
|
45
|
+
Sec-Fetch-Mode
|
46
|
+
Sec-Fetch-User
|
47
|
+
Accept-Encoding
|
48
|
+
Accept-Language
|
49
|
+
Cookie
|
50
|
+
].freeze
|
51
|
+
common_http_headers.each_with_object({}) do |header, response|
|
52
|
+
response["HTTP_#{header.upcase.tr('-', '_')}"] = header
|
53
|
+
end.freeze
|
54
|
+
end
|
55
|
+
|
30
56
|
def self.find_supported_method(route_method)
|
31
57
|
Grape::Http::Headers::SUPPORTED_METHODS.detect { |supported_method| supported_method.casecmp(route_method).zero? }
|
32
58
|
end
|
@@ -10,9 +10,9 @@ module Grape
|
|
10
10
|
|
11
11
|
attr_accessor :options, :app, :env
|
12
12
|
|
13
|
-
def initialize(app,
|
13
|
+
def initialize(app, *options)
|
14
14
|
@app = app
|
15
|
-
@options = options
|
15
|
+
@options = options.shift
|
16
16
|
end
|
17
17
|
|
18
18
|
def call(env)
|
@@ -23,7 +23,7 @@ module Grape
|
|
23
23
|
self.env = env
|
24
24
|
|
25
25
|
if options.key?(:type)
|
26
|
-
auth_proc
|
26
|
+
auth_proc = options[:proc]
|
27
27
|
auth_proc_context = context
|
28
28
|
|
29
29
|
strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]
|
@@ -8,15 +8,16 @@ module Grape
|
|
8
8
|
include Helpers
|
9
9
|
|
10
10
|
attr_reader :app, :env, :options
|
11
|
+
|
11
12
|
TEXT_HTML = 'text/html'
|
12
13
|
|
13
14
|
include Grape::DSL::Headers
|
14
15
|
|
15
16
|
# @param [Rack Application] app The standard argument for a Rack middleware.
|
16
17
|
# @param [Hash] options A hash of options, simply stored for use by subclasses.
|
17
|
-
def initialize(app,
|
18
|
+
def initialize(app, *options)
|
18
19
|
@app = app
|
19
|
-
@options = default_options.merge(
|
20
|
+
@options = options.any? ? default_options.merge(options.shift) : default_options
|
20
21
|
@app_response = nil
|
21
22
|
end
|
22
23
|
|
@@ -74,11 +75,9 @@ module Grape
|
|
74
75
|
end
|
75
76
|
|
76
77
|
def mime_types
|
77
|
-
|
78
|
-
content_types.each_pair do |k, v|
|
78
|
+
@mime_type ||= content_types.each_pair.with_object({}) do |(k, v), types_without_params|
|
79
79
|
types_without_params[v.split(';').first] = k
|
80
80
|
end
|
81
|
-
types_without_params
|
82
81
|
end
|
83
82
|
|
84
83
|
private
|