grape 4.0.1 → 4.1.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 +102 -1
- data/README.md +195 -95
- data/UPGRADING.md +364 -0
- data/grape.gemspec +1 -1
- data/lib/grape/api/instance.rb +57 -30
- data/lib/grape/api.rb +50 -20
- data/lib/grape/content_types.rb +26 -9
- data/lib/grape/cookies.rb +1 -1
- data/lib/grape/declared_params_handler.rb +35 -7
- data/lib/grape/dsl/declared.rb +5 -4
- data/lib/grape/dsl/desc.rb +1 -2
- data/lib/grape/dsl/entity.rb +35 -19
- data/lib/grape/dsl/headers.rb +3 -3
- data/lib/grape/dsl/helpers.rb +5 -8
- data/lib/grape/dsl/inside_route.rb +33 -27
- data/lib/grape/dsl/logger.rb +10 -4
- data/lib/grape/dsl/middleware.rb +4 -9
- data/lib/grape/dsl/mounting.rb +149 -0
- data/lib/grape/dsl/parameters.rb +78 -104
- data/lib/grape/dsl/request_response.rb +35 -22
- data/lib/grape/dsl/routing.rb +17 -191
- data/lib/grape/dsl/settings.rb +1 -1
- data/lib/grape/dsl/version_options.rb +2 -2
- data/lib/grape/dsl/versioning.rb +98 -0
- data/lib/grape/endpoint/state.rb +15 -0
- data/lib/grape/endpoint.rb +229 -137
- data/lib/grape/error_formatter/base.rb +28 -13
- data/lib/grape/error_formatter/json.rb +3 -0
- data/lib/grape/error_formatter/jsonl.rb +15 -0
- data/lib/grape/error_formatter/xml.rb +3 -1
- data/lib/grape/exceptions/error_response.rb +23 -7
- data/lib/grape/exceptions/halt.rb +39 -0
- data/lib/grape/exceptions/validation.rb +9 -9
- data/lib/grape/exceptions/validation_errors.rb +12 -2
- data/lib/grape/formatter/jsonl.rb +34 -0
- data/lib/grape/formatter/markdown.rb +17 -0
- data/lib/grape/formatter/md.rb +11 -0
- data/lib/grape/locale/en.yml +1 -0
- data/lib/grape/middleware/auth/base.rb +16 -1
- data/lib/grape/middleware/auth/strategy_info.rb +2 -2
- data/lib/grape/middleware/base.rb +19 -13
- data/lib/grape/middleware/error.rb +133 -35
- data/lib/grape/middleware/formatter.rb +235 -94
- data/lib/grape/middleware/precomputed_content_types.rb +14 -2
- data/lib/grape/middleware/stack.rb +7 -8
- data/lib/grape/middleware/versioner/base.rb +26 -10
- data/lib/grape/middleware/versioner/header.rb +70 -15
- data/lib/grape/middleware/versioner/param.rb +3 -5
- data/lib/grape/middleware/versioner/path.rb +20 -11
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/mountable.rb +2 -4
- data/lib/grape/namespace.rb +2 -3
- data/lib/grape/parser/jsonl.rb +19 -0
- data/lib/grape/request.rb +46 -8
- data/lib/grape/router/base_route.rb +41 -0
- data/lib/grape/router/greedy_route.rb +1 -1
- data/lib/grape/router/mustermann_pattern.rb +1 -1
- data/lib/grape/router/pattern/path.rb +1 -1
- data/lib/grape/router/pattern.rb +11 -5
- data/lib/grape/router/route.rb +42 -11
- data/lib/grape/router/route_buckets.rb +246 -0
- data/lib/grape/router.rb +262 -32
- data/lib/grape/util/endpoint_configuration.rb +7 -1
- data/lib/grape/util/inheritable_setting.rb +80 -161
- data/lib/grape/util/lazy/value.rb +52 -11
- data/lib/grape/util/media_type.rb +38 -15
- data/lib/grape/util/path_normalizer.rb +7 -2
- data/lib/grape/util/registry.rb +9 -2
- data/lib/grape/util/scrub.rb +33 -0
- data/lib/grape/util/translation.rb +6 -6
- data/lib/grape/validations/attributes_iterator.rb +73 -37
- data/lib/grape/validations/coerce_options.rb +1 -1
- data/lib/grape/validations/params_documentation.rb +15 -7
- data/lib/grape/validations/params_scope.rb +176 -43
- data/lib/grape/validations/types/custom_type_coercer.rb +2 -3
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +20 -12
- data/lib/grape/validations/types/multiple_type_coercer.rb +1 -2
- data/lib/grape/validations/types/primitive_coercer.rb +17 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +24 -10
- data/lib/grape/validations/types.rb +12 -18
- data/lib/grape/validations/validations_spec.rb +53 -22
- data/lib/grape/validations/validators/allow_blank_validator.rb +2 -2
- data/lib/grape/validations/validators/as_validator.rb +2 -3
- data/lib/grape/validations/validators/base.rb +124 -25
- data/lib/grape/validations/validators/coerce_validator.rb +2 -3
- data/lib/grape/validations/validators/default_validator.rb +18 -2
- data/lib/grape/validations/validators/except_values_validator.rb +16 -3
- data/lib/grape/validations/validators/hash_elements_validator.rb +34 -0
- data/lib/grape/validations/validators/length_validator.rb +24 -16
- data/lib/grape/validations/validators/multiple_params_base.rb +0 -4
- data/lib/grape/validations/validators/values_validator.rb +18 -24
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +3 -0
- metadata +18 -11
- data/lib/grape/util/lazy/value_array.rb +0 -21
- data/lib/grape/util/lazy/value_enumerable.rb +0 -31
- data/lib/grape/util/lazy/value_hash.rb +0 -21
- data/lib/grape/validations/multiple_attributes_iterator.rb +0 -13
- data/lib/grape/validations/single_attribute_iterator.rb +0 -28
|
@@ -32,19 +32,7 @@ module Grape
|
|
|
32
32
|
with = payload.delete(:with)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
presenter = with || env[Grape::Env::API_ENDPOINT].entity_class_for_obj(payload)
|
|
36
|
-
|
|
37
|
-
unless presenter || env[Grape::Env::GRAPE_ROUTING_ARGS].nil?
|
|
38
|
-
# env['api.endpoint'].route does not work when the error occurs within a middleware
|
|
39
|
-
# the Endpoint does not have a valid env at this moment
|
|
40
|
-
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
|
41
|
-
found_code = http_codes.find do |http_code|
|
|
42
|
-
(http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
|
|
43
|
-
end if env[Grape::Env::API_ENDPOINT].request
|
|
44
|
-
|
|
45
|
-
presenter = found_code[2] if found_code
|
|
46
|
-
end
|
|
47
|
-
|
|
35
|
+
presenter = with || env[Grape::Env::API_ENDPOINT].entity_class_for_obj(payload) || failure_presenter(payload, env)
|
|
48
36
|
return payload unless presenter
|
|
49
37
|
|
|
50
38
|
embeds = { env: }
|
|
@@ -64,6 +52,33 @@ module Grape
|
|
|
64
52
|
|
|
65
53
|
private
|
|
66
54
|
|
|
55
|
+
# The entity the route's failure codes name for the status being
|
|
56
|
+
# answered: those entities document a structured error body.
|
|
57
|
+
#
|
|
58
|
+
# A String message has no attributes for one to expose -- presenting
|
|
59
|
+
# it raised, and the failsafe answered 500 in place of the status
|
|
60
|
+
# +error!+ asked for -- so it is left for #wrap_message. And a route's
|
|
61
|
+
# failure presenters are written for the errors the endpoint itself
|
|
62
|
+
# raises: one raised by middleware before the endpoint ran -- it has
|
|
63
|
+
# no request yet -- keeps the shape that middleware gave it rather than
|
|
64
|
+
# being fit to an entity it was not written for.
|
|
65
|
+
def failure_presenter(payload, env)
|
|
66
|
+
return if payload.is_a?(String)
|
|
67
|
+
|
|
68
|
+
routing_args = env[Grape::Env::GRAPE_ROUTING_ARGS]
|
|
69
|
+
endpoint = env[Grape::Env::API_ENDPOINT]
|
|
70
|
+
return if routing_args.nil? || !endpoint.request
|
|
71
|
+
|
|
72
|
+
presenter_for_status(routing_args[:route_info].failure, endpoint.status)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The entity +failure+ names for +status+, among the +[code, message,
|
|
76
|
+
# entity]+ entries a route's failure codes are.
|
|
77
|
+
def presenter_for_status(failure, status)
|
|
78
|
+
found_code = Array(failure).find { |http_code| http_code[0].to_i == status && http_code[2].respond_to?(:represent) }
|
|
79
|
+
found_code[2] if found_code
|
|
80
|
+
end
|
|
81
|
+
|
|
67
82
|
def inherited(klass)
|
|
68
83
|
super
|
|
69
84
|
ErrorFormatter.register(klass)
|
|
@@ -23,8 +23,11 @@ module Grape
|
|
|
23
23
|
{ error: ensure_utf8(message) }
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# Re-encoding a String that already is valid UTF-8 only copies it, for
|
|
27
|
+
# about 260 ns on every error response.
|
|
26
28
|
def ensure_utf8(message)
|
|
27
29
|
return message unless message.respond_to? :encode
|
|
30
|
+
return message if message.is_a?(String) && message.encoding == Encoding::UTF_8 && message.valid_encoding?
|
|
28
31
|
|
|
29
32
|
message.encode('UTF-8', invalid: :replace, undef: :replace)
|
|
30
33
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module ErrorFormatter
|
|
5
|
+
# An error renders as the JSON error formatter renders it, ended by a
|
|
6
|
+
# newline, so the body is a JSON Lines document of one line.
|
|
7
|
+
class Jsonl < Json
|
|
8
|
+
class << self
|
|
9
|
+
def format_structured_message(structured_message)
|
|
10
|
+
"#{super}\n"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module ErrorFormatter
|
|
5
5
|
class Xml < Base
|
|
6
|
+
# Base#wrap_message always hands over a Hash, which ActiveSupport's
|
|
7
|
+
# conversions (required by grape.rb) give +to_xml+.
|
|
6
8
|
def self.format_structured_message(structured_message)
|
|
7
|
-
structured_message.
|
|
9
|
+
structured_message.to_xml(root: :error)
|
|
8
10
|
end
|
|
9
11
|
end
|
|
10
12
|
end
|
|
@@ -2,13 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
module Grape
|
|
4
4
|
module Exceptions
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# implicit-schema Hash
|
|
8
|
-
# and the error middleware.
|
|
9
|
-
|
|
5
|
+
# The frozen payload thrown via `throw :error, ...` (or raised by `error!`
|
|
6
|
+
# inside a `Grape::Exceptions::Halt`) and consumed by
|
|
7
|
+
# `Middleware::Error#error_response`. Replaces the implicit-schema Hash
|
|
8
|
+
# that previously circulated between throw sites and the error middleware.
|
|
9
|
+
#
|
|
10
|
+
# A plain frozen class rather than a +Data+: every error response builds
|
|
11
|
+
# at least two (the payload, then the one +error_response+ fills the
|
|
12
|
+
# defaults into), and +Data+'s constructor takes its keywords as a Hash it
|
|
13
|
+
# then validates, about 450 ns apiece. A Ruby method keeps keyword
|
|
14
|
+
# arguments on the stack. It answers only what is read off it: its
|
|
15
|
+
# readers.
|
|
16
|
+
class ErrorResponse
|
|
17
|
+
MEMBERS = %i[status message headers backtrace original_exception].freeze
|
|
18
|
+
|
|
19
|
+
attr_reader(*MEMBERS)
|
|
20
|
+
|
|
10
21
|
def initialize(status: nil, message: nil, headers: nil, backtrace: nil, original_exception: nil)
|
|
11
|
-
|
|
22
|
+
@status = status
|
|
23
|
+
@message = message
|
|
24
|
+
@headers = headers
|
|
25
|
+
@backtrace = backtrace
|
|
26
|
+
@original_exception = original_exception
|
|
27
|
+
freeze
|
|
12
28
|
end
|
|
13
29
|
|
|
14
30
|
def to_s
|
|
@@ -38,7 +54,7 @@ module Grape
|
|
|
38
54
|
when Grape::Exceptions::Base
|
|
39
55
|
from_exception(input)
|
|
40
56
|
when Hash
|
|
41
|
-
new(**input.slice(
|
|
57
|
+
new(**input.slice(*MEMBERS))
|
|
42
58
|
else
|
|
43
59
|
new
|
|
44
60
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Exceptions
|
|
5
|
+
# Raised by +error!+ to end the request with an error response. It carries
|
|
6
|
+
# the ErrorResponse a +throw :error+ carries, and +Middleware::Error+
|
|
7
|
+
# renders it the same way: as it is, without consulting +rescue_from+.
|
|
8
|
+
#
|
|
9
|
+
# It is raised rather than thrown so that it leaves a block the way an
|
|
10
|
+
# exception does. Active Record rolls a transaction back only when an
|
|
11
|
+
# exception leaves its block, and commits it on a +throw+. A +throw+ cannot
|
|
12
|
+
# leave the fiber or thread it was thrown in either, while +Async::Task#wait+
|
|
13
|
+
# and +Thread#value+ re-raise an exception in the request's own.
|
|
14
|
+
#
|
|
15
|
+
# A StandardError on purpose: async treats any other exception leaving a
|
|
16
|
+
# task as fatal and stops its reactor. It is not a Grape::Exceptions::Base,
|
|
17
|
+
# so a +rescue+ of those around +error!+ still lets it through.
|
|
18
|
+
class Halt < StandardError
|
|
19
|
+
extend Forwardable
|
|
20
|
+
|
|
21
|
+
EMPTY_BACKTRACE = [].freeze
|
|
22
|
+
|
|
23
|
+
attr_reader :response
|
|
24
|
+
|
|
25
|
+
def_delegators :response, :status, :headers
|
|
26
|
+
|
|
27
|
+
# Keywords spelled out rather than forwarded with **, which would gather
|
|
28
|
+
# them into a Hash on every error! only to spread it again.
|
|
29
|
+
def initialize(status: nil, message: nil, headers: nil, backtrace: nil, original_exception: nil)
|
|
30
|
+
@response = ErrorResponse.new(status:, message:, headers:, backtrace:, original_exception:)
|
|
31
|
+
super(message)
|
|
32
|
+
# Pre-seed the backtrace so Ruby's raise skips capture, as
|
|
33
|
+
# Grape::Exceptions::Validation does: +error!+ is how a route answers
|
|
34
|
+
# with an error, and the backtrace would only point at that route.
|
|
35
|
+
set_backtrace(EMPTY_BACKTRACE)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -9,16 +9,16 @@ module Grape
|
|
|
9
9
|
|
|
10
10
|
def initialize(params:, message: nil, status: nil, headers: nil)
|
|
11
11
|
@params = Array(params)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
# Without a message, StandardError#message answers with this class's
|
|
13
|
+
# name, and that reached the response -- from a custom validator with
|
|
14
|
+
# no default_message_key, say.
|
|
15
|
+
message ||= :invalid
|
|
16
|
+
@message_key = case message
|
|
17
|
+
when Symbol then message
|
|
18
|
+
when Hash then message[:key]
|
|
19
|
+
end
|
|
20
20
|
|
|
21
|
-
super(status:, message:
|
|
21
|
+
super(status:, message: translate_message(message), headers:)
|
|
22
22
|
# Pre-seed the backtrace so Ruby's raise skips capture. Validation errors are
|
|
23
23
|
# a hot path (raised per bad attribute) and end up as 400 Bad Request responses;
|
|
24
24
|
# backtraces here point into Grape internals and have no diagnostic value.
|
|
@@ -23,7 +23,19 @@ module Grape
|
|
|
23
23
|
as_json.to_json
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# Translated once, when the error is built for its #message, and handed
|
|
27
|
+
# out as a copy from then on. Every lookup here is an I18n call of a few
|
|
28
|
+
# microseconds, and the README's recipe for answering with the list,
|
|
29
|
+
# +error!({ messages: e.full_messages }, 400)+, asked for all of them a
|
|
30
|
+
# second time. It also keeps the list the same as #message, which was
|
|
31
|
+
# already fixed at that point, if the locale changes in between.
|
|
26
32
|
def full_messages
|
|
33
|
+
(@full_messages ||= translate_full_messages).dup
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def translate_full_messages
|
|
27
39
|
messages = errors.flat_map do |attributes, errs|
|
|
28
40
|
errs.map do |error|
|
|
29
41
|
translate(
|
|
@@ -39,8 +51,6 @@ module Grape
|
|
|
39
51
|
messages
|
|
40
52
|
end
|
|
41
53
|
|
|
42
|
-
private
|
|
43
|
-
|
|
44
54
|
def translate_attributes(keys)
|
|
45
55
|
keys.map do |key|
|
|
46
56
|
translate(key, scope: 'grape.errors.attributes', default: key.to_s)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Formatter
|
|
5
|
+
# Renders JSON Lines (https://jsonlines.org): one JSON value per line, each
|
|
6
|
+
# line ended by a newline. An Array renders one line per element, and any
|
|
7
|
+
# other object one line. A String is served as it is, taken to be JSON
|
|
8
|
+
# Lines already.
|
|
9
|
+
#
|
|
10
|
+
# An Enumerator renders as it is read rather than up front, so the server
|
|
11
|
+
# streams each line as the enumerator yields its element:
|
|
12
|
+
#
|
|
13
|
+
# get :export do
|
|
14
|
+
# User.find_each # without a block, an Enumerator
|
|
15
|
+
# end
|
|
16
|
+
class Jsonl < Base
|
|
17
|
+
class << self
|
|
18
|
+
def call(object, env)
|
|
19
|
+
return object if object.is_a?(String)
|
|
20
|
+
return Grape::ServeStream::StreamResponse.new(object.lazy.map { |element| line(element, env) }) if object.is_a?(Enumerator)
|
|
21
|
+
return object.to_ary.map { |element| line(element, env) }.join if object.respond_to?(:to_ary)
|
|
22
|
+
|
|
23
|
+
line(object, env)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def line(object, env)
|
|
29
|
+
"#{Formatter::Json.call(object, env)}\n"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Formatter
|
|
5
|
+
class Markdown < Base
|
|
6
|
+
# Md inherits this, so the format named in the error comes from the class
|
|
7
|
+
# name, the way Grape::Util::Registry derives the name each class is
|
|
8
|
+
# registered under.
|
|
9
|
+
def self.call(object, _env)
|
|
10
|
+
return object.to_markdown if object.respond_to?(:to_markdown)
|
|
11
|
+
return object if object.is_a?(String)
|
|
12
|
+
|
|
13
|
+
raise Grape::Exceptions::InvalidFormatter.new(object.class, name.demodulize.underscore)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Formatter
|
|
5
|
+
# Registers Markdown's rendering under +md+ too, for an API that declares
|
|
6
|
+
# +content_type :md+ rather than +:markdown+ (see
|
|
7
|
+
# Grape::ContentTypes::FORMAT_SYNONYMS).
|
|
8
|
+
class Md < Markdown
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/grape/locale/en.yml
CHANGED
|
@@ -12,6 +12,7 @@ en:
|
|
|
12
12
|
except_values: 'has a value not allowed'
|
|
13
13
|
incompatible_option_values: '%{option1}: %{value1} is incompatible with %{option2}: %{value2}'
|
|
14
14
|
internal_server_error: 'Internal Server Error'
|
|
15
|
+
invalid: 'is invalid'
|
|
15
16
|
invalid_accept_header:
|
|
16
17
|
problem: 'invalid accept header'
|
|
17
18
|
resolution: '%{message}'
|
|
@@ -12,10 +12,25 @@ module Grape
|
|
|
12
12
|
raise Grape::Exceptions::UnknownAuthStrategy.new(strategy: options[:type]) unless @auth_strategy
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
# Base#call copies the middleware per request so #call! can keep the
|
|
16
|
+
# env in an ivar, and all that reads it is the strategy's block, to find
|
|
17
|
+
# the endpoint that checks the credentials. The block takes the
|
|
18
|
+
# endpoint from the env it was built for instead, so the request is
|
|
19
|
+
# answered by the one instance the stack built.
|
|
20
|
+
def call(env)
|
|
21
|
+
authenticate(env).to_a
|
|
22
|
+
end
|
|
23
|
+
|
|
15
24
|
def call!(env)
|
|
16
25
|
@env = env
|
|
26
|
+
authenticate(env)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def authenticate(env)
|
|
17
32
|
@auth_strategy.create(app, options) do |*args|
|
|
18
|
-
|
|
33
|
+
env[Grape::Env::API_ENDPOINT].instance_exec(*args, &options[:proc])
|
|
19
34
|
end.call(env)
|
|
20
35
|
end
|
|
21
36
|
end
|
|
@@ -4,10 +4,10 @@ module Grape
|
|
|
4
4
|
module Middleware
|
|
5
5
|
module Auth
|
|
6
6
|
StrategyInfo = Struct.new(:auth_class, :settings_fetcher) do
|
|
7
|
-
def create(app, options, &
|
|
7
|
+
def create(app, options, &)
|
|
8
8
|
strategy_args = settings_fetcher.call(options)
|
|
9
9
|
|
|
10
|
-
auth_class.new(app, *strategy_args, &
|
|
10
|
+
auth_class.new(app, *strategy_args, &)
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -35,12 +35,7 @@ module Grape
|
|
|
35
35
|
begin
|
|
36
36
|
@app_response = @app.call(@env)
|
|
37
37
|
ensure
|
|
38
|
-
|
|
39
|
-
after_response = after
|
|
40
|
-
rescue StandardError => e
|
|
41
|
-
warn "caught error of type #{e.class} in after callback inside #{self.class.name} : #{e.message}"
|
|
42
|
-
raise e
|
|
43
|
-
end
|
|
38
|
+
after_response = reporting_after_errors { after }
|
|
44
39
|
end
|
|
45
40
|
|
|
46
41
|
response = after_response || @app_response
|
|
@@ -54,7 +49,7 @@ module Grape
|
|
|
54
49
|
|
|
55
50
|
# @abstract
|
|
56
51
|
# Called after the application is called in the middleware lifecycle.
|
|
57
|
-
# @return [Response, nil] a Rack SPEC response or nil to
|
|
52
|
+
# @return [Response, nil] a Rack SPEC response, or nil to keep the application's.
|
|
58
53
|
def after; end
|
|
59
54
|
|
|
60
55
|
def rack_request
|
|
@@ -79,13 +74,24 @@ module Grape
|
|
|
79
74
|
|
|
80
75
|
private
|
|
81
76
|
|
|
77
|
+
# Runs what the middleware does once the app has answered. An error it
|
|
78
|
+
# raises is reported before it goes on: a +rescue_from :all+ handler
|
|
79
|
+
# turns it into a 500 without logging it, so this may be the only trace
|
|
80
|
+
# of it.
|
|
81
|
+
def reporting_after_errors
|
|
82
|
+
yield
|
|
83
|
+
rescue StandardError => e
|
|
84
|
+
warn "caught error of type #{e.class} in after callback inside #{self.class.name} : #{e.message}"
|
|
85
|
+
raise e
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# +response+ is a Rack triplet, unless +after+ or the app handed back a
|
|
89
|
+
# Rack::Response.
|
|
82
90
|
def merge_headers(response)
|
|
83
|
-
return if @header.
|
|
91
|
+
return if @header.nil? || @header.empty?
|
|
92
|
+
return response.headers.merge!(@header) if response.is_a?(Rack::Response)
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
when Rack::Response then response.headers.merge!(@header)
|
|
87
|
-
when Array then response[1].merge!(@header)
|
|
88
|
-
end
|
|
94
|
+
response[1].merge!(@header)
|
|
89
95
|
end
|
|
90
96
|
|
|
91
97
|
def merge_default_options(options)
|
|
@@ -112,7 +118,7 @@ module Grape
|
|
|
112
118
|
end
|
|
113
119
|
|
|
114
120
|
def try_scrub(obj)
|
|
115
|
-
|
|
121
|
+
Grape::Util::Scrub.scrub(obj)
|
|
116
122
|
end
|
|
117
123
|
end
|
|
118
124
|
end
|