grape 2.1.2 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +4 -3
- data/UPGRADING.md +8 -0
- data/grape.gemspec +2 -1
- data/lib/grape/content_types.rb +13 -8
- data/lib/grape/dsl/helpers.rb +7 -3
- data/lib/grape/dsl/inside_route.rb +10 -3
- data/lib/grape/dsl/request_response.rb +14 -18
- data/lib/grape/endpoint.rb +32 -21
- data/lib/grape/error_formatter/json.rb +13 -4
- data/lib/grape/error_formatter.rb +13 -25
- data/lib/grape/formatter.rb +15 -25
- data/lib/grape/locale/en.yml +1 -0
- data/lib/grape/middleware/base.rb +14 -13
- data/lib/grape/middleware/error.rb +11 -9
- data/lib/grape/middleware/formatter.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +8 -31
- data/lib/grape/middleware/versioner/header.rb +95 -10
- data/lib/grape/middleware/versioner/param.rb +5 -21
- data/lib/grape/middleware/versioner/path.rb +11 -31
- data/lib/grape/middleware/versioner.rb +5 -14
- data/lib/grape/middleware/versioner_helpers.rb +75 -0
- data/lib/grape/parser.rb +8 -24
- data/lib/grape/validations/params_scope.rb +7 -1
- data/lib/grape/validations/validators/length_validator.rb +10 -3
- data/lib/grape/version.rb +1 -1
- metadata +7 -7
- data/lib/grape/util/accept_header_handler.rb +0 -105
- data/lib/grape/util/registrable.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc04f424b8181e92cc304d02d2923952f07e2532ef0291233596813726a2cb68
|
4
|
+
data.tar.gz: 46729a20982fc16129540a81061bea229fd3ba5dd81426c1830119b52fe6ccd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45e47b5059a37bbf75331c41bb1fdda2487610e3713baf5b8181dcbbae1727c47995503f5dc8a12375fd0765908140540100579afe834dfe96dc4c07cd771b2f
|
7
|
+
data.tar.gz: 9692f64fc61714c33035ef59c0d68a9c34fb3725a5842e0fe16cbd4d7025265bef50ea5086a3f61d5ed1f0fd8740435619ff616eed55e45fd3cc48e090a02b7d
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
### 2.2.0 (2024-09-14)
|
2
|
+
|
3
|
+
#### Features
|
4
|
+
|
5
|
+
* [#2475](https://github.com/ruby-grape/grape/pull/2475): Remove Grape::Util::Registrable - [@ericproulx](https://github.com/ericproulx).
|
6
|
+
* [#2484](https://github.com/ruby-grape/grape/pull/2484): Refactor versioner middlewares - [@ericproulx](https://github.com/ericproulx).
|
7
|
+
* [#2489](https://github.com/ruby-grape/grape/pull/2489): Add Rails 7.2 in CI workflow - [@ericproulx](https://github.com/ericproulx).
|
8
|
+
* [#2493](https://github.com/ruby-grape/grape/pull/2493): MFA required when releasing - [@ericproulx](https://github.com/ericproulx).
|
9
|
+
|
10
|
+
#### Fixes
|
11
|
+
|
12
|
+
* [#2471](https://github.com/ruby-grape/grape/pull/2471): Fix absence of original_exception and/or backtrace even if passed in error! - [@numbata](https://github.com/numbata).
|
13
|
+
* [#2478](https://github.com/ruby-grape/grape/pull/2478): Fix rescue_from with invalid response - [@ericproulx](https://github.com/ericproulx).
|
14
|
+
* [#2480](https://github.com/ruby-grape/grape/pull/2480): Fix rescue_from ValidationErrors exception - [@numbata](https://github.com/numbata).
|
15
|
+
* [#2464](https://github.com/ruby-grape/grape/pull/2464): The `length` validator only takes effect for parameters with types that support `#length` method - [@OuYangJinTing](https://github.com/OuYangJinTing).
|
16
|
+
* [#2485](https://github.com/ruby-grape/grape/pull/2485): Add `is:` param to length validator - [@dakad](https://github.com/dakad).
|
17
|
+
* [#2492](https://github.com/ruby-grape/grape/pull/2492): Fix `Grape::Endpoint#inspect` method - [@ericproulx](https://github.com/ericproulx).
|
18
|
+
* [#2496](https://github.com/ruby-grape/grape/pull/2496): Reduce object allocation when compiling - [@ericproulx](https://github.com/ericproulx).
|
19
|
+
|
20
|
+
### 2.1.3 (2024-07-13)
|
21
|
+
|
22
|
+
#### Fixes
|
23
|
+
|
24
|
+
* [#2467](https://github.com/ruby-grape/grape/pull/2467): Fix repo coverage - [@ericproulx](https://github.com/ericproulx).
|
25
|
+
* [#2468](https://github.com/ruby-grape/grape/pull/2468): Align `error!` method signatures across different places - [@numbata](https://github.com/numbata).
|
26
|
+
* [#2469](https://github.com/ruby-grape/grape/pull/2469): Fix full path building for lateral scopes - [@numbata](https://github.com/numbata).
|
27
|
+
|
1
28
|
### 2.1.2 (2024-06-28)
|
2
29
|
|
3
30
|
#### Fixes
|
data/README.md
CHANGED
@@ -157,8 +157,7 @@ Grape is a REST-like API framework for Ruby. It's designed to run on Rack or com
|
|
157
157
|
|
158
158
|
## Stable Release
|
159
159
|
|
160
|
-
You're reading the documentation for the stable release of Grape,
|
161
|
-
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
160
|
+
You're reading the documentation for the stable release of Grape, 2.2.0. Please read UPGRADING when upgrading from a previous version.
|
162
161
|
|
163
162
|
## Project Resources
|
164
163
|
|
@@ -1713,10 +1712,11 @@ end
|
|
1713
1712
|
|
1714
1713
|
Parameters with types that support `#length` method can be restricted to have a specific length with the `:length` option.
|
1715
1714
|
|
1716
|
-
The validator accepts `:min` or `:max` or both options to validate that the value of the parameter is within the given limits.
|
1715
|
+
The validator accepts `:min` or `:max` or both options or only `:is` to validate that the value of the parameter is within the given limits.
|
1717
1716
|
|
1718
1717
|
```ruby
|
1719
1718
|
params do
|
1719
|
+
requires :code, type: String, length: { is: 2 }
|
1720
1720
|
requires :str, type: String, length: { min: 3 }
|
1721
1721
|
requires :list, type: [Integer], length: { min: 3, max: 5 }
|
1722
1722
|
requires :hash, type: Hash, length: { max: 5 }
|
@@ -2044,6 +2044,7 @@ end
|
|
2044
2044
|
|
2045
2045
|
```ruby
|
2046
2046
|
params do
|
2047
|
+
requires :code, type: String, length: { is: 2, message: 'code is expected to be exactly 2 characters long' }
|
2047
2048
|
requires :str, type: String, length: { min: 5, message: 'str is expected to be atleast 5 characters long' }
|
2048
2049
|
requires :list, type: [Integer], length: { min: 2, max: 3, message: 'list is expected to have between 2 and 3 elements' }
|
2049
2050
|
end
|
data/UPGRADING.md
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
Upgrading Grape
|
2
2
|
===============
|
3
3
|
|
4
|
+
### Upgrading to >= 2.2.0
|
5
|
+
|
6
|
+
### `Length` validator
|
7
|
+
|
8
|
+
After Grape 2.2.0, `length` validator will only take effect for parameters with types that support `#length` method, will not throw `ArgumentError` exception.
|
9
|
+
|
10
|
+
See [#2464](https://github.com/ruby-grape/grape/pull/2464) for more information.
|
11
|
+
|
4
12
|
### Upgrading to >= 2.1.0
|
5
13
|
|
6
14
|
#### Optional Builder
|
data/grape.gemspec
CHANGED
@@ -17,7 +17,8 @@ Gem::Specification.new do |s|
|
|
17
17
|
'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
|
18
18
|
'changelog_uri' => "https://github.com/ruby-grape/grape/blob/v#{s.version}/CHANGELOG.md",
|
19
19
|
'documentation_uri' => "https://www.rubydoc.info/gems/grape/#{s.version}",
|
20
|
-
'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
|
20
|
+
'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}",
|
21
|
+
'rubygems_mfa_required' => 'true'
|
21
22
|
}
|
22
23
|
|
23
24
|
s.add_runtime_dependency 'activesupport', '>= 6'
|
data/lib/grape/content_types.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ContentTypes
|
5
|
-
|
5
|
+
module_function
|
6
6
|
|
7
7
|
# Content types are listed in order of preference.
|
8
|
-
|
8
|
+
DEFAULTS = {
|
9
9
|
xml: 'application/xml',
|
10
10
|
serializable_hash: 'application/json',
|
11
11
|
json: 'application/json',
|
@@ -13,13 +13,18 @@ module Grape
|
|
13
13
|
txt: 'text/plain'
|
14
14
|
}.freeze
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
MIME_TYPES = Grape::ContentTypes::DEFAULTS.except(:serializable_hash).invert.freeze
|
17
|
+
|
18
|
+
def content_types_for(from_settings)
|
19
|
+
from_settings.presence || DEFAULTS
|
20
|
+
end
|
21
|
+
|
22
|
+
def mime_types_for(from_settings)
|
23
|
+
return MIME_TYPES if from_settings == Grape::ContentTypes::DEFAULTS
|
20
24
|
|
21
|
-
|
22
|
-
|
25
|
+
from_settings.each_with_object({}) do |(k, v), types_without_params|
|
26
|
+
# remove optional parameter
|
27
|
+
types_without_params[v.split(';', 2).first] = k
|
23
28
|
end
|
24
29
|
end
|
25
30
|
end
|
data/lib/grape/dsl/helpers.rb
CHANGED
@@ -33,18 +33,22 @@ module Grape
|
|
33
33
|
# end
|
34
34
|
#
|
35
35
|
def helpers(*new_modules, &block)
|
36
|
-
include_new_modules(new_modules)
|
37
|
-
include_block(block)
|
36
|
+
include_new_modules(new_modules)
|
37
|
+
include_block(block)
|
38
38
|
include_all_in_scope if !block && new_modules.empty?
|
39
39
|
end
|
40
40
|
|
41
41
|
protected
|
42
42
|
|
43
43
|
def include_new_modules(modules)
|
44
|
+
return if modules.empty?
|
45
|
+
|
44
46
|
modules.each { |mod| make_inclusion(mod) }
|
45
47
|
end
|
46
48
|
|
47
49
|
def include_block(block)
|
50
|
+
return unless block
|
51
|
+
|
48
52
|
Module.new.tap do |mod|
|
49
53
|
make_inclusion(mod) { mod.class_eval(&block) }
|
50
54
|
end
|
@@ -58,7 +62,7 @@ module Grape
|
|
58
62
|
|
59
63
|
def include_all_in_scope
|
60
64
|
Module.new.tap do |mod|
|
61
|
-
namespace_stackable(:helpers).each { |mod_to_include| mod.
|
65
|
+
namespace_stackable(:helpers).each { |mod_to_include| mod.include mod_to_include }
|
62
66
|
change!
|
63
67
|
end
|
64
68
|
end
|
@@ -163,12 +163,19 @@ module Grape
|
|
163
163
|
# end user with the specified message.
|
164
164
|
#
|
165
165
|
# @param message [String] The message to display.
|
166
|
-
# @param status [Integer]
|
166
|
+
# @param status [Integer] The HTTP Status Code. Defaults to default_error_status, 500 if not set.
|
167
167
|
# @param additional_headers [Hash] Addtional headers for the response.
|
168
|
-
|
168
|
+
# @param backtrace [Array<String>] The backtrace of the exception that caused the error.
|
169
|
+
# @param original_exception [Exception] The original exception that caused the error.
|
170
|
+
def error!(message, status = nil, additional_headers = nil, backtrace = nil, original_exception = nil)
|
169
171
|
status = self.status(status || namespace_inheritable(:default_error_status))
|
170
172
|
headers = additional_headers.present? ? header.merge(additional_headers) : header
|
171
|
-
throw :error,
|
173
|
+
throw :error,
|
174
|
+
message: message,
|
175
|
+
status: status,
|
176
|
+
headers: headers,
|
177
|
+
backtrace: backtrace,
|
178
|
+
original_exception: original_exception
|
172
179
|
end
|
173
180
|
|
174
181
|
# Creates a Rack response based on the provided message, status, and headers.
|
@@ -17,18 +17,16 @@ module Grape
|
|
17
17
|
# Specify the format for the API's serializers.
|
18
18
|
# May be `:json`, `:xml`, `:txt`, etc.
|
19
19
|
def format(new_format = nil)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
namespace_inheritable(:format)
|
31
|
-
end
|
20
|
+
return namespace_inheritable(:format) unless new_format
|
21
|
+
|
22
|
+
symbolic_new_format = new_format.to_sym
|
23
|
+
namespace_inheritable(:format, symbolic_new_format)
|
24
|
+
namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(symbolic_new_format))
|
25
|
+
|
26
|
+
content_type = content_types[symbolic_new_format]
|
27
|
+
raise Grape::Exceptions::MissingMimeType.new(new_format) unless content_type
|
28
|
+
|
29
|
+
namespace_stackable(:content_types, symbolic_new_format => content_type)
|
32
30
|
end
|
33
31
|
|
34
32
|
# Specify a custom formatter for a content-type.
|
@@ -43,12 +41,10 @@ module Grape
|
|
43
41
|
|
44
42
|
# Specify a default error formatter.
|
45
43
|
def default_error_formatter(new_formatter_name = nil)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
namespace_inheritable(:default_error_formatter)
|
51
|
-
end
|
44
|
+
return namespace_inheritable(:default_error_formatter) unless new_formatter_name
|
45
|
+
|
46
|
+
new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name)
|
47
|
+
namespace_inheritable(:default_error_formatter, new_formatter)
|
52
48
|
end
|
53
49
|
|
54
50
|
def error_formatter(format, options)
|
data/lib/grape/endpoint.rb
CHANGED
@@ -114,10 +114,10 @@ module Grape
|
|
114
114
|
# Update our settings from a given set of stackable parameters. Used when
|
115
115
|
# the endpoint's API is mounted under another one.
|
116
116
|
def inherit_settings(namespace_stackable)
|
117
|
-
|
117
|
+
parent_validations = namespace_stackable[:validations]
|
118
|
+
inheritable_setting.route[:saved_validations].concat(parent_validations) if parent_validations.any?
|
118
119
|
parent_declared_params = namespace_stackable[:declared_params]
|
119
|
-
|
120
|
-
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
|
120
|
+
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params.any?
|
121
121
|
|
122
122
|
endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
|
123
123
|
end
|
@@ -191,8 +191,7 @@ module Grape
|
|
191
191
|
|
192
192
|
def prepare_version
|
193
193
|
version = namespace_inheritable(:version)
|
194
|
-
return
|
195
|
-
return if version.empty?
|
194
|
+
return if version.blank?
|
196
195
|
|
197
196
|
version.length == 1 ? version.first : version
|
198
197
|
end
|
@@ -206,7 +205,9 @@ module Grape
|
|
206
205
|
end
|
207
206
|
|
208
207
|
def prepare_path(path)
|
209
|
-
|
208
|
+
namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash
|
209
|
+
namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash
|
210
|
+
path_settings = namespace_stackable_hash.merge!(namespace_inheritable_hash)
|
210
211
|
Path.new(path, namespace, path_settings)
|
211
212
|
end
|
212
213
|
|
@@ -235,6 +236,15 @@ module Grape
|
|
235
236
|
(options == endpoint.options) && (inheritable_setting.to_hash == endpoint.inheritable_setting.to_hash)
|
236
237
|
end
|
237
238
|
|
239
|
+
# The purpose of this override is solely for stripping internals when an error occurs while calling
|
240
|
+
# an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
|
241
|
+
# Otherwise, it calls super.
|
242
|
+
def inspect
|
243
|
+
return super unless env
|
244
|
+
|
245
|
+
"#{self.class} in '#{route.origin}' endpoint"
|
246
|
+
end
|
247
|
+
|
238
248
|
protected
|
239
249
|
|
240
250
|
def run
|
@@ -280,36 +290,39 @@ module Grape
|
|
280
290
|
def build_stack(helpers)
|
281
291
|
stack = Grape::Middleware::Stack.new
|
282
292
|
|
293
|
+
content_types = namespace_stackable_with_hash(:content_types)
|
294
|
+
format = namespace_inheritable(:format)
|
295
|
+
|
283
296
|
stack.use Rack::Head
|
284
297
|
stack.use Class.new(Grape::Middleware::Error),
|
285
298
|
helpers: helpers,
|
286
|
-
format:
|
287
|
-
content_types:
|
299
|
+
format: format,
|
300
|
+
content_types: content_types,
|
288
301
|
default_status: namespace_inheritable(:default_error_status),
|
289
302
|
rescue_all: namespace_inheritable(:rescue_all),
|
290
303
|
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
291
304
|
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
292
305
|
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
293
|
-
rescue_options: namespace_stackable_with_hash(:rescue_options)
|
294
|
-
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers)
|
295
|
-
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers)
|
306
|
+
rescue_options: namespace_stackable_with_hash(:rescue_options),
|
307
|
+
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers),
|
308
|
+
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers),
|
296
309
|
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
297
310
|
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
298
311
|
|
299
312
|
stack.concat namespace_stackable(:middleware)
|
300
313
|
|
301
|
-
if namespace_inheritable(:version)
|
314
|
+
if namespace_inheritable(:version).present?
|
302
315
|
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
303
|
-
versions: namespace_inheritable(:version)
|
316
|
+
versions: namespace_inheritable(:version).flatten,
|
304
317
|
version_options: namespace_inheritable(:version_options),
|
305
318
|
prefix: namespace_inheritable(:root_prefix),
|
306
319
|
mount_path: namespace_stackable(:mount_path).first
|
307
320
|
end
|
308
321
|
|
309
322
|
stack.use Grape::Middleware::Formatter,
|
310
|
-
format:
|
323
|
+
format: format,
|
311
324
|
default_format: namespace_inheritable(:default_format) || :txt,
|
312
|
-
content_types:
|
325
|
+
content_types: content_types,
|
313
326
|
formatters: namespace_stackable_with_hash(:formatters),
|
314
327
|
parsers: namespace_stackable_with_hash(:parsers)
|
315
328
|
|
@@ -320,7 +333,9 @@ module Grape
|
|
320
333
|
|
321
334
|
def build_helpers
|
322
335
|
helpers = namespace_stackable(:helpers)
|
323
|
-
|
336
|
+
return if helpers.empty?
|
337
|
+
|
338
|
+
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
324
339
|
end
|
325
340
|
|
326
341
|
private :build_stack, :build_helpers
|
@@ -339,7 +354,7 @@ module Grape
|
|
339
354
|
@lazy_initialize_lock.synchronize do
|
340
355
|
return true if @lazy_initialized
|
341
356
|
|
342
|
-
@helpers = build_helpers
|
357
|
+
@helpers = build_helpers&.tap { |mod| self.class.include mod }
|
343
358
|
@app = options[:app] || build_stack(@helpers)
|
344
359
|
|
345
360
|
@lazy_initialized = true
|
@@ -404,9 +419,5 @@ module Grape
|
|
404
419
|
options[:options_route_enabled] &&
|
405
420
|
env[Rack::REQUEST_METHOD] == Rack::OPTIONS
|
406
421
|
end
|
407
|
-
|
408
|
-
def inspect
|
409
|
-
"#{self.class} in `#{route.origin}' endpoint"
|
410
|
-
end
|
411
422
|
end
|
412
423
|
end
|
@@ -9,17 +9,18 @@ module Grape
|
|
9
9
|
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
|
10
10
|
result = wrap_message(present(message, env))
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
12
|
+
result = merge_rescue_options(result, backtrace, options, original_exception) if result.is_a?(Hash)
|
13
|
+
|
15
14
|
::Grape::Json.dump(result)
|
16
15
|
end
|
17
16
|
|
18
17
|
private
|
19
18
|
|
20
19
|
def wrap_message(message)
|
21
|
-
if message.is_a?(
|
20
|
+
if message.is_a?(Hash)
|
22
21
|
message
|
22
|
+
elsif message.is_a?(Exceptions::ValidationErrors)
|
23
|
+
message.as_json
|
23
24
|
else
|
24
25
|
{ error: ensure_utf8(message) }
|
25
26
|
end
|
@@ -30,6 +31,14 @@ module Grape
|
|
30
31
|
|
31
32
|
message.encode('UTF-8', invalid: :replace, undef: :replace)
|
32
33
|
end
|
34
|
+
|
35
|
+
def merge_rescue_options(result, backtrace, options, original_exception)
|
36
|
+
rescue_options = options[:rescue_options] || {}
|
37
|
+
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
38
|
+
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
39
|
+
|
40
|
+
result
|
41
|
+
end
|
33
42
|
end
|
34
43
|
end
|
35
44
|
end
|
@@ -2,34 +2,22 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
5
|
+
module_function
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
xml: Grape::ErrorFormatter::Xml
|
15
|
-
}
|
16
|
-
end
|
7
|
+
DEFAULTS = {
|
8
|
+
serializable_hash: Grape::ErrorFormatter::Json,
|
9
|
+
json: Grape::ErrorFormatter::Json,
|
10
|
+
jsonapi: Grape::ErrorFormatter::Json,
|
11
|
+
txt: Grape::ErrorFormatter::Txt,
|
12
|
+
xml: Grape::ErrorFormatter::Xml
|
13
|
+
}.freeze
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
def formatter_for(format, error_formatters = nil, default_error_formatter = nil)
|
16
|
+
select_formatter(error_formatters, format) || default_error_formatter || DEFAULTS[:txt]
|
17
|
+
end
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
case spec
|
25
|
-
when nil
|
26
|
-
options[:default_error_formatter] || Grape::ErrorFormatter::Txt
|
27
|
-
when Symbol
|
28
|
-
method(spec)
|
29
|
-
else
|
30
|
-
spec
|
31
|
-
end
|
32
|
-
end
|
19
|
+
def select_formatter(error_formatters, format)
|
20
|
+
error_formatters&.key?(format) ? error_formatters[format] : DEFAULTS[format]
|
33
21
|
end
|
34
22
|
end
|
35
23
|
end
|
data/lib/grape/formatter.rb
CHANGED
@@ -2,34 +2,24 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Formatter
|
5
|
-
|
5
|
+
module_function
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
xml: Grape::Formatter::Xml
|
15
|
-
}
|
16
|
-
end
|
7
|
+
DEFAULTS = {
|
8
|
+
json: Grape::Formatter::Json,
|
9
|
+
jsonapi: Grape::Formatter::Json,
|
10
|
+
serializable_hash: Grape::Formatter::SerializableHash,
|
11
|
+
txt: Grape::Formatter::Txt,
|
12
|
+
xml: Grape::Formatter::Xml
|
13
|
+
}.freeze
|
17
14
|
|
18
|
-
|
19
|
-
builtin_formatters.merge(default_elements).merge!(options[:formatters] || {})
|
20
|
-
end
|
15
|
+
DEFAULT_LAMBDA_FORMATTER = ->(obj, _env) { obj }
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
method(spec)
|
29
|
-
else
|
30
|
-
spec
|
31
|
-
end
|
32
|
-
end
|
17
|
+
def formatter_for(api_format, formatters)
|
18
|
+
select_formatter(formatters, api_format) || DEFAULT_LAMBDA_FORMATTER
|
19
|
+
end
|
20
|
+
|
21
|
+
def select_formatter(formatters, api_format)
|
22
|
+
formatters&.key?(api_format) ? formatters[api_format] : DEFAULTS[api_format]
|
33
23
|
end
|
34
24
|
end
|
35
25
|
end
|
data/lib/grape/locale/en.yml
CHANGED
@@ -11,6 +11,7 @@ en:
|
|
11
11
|
except_values: 'has a value not allowed'
|
12
12
|
same_as: 'is not the same as %{parameter}'
|
13
13
|
length: 'is expected to have length within %{min} and %{max}'
|
14
|
+
length_is: 'is expected to have length exactly equal to %{is}'
|
14
15
|
length_min: 'is expected to have length greater than or equal to %{min}'
|
15
16
|
length_max: 'is expected to have length less than or equal to %{max}'
|
16
17
|
missing_vendor_option:
|
@@ -4,18 +4,17 @@ module Grape
|
|
4
4
|
module Middleware
|
5
5
|
class Base
|
6
6
|
include Helpers
|
7
|
+
include Grape::DSL::Headers
|
7
8
|
|
8
9
|
attr_reader :app, :env, :options
|
9
10
|
|
10
11
|
TEXT_HTML = 'text/html'
|
11
12
|
|
12
|
-
include Grape::DSL::Headers
|
13
|
-
|
14
13
|
# @param [Rack Application] app The standard argument for a Rack middleware.
|
15
14
|
# @param [Hash] options A hash of options, simply stored for use by subclasses.
|
16
15
|
def initialize(app, *options)
|
17
16
|
@app = app
|
18
|
-
@options = options.any? ? default_options.
|
17
|
+
@options = options.any? ? default_options.deep_merge(options.shift) : default_options
|
19
18
|
@app_response = nil
|
20
19
|
end
|
21
20
|
|
@@ -61,22 +60,20 @@ module Grape
|
|
61
60
|
@app_response = Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
|
62
61
|
end
|
63
62
|
|
64
|
-
def
|
65
|
-
|
63
|
+
def content_types
|
64
|
+
@content_types ||= Grape::ContentTypes.content_types_for(options[:content_types])
|
66
65
|
end
|
67
66
|
|
68
|
-
def
|
69
|
-
ContentTypes.
|
67
|
+
def mime_types
|
68
|
+
@mime_types ||= Grape::ContentTypes.mime_types_for(content_types)
|
70
69
|
end
|
71
70
|
|
72
|
-
def
|
73
|
-
|
71
|
+
def content_type_for(format)
|
72
|
+
content_types_indifferent_access[format]
|
74
73
|
end
|
75
74
|
|
76
|
-
def
|
77
|
-
|
78
|
-
types_without_params[v.split(';').first] = k
|
79
|
-
end
|
75
|
+
def content_type
|
76
|
+
content_type_for(env[Grape::Env::API_FORMAT] || options[:format]) || TEXT_HTML
|
80
77
|
end
|
81
78
|
|
82
79
|
private
|
@@ -89,6 +86,10 @@ module Grape
|
|
89
86
|
when Array then response[1].merge!(headers)
|
90
87
|
end
|
91
88
|
end
|
89
|
+
|
90
|
+
def content_types_indifferent_access
|
91
|
+
@content_types_indifferent_access ||= content_types.with_indifferent_access
|
92
|
+
end
|
92
93
|
end
|
93
94
|
end
|
94
95
|
end
|
@@ -26,7 +26,7 @@ module Grape
|
|
26
26
|
|
27
27
|
def initialize(app, *options)
|
28
28
|
super
|
29
|
-
self.class.
|
29
|
+
self.class.include(@options[:helpers]) if @options[:helpers]
|
30
30
|
end
|
31
31
|
|
32
32
|
def call!(env)
|
@@ -45,7 +45,7 @@ module Grape
|
|
45
45
|
|
46
46
|
def format_message(message, backtrace, original_exception = nil)
|
47
47
|
format = env[Grape::Env::API_FORMAT] || options[:format]
|
48
|
-
formatter = Grape::ErrorFormatter.formatter_for(format,
|
48
|
+
formatter = Grape::ErrorFormatter.formatter_for(format, options[:error_formatters], options[:default_error_formatter])
|
49
49
|
return formatter.call(message, backtrace, options, env, original_exception) if formatter
|
50
50
|
|
51
51
|
throw :error,
|
@@ -79,7 +79,7 @@ module Grape
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def rescue_handler_for_base_only_class(klass)
|
82
|
-
error, handler = options[:base_only_rescue_handlers]
|
82
|
+
error, handler = options[:base_only_rescue_handlers]&.find { |err, _handler| klass == err }
|
83
83
|
|
84
84
|
return unless error
|
85
85
|
|
@@ -87,7 +87,7 @@ module Grape
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def rescue_handler_for_class_or_its_ancestor(klass)
|
90
|
-
error, handler = options[:rescue_handlers]
|
90
|
+
error, handler = options[:rescue_handlers]&.find { |err, _handler| klass <= err }
|
91
91
|
|
92
92
|
return unless error
|
93
93
|
|
@@ -120,12 +120,12 @@ module Grape
|
|
120
120
|
handler.arity.zero? ? endpoint.instance_exec(&handler) : endpoint.instance_exec(error, &handler)
|
121
121
|
end
|
122
122
|
|
123
|
-
|
124
|
-
|
125
|
-
|
123
|
+
if error?(response)
|
124
|
+
error_response(response)
|
125
|
+
elsif response.is_a?(Rack::Response)
|
126
126
|
response
|
127
127
|
else
|
128
|
-
run_rescue_handler(:default_rescue_handler, Grape::Exceptions::InvalidResponse.new, endpoint)
|
128
|
+
run_rescue_handler(method(:default_rescue_handler), Grape::Exceptions::InvalidResponse.new, endpoint)
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
@@ -137,7 +137,9 @@ module Grape
|
|
137
137
|
end
|
138
138
|
|
139
139
|
def error?(response)
|
140
|
-
response.is_a?(Hash)
|
140
|
+
return false unless response.is_a?(Hash)
|
141
|
+
|
142
|
+
response.key?(:message) && response.key?(:status) && response.key?(:headers)
|
141
143
|
end
|
142
144
|
end
|
143
145
|
end
|