rescue_registry 0.1.0 → 0.2.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 +13 -1
- data/lib/rescue_registry.rb +13 -2
- data/lib/rescue_registry/action_dispatch.rb +5 -69
- data/lib/rescue_registry/action_dispatch/debug_exceptions.rb +43 -0
- data/lib/rescue_registry/action_dispatch/exception_wrapper.rb +9 -0
- data/lib/rescue_registry/action_dispatch/show_exceptions.rb +17 -0
- data/lib/rescue_registry/context.rb +37 -0
- data/lib/rescue_registry/controller.rb +1 -22
- data/lib/rescue_registry/exception_handler.rb +19 -12
- data/lib/rescue_registry/exceptions_app.rb +14 -11
- data/lib/rescue_registry/railtie.rb +6 -2
- data/lib/rescue_registry/registry.rb +31 -12
- data/lib/rescue_registry/reset_context.rb +14 -0
- data/lib/rescue_registry/show_exceptions.rb +59 -0
- data/lib/rescue_registry/version.rb +1 -1
- metadata +9 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: abb4709b529437899b1f600805b3218d82581ba88c03642561d015385776dee3
|
4
|
+
data.tar.gz: 1f3efb50ac2375c0b204bb8af849615922f48d74c2eb06179a72bef6ce101754
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3025ac89186c93d3d16eea9f1aa4039955d4527eefbeccc939f7501753ad3f9ea306ebc3e705009ee06e9bfd928db5521643d159646e54c271642a2c9e358f0
|
7
|
+
data.tar.gz: f3df89c3ea0204a2388b6ccd40deb6d4061586207dc8dca97b2475b07027e28a1db19574741487fb439e7c755bf5d79c9c3f687d4ac20663b5bf2d32b5995b41
|
data/CHANGELOG.md
CHANGED
@@ -7,4 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
---
|
9
9
|
|
10
|
-
|
10
|
+
## [0.2.0] - 2019-05-21
|
11
|
+
### Added
|
12
|
+
- Support for non-Rails applications.
|
13
|
+
### Changed
|
14
|
+
- Passthrough statuses are currently only support for Rails applications.
|
15
|
+
- Added a new Rails middleware for handling the current context.
|
16
|
+
### Fixed
|
17
|
+
- Registry is now properly inherited so that new registrations in subclasses do not affect the parent.
|
18
|
+
- Default exception handler now works property in the root context.
|
19
|
+
|
20
|
+
## [0.1.0] - 2019-05-15
|
21
|
+
### Added
|
22
|
+
- Everything, it's the first release!
|
data/lib/rescue_registry.rb
CHANGED
@@ -3,11 +3,15 @@
|
|
3
3
|
require 'active_support'
|
4
4
|
|
5
5
|
module RescueRegistry
|
6
|
+
autoload :ActionDispatch, "rescue_registry/action_dispatch"
|
7
|
+
autoload :Context, "rescue_registry/context"
|
6
8
|
autoload :Controller, "rescue_registry/controller"
|
7
9
|
autoload :ExceptionsApp, "rescue_registry/exceptions_app"
|
8
10
|
autoload :ExceptionHandler, "rescue_registry/exception_handler"
|
9
11
|
autoload :RailsExceptionHandler, "rescue_registry/exception_handler"
|
10
12
|
autoload :Registry, "rescue_registry/registry"
|
13
|
+
autoload :ResetContext, "rescue_registry/reset_context"
|
14
|
+
autoload :ShowExceptions, "rescue_registry/show_exceptions"
|
11
15
|
|
12
16
|
class HandlerNotFound < StandardError; end
|
13
17
|
|
@@ -43,9 +47,16 @@ module RescueRegistry
|
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
50
|
+
ActiveSupport.on_load(:before_initialize) do
|
51
|
+
ActionDispatch::ExceptionWrapper.singleton_class.prepend RescueRegistry::ActionDispatch::ExceptionWrapper
|
52
|
+
ActionDispatch::DebugExceptions.prepend RescueRegistry::ActionDispatch::DebugExceptions
|
53
|
+
ActionDispatch::ShowExceptions.prepend RescueRegistry::ActionDispatch::ShowExceptions
|
54
|
+
end
|
55
|
+
|
46
56
|
ActiveSupport.on_load(:action_controller) do
|
47
57
|
include RescueRegistry::Controller
|
48
58
|
end
|
49
59
|
|
50
|
-
|
51
|
-
require 'rescue_registry/railtie'
|
60
|
+
if defined?(Rails)
|
61
|
+
require 'rescue_registry/railtie'
|
62
|
+
end
|
@@ -1,71 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def status_code_for_exception(class_name)
|
7
|
-
RescueRegistry.status_code_for_exception(class_name) ||
|
8
|
-
status_code_for_exception_without_rescue_registry(class_name)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
# Since this is for debugging only it's less critical to make changes here. The main area that matters is
|
14
|
-
# returning the correct status code. Since all this code relies upon the ExceptionWrapper which we've monkeypatched,
|
15
|
-
# it should work correctly without changes. However, we can provide more details in some areas so we hook in for that.
|
16
|
-
ActionDispatch::DebugExceptions.class_eval do
|
17
|
-
private
|
18
|
-
|
19
|
-
# `#log_error`
|
20
|
-
# TODO: We may be able to add more information, though the details remain to be determined
|
21
|
-
|
22
|
-
# `#render_for_browser_request`
|
23
|
-
# TODO: We may be able to add more information, though the details remain to be determined
|
24
|
-
|
25
|
-
# This would work without changes, but the formatting would be incorrect. Since it's for debugging only,
|
26
|
-
# we could choose to ignore it, but the detailed information would definitely be useful.
|
27
|
-
alias_method :render_for_api_request_without_rescue_registry, :render_for_api_request
|
28
|
-
def render_for_api_request(content_type, wrapper)
|
29
|
-
response = nil
|
30
|
-
|
31
|
-
if RescueRegistry.handles_exception?(wrapper.exception)
|
32
|
-
# Ideally `render_for_api_request` would be split up so we could avoid some duplication in RescueRegistry
|
33
|
-
begin
|
34
|
-
response = RescueRegistry.response_for_debugging(content_type, wrapper.exception, traces: wrapper.traces)
|
35
|
-
rescue Exception => e
|
36
|
-
# Replace the original exception (still available via `cause`) and let it get handled with default handlers
|
37
|
-
wrapper = ActionDispatch::ExceptionWrapper.new(wrapper.backtrace_cleaner, e)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
if response
|
42
|
-
render(*response)
|
43
|
-
else
|
44
|
-
# One of the following is true:
|
45
|
-
# - No handler for the exception
|
46
|
-
# - No response for content_type
|
47
|
-
# - An exception while generating the response
|
48
|
-
# In any case, we go with the default here.
|
49
|
-
render_for_api_request_without_rescue_registry(content_type, wrapper)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
ActionDispatch::ShowExceptions.class_eval do
|
55
|
-
# @private
|
56
|
-
alias_method :initialize_without_rescue_registry, :initialize
|
57
|
-
|
58
|
-
# @private
|
59
|
-
def initialize(*args)
|
60
|
-
initialize_without_rescue_registry(*args)
|
61
|
-
@exceptions_app = RescueRegistry::ExceptionsApp.new(@exceptions_app)
|
62
|
-
end
|
63
|
-
|
64
|
-
alias_method :call_without_rescue_registry, :call
|
65
|
-
def call(*args)
|
66
|
-
warn "Didn't expect RescueRegistry context to be set in middleware" if RescueRegistry.context
|
67
|
-
call_without_rescue_registry(*args)
|
68
|
-
ensure
|
69
|
-
RescueRegistry.context = nil
|
1
|
+
module RescueRegistry
|
2
|
+
module ActionDispatch
|
3
|
+
autoload :DebugExceptions, "rescue_registry/action_dispatch/debug_exceptions"
|
4
|
+
autoload :ExceptionWrapper, "rescue_registry/action_dispatch/exception_wrapper"
|
5
|
+
autoload :ShowExceptions, "rescue_registry/action_dispatch/show_exceptions"
|
70
6
|
end
|
71
7
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RescueRegistry
|
2
|
+
module ActionDispatch
|
3
|
+
# Since this is for debugging only it's less critical to make changes here. The main area that matters is
|
4
|
+
# returning the correct status code. Since all this code relies upon the ExceptionWrapper which we've monkeypatched,
|
5
|
+
# it should work correctly without changes. However, we can provide more details in some areas so we hook in for that.
|
6
|
+
module DebugExceptions
|
7
|
+
private
|
8
|
+
|
9
|
+
# `#log_error`
|
10
|
+
# TODO: We may be able to add more information, though the details remain to be determined
|
11
|
+
|
12
|
+
# `#render_for_browser_request`
|
13
|
+
# TODO: We may be able to add more information, though the details remain to be determined
|
14
|
+
|
15
|
+
# This would work without changes, but the formatting would be incorrect. Since it's for debugging only,
|
16
|
+
# we could choose to ignore it, but the detailed information would definitely be useful.
|
17
|
+
def render_for_api_request(content_type, wrapper)
|
18
|
+
response = nil
|
19
|
+
|
20
|
+
if RescueRegistry.handles_exception?(wrapper.exception)
|
21
|
+
# Ideally `render_for_api_request` would be split up so we could avoid some duplication in RescueRegistry
|
22
|
+
begin
|
23
|
+
response = RescueRegistry.response_for_debugging(content_type, wrapper.exception, traces: wrapper.traces)
|
24
|
+
rescue Exception => e
|
25
|
+
# Replace the original exception (still available via `cause`) and let it get handled with default handlers
|
26
|
+
wrapper = ActionDispatch::ExceptionWrapper.new(wrapper.backtrace_cleaner, e)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if response
|
31
|
+
render(*response)
|
32
|
+
else
|
33
|
+
# One of the following is true:
|
34
|
+
# - No handler for the exception
|
35
|
+
# - No response for content_type
|
36
|
+
# - An exception while generating the response
|
37
|
+
# In any case, we go with the default here.
|
38
|
+
super(content_type, wrapper)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RescueRegistry
|
2
|
+
module ActionDispatch
|
3
|
+
module ShowExceptions
|
4
|
+
def initialize(*args)
|
5
|
+
super
|
6
|
+
@exceptions_app = RescueRegistry::ExceptionsApp.new(@exceptions_app)
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(*args)
|
10
|
+
warn "Didn't expect RescueRegistry context to be set in middleware" if RescueRegistry.context
|
11
|
+
super
|
12
|
+
ensure
|
13
|
+
RescueRegistry.context = nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RescueRegistry
|
2
|
+
module Context
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
if respond_to?(:class_attribute)
|
7
|
+
# Prevents subclasses from sharing, but only available to classes
|
8
|
+
class_attribute :rescue_registry
|
9
|
+
else
|
10
|
+
# Allows this module to be included in a module
|
11
|
+
mattr_accessor :rescue_registry
|
12
|
+
end
|
13
|
+
|
14
|
+
self.rescue_registry = Registry.new(self)
|
15
|
+
|
16
|
+
class << self
|
17
|
+
delegate :register_exception, to: :rescue_registry
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def rescue_registry
|
22
|
+
self.class.rescue_registry
|
23
|
+
end
|
24
|
+
|
25
|
+
class_methods do
|
26
|
+
def inherited(subklass)
|
27
|
+
super
|
28
|
+
subklass.rescue_registry = rescue_registry.dup
|
29
|
+
subklass.rescue_registry.owner = subklass
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_exception_handler
|
33
|
+
ExceptionHandler
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -3,16 +3,7 @@ module RescueRegistry
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
|
7
|
-
self.rescue_registry = Registry.new(self)
|
8
|
-
|
9
|
-
class << self
|
10
|
-
delegate :register_exception, to: :rescue_registry
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def rescue_registry
|
15
|
-
self.class.rescue_registry
|
6
|
+
include Context
|
16
7
|
end
|
17
8
|
|
18
9
|
def process_action(*args)
|
@@ -32,17 +23,5 @@ module RescueRegistry
|
|
32
23
|
|
33
24
|
super
|
34
25
|
end
|
35
|
-
|
36
|
-
class_methods do
|
37
|
-
def inherited(subklass)
|
38
|
-
super
|
39
|
-
subklass.rescue_registry = rescue_registry.dup
|
40
|
-
subklass.rescue_registry.owner = subklass
|
41
|
-
end
|
42
|
-
|
43
|
-
def default_exception_handler
|
44
|
-
ExceptionHandler
|
45
|
-
end
|
46
|
-
end
|
47
26
|
end
|
48
27
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module RescueRegistry
|
2
4
|
class ExceptionHandler
|
3
5
|
def self.default_status
|
@@ -8,11 +10,7 @@ module RescueRegistry
|
|
8
10
|
def initialize(exception, **options)
|
9
11
|
@exception = exception
|
10
12
|
|
11
|
-
status = options[:status]
|
12
|
-
if status == :passthrough
|
13
|
-
status = ActionDispatch::ExceptionWrapper.status_code_for_exception_without_rescue_registry(exception.class.name)
|
14
|
-
end
|
15
|
-
@status = status
|
13
|
+
@status = options[:status]
|
16
14
|
|
17
15
|
@title = options[:title]
|
18
16
|
|
@@ -46,10 +44,14 @@ module RescueRegistry
|
|
46
44
|
when Proc
|
47
45
|
@detail.call(exception)
|
48
46
|
else
|
49
|
-
@detail.
|
47
|
+
if @detail.respond_to?(:to_s)
|
48
|
+
val = @detail.to_s
|
49
|
+
# Don't return empty string
|
50
|
+
val.empty? ? nil : val
|
51
|
+
end
|
50
52
|
end
|
51
53
|
|
52
|
-
detail
|
54
|
+
detail || default_detail_for_status
|
53
55
|
end
|
54
56
|
|
55
57
|
def meta
|
@@ -83,20 +85,25 @@ module RescueRegistry
|
|
83
85
|
}
|
84
86
|
end
|
85
87
|
|
86
|
-
|
88
|
+
# `content_type` should be an object with:
|
89
|
+
# * `to_sym` returning sane name for the content_type (e.g. :jsonapi, :json, :xml, :html)
|
90
|
+
# * `to_s` returning the content_type string (e.g. "application/vnd.api+json", "application/json", "text/xml", "text/html")
|
91
|
+
def formatted_response(content_type, fallback: :none, **options)
|
87
92
|
body = build_payload(**options)
|
88
93
|
|
89
94
|
# TODO: Maybe make a helper to register these types?
|
90
|
-
to_format = content_type == :jsonapi ? "to_json" : "to_#{content_type.to_sym}"
|
95
|
+
to_format = content_type.to_sym == :jsonapi ? "to_json" : "to_#{content_type.to_sym}"
|
91
96
|
|
92
97
|
if content_type && body.respond_to?(to_format)
|
93
98
|
formatted_body = body.public_send(to_format)
|
94
99
|
format = content_type
|
95
100
|
else
|
96
|
-
|
101
|
+
case fallback.to_sym
|
102
|
+
when :json
|
97
103
|
formatted_body = body.to_json
|
98
|
-
|
99
|
-
|
104
|
+
# FIXME: This won't work without Rails
|
105
|
+
format = fallback
|
106
|
+
when :none
|
100
107
|
return nil
|
101
108
|
else
|
102
109
|
raise ArgumentError, "unknown fallback=#{fallback}"
|
@@ -5,7 +5,7 @@ module RescueRegistry
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def call(env)
|
8
|
-
request = ActionDispatch::Request.new(env)
|
8
|
+
request = ::ActionDispatch::Request.new(env)
|
9
9
|
exception = request.get_header "action_dispatch.exception"
|
10
10
|
|
11
11
|
if RescueRegistry.handles_exception?(exception)
|
@@ -15,20 +15,23 @@ module RescueRegistry
|
|
15
15
|
content_type = Mime[:text]
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
response = RescueRegistry.response_for_public(content_type, exception)
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
21
|
+
if response
|
22
|
+
status, body, format = response
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
else
|
28
|
-
# If we have no response, it means we couldn't render for the content_type, use the default handler instead
|
29
|
-
@app.call(env)
|
24
|
+
if request.path_info != "/#{status}"
|
25
|
+
warn "status mismatch; path_info=#{request.path_info}; status=#{status}"
|
30
26
|
end
|
27
|
+
|
28
|
+
[status, { "Content-Type" => "#{format}; charset=#{::ActionDispatch::Response.default_charset}",
|
29
|
+
"Content-Length" => body.bytesize.to_s }, [body]]
|
31
30
|
else
|
31
|
+
# If we have no response, it means one of the following:
|
32
|
+
# * RescueRegistry doesn't handle this exception
|
33
|
+
# * RescueRegistry doesn't have a response to render for this content_type.
|
34
|
+
# In either case, we use the default handler instead
|
32
35
|
@app.call(env)
|
33
36
|
end
|
34
37
|
end
|
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rails/railtie'
|
4
|
-
|
5
3
|
module RescueRegistry
|
6
4
|
class Railtie < Rails::Railtie
|
5
|
+
initializer "rescue_registry.add_middleware" do |app|
|
6
|
+
# We add this middleware to ensure that the RescueRegistry.context is properly handled.
|
7
|
+
# The context is set in the controller action and will be available until the Middleware
|
8
|
+
# returns. Any middleware that are before this one will not have access to the context.
|
9
|
+
app.middleware.insert_before ::ActionDispatch::ShowExceptions, RescueRegistry::ResetContext
|
10
|
+
end
|
7
11
|
end
|
8
12
|
end
|
@@ -3,7 +3,7 @@ module RescueRegistry
|
|
3
3
|
attr_accessor :owner
|
4
4
|
|
5
5
|
def initialize(owner)
|
6
|
-
@owner =
|
6
|
+
@owner = owner
|
7
7
|
@handlers = { }
|
8
8
|
end
|
9
9
|
|
@@ -11,16 +11,28 @@ module RescueRegistry
|
|
11
11
|
@handlers = @handlers.dup
|
12
12
|
end
|
13
13
|
|
14
|
+
def passthrough_allowed?
|
15
|
+
defined?(ActionDispatch::ExceptionWrapper)
|
16
|
+
end
|
17
|
+
|
18
|
+
def passthrough_status(exception)
|
19
|
+
::ActionDispatch::ExceptionWrapper.status_code_for_exception(exception.class.name)
|
20
|
+
end
|
21
|
+
|
14
22
|
# TODO: Support a shorthand for handler
|
15
23
|
def register_exception(exception_class, handler: nil, **options)
|
16
24
|
raise ArgumentError, "#{exception_class} is not an Exception" unless exception_class <= Exception
|
17
25
|
|
18
|
-
|
26
|
+
if owner.respond_to?(:default_exception_handler)
|
27
|
+
handler ||= owner.default_exception_handler
|
28
|
+
end
|
19
29
|
raise ArgumentError, "handler must be provided" unless handler
|
20
30
|
|
21
31
|
status = options[:status] ||= handler.default_status
|
22
32
|
raise ArgumentError, "status must be provided" unless status
|
23
|
-
|
33
|
+
unless status.is_a?(Integer) || (passthrough_allowed? && status == :passthrough)
|
34
|
+
raise ArgumentError, "invalid status: #{status}"
|
35
|
+
end
|
24
36
|
|
25
37
|
# TODO: Validate options here
|
26
38
|
|
@@ -33,20 +45,27 @@ module RescueRegistry
|
|
33
45
|
raise HandlerNotFound, "no handler found for #{exception.class}" unless handler_info
|
34
46
|
|
35
47
|
handler_class, handler_options = handler_info
|
48
|
+
|
49
|
+
if handler_options[:status] == :passthrough
|
50
|
+
handler_options = handler_options.merge(status: passthrough_status(exception))
|
51
|
+
end
|
52
|
+
|
36
53
|
handler_class.new(exception, **handler_options)
|
37
54
|
end
|
38
55
|
|
39
56
|
def handles_exception?(exception)
|
40
|
-
handler_info_for_exception(exception).
|
57
|
+
!handler_info_for_exception(exception).nil?
|
41
58
|
end
|
42
59
|
|
43
|
-
def status_code_for_exception(exception)
|
60
|
+
def status_code_for_exception(exception, passthrough: true)
|
44
61
|
_, options = handler_info_for_exception(exception)
|
45
62
|
return unless options
|
46
63
|
|
47
|
-
|
48
|
-
|
49
|
-
|
64
|
+
if options[:status] == :passthrough
|
65
|
+
passthrough ? passthrough_status(exception) : nil
|
66
|
+
else
|
67
|
+
options[:status]
|
68
|
+
end
|
50
69
|
end
|
51
70
|
|
52
71
|
def build_response(content_type, exception, **options)
|
@@ -54,12 +73,12 @@ module RescueRegistry
|
|
54
73
|
handler.formatted_response(content_type, **options)
|
55
74
|
end
|
56
75
|
|
57
|
-
def response_for_debugging(content_type, exception, traces: nil)
|
58
|
-
build_response(content_type, exception, show_details: true, traces: traces)
|
76
|
+
def response_for_debugging(content_type, exception, traces: nil, fallback: :none)
|
77
|
+
build_response(content_type, exception, show_details: true, traces: traces, fallback: fallback)
|
59
78
|
end
|
60
79
|
|
61
|
-
def response_for_public(content_type, exception)
|
62
|
-
build_response(content_type, exception, fallback:
|
80
|
+
def response_for_public(content_type, exception, fallback: :none)
|
81
|
+
build_response(content_type, exception, fallback: fallback)
|
63
82
|
end
|
64
83
|
|
65
84
|
private
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RescueRegistry
|
2
|
+
class ResetContext
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(*args)
|
8
|
+
warn "Didn't expect RescueRegistry context to already be set in middleware" if RescueRegistry.context
|
9
|
+
@app.call(*args)
|
10
|
+
ensure
|
11
|
+
RescueRegistry.context = nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is for use in non-Rails Rack apps. Rails apps will handle exceptions with
|
4
|
+
# ActionDispatch::DebugExceptions and ActionDispatch::ShowExceptions
|
5
|
+
module RescueRegistry
|
6
|
+
class ShowExceptions
|
7
|
+
# Unfortunately, Rail's nice mime types are in ActionDispatch which we'd rather not require
|
8
|
+
CONTENT_TYPES = {
|
9
|
+
jsonapi: "application/vnd.api+json",
|
10
|
+
json: "application/json",
|
11
|
+
xml: ["application/xml", "text/xml"],
|
12
|
+
plain: "text/plain"
|
13
|
+
}
|
14
|
+
|
15
|
+
# Match Rail's Mime::Type API on a basic level
|
16
|
+
MimeType = Struct.new(:to_sym, :to_s)
|
17
|
+
|
18
|
+
def initialize(app, debug: false, content_types: CONTENT_TYPES)
|
19
|
+
@app = app
|
20
|
+
@content_types = content_types
|
21
|
+
@debug = debug
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(env)
|
25
|
+
@app.call(env)
|
26
|
+
rescue Exception => exception
|
27
|
+
handle_exception(env, exception)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def handle_exception(env, exception)
|
33
|
+
if RescueRegistry.handles_exception?(exception)
|
34
|
+
accept = Rack::Utils.best_q_match(env["HTTP_ACCEPT"], @content_types.values.flatten)
|
35
|
+
accept ||= "text/plain"
|
36
|
+
|
37
|
+
symbol = CONTENT_TYPES.find { |(k,v)| Array(v).include?(accept) }.first
|
38
|
+
content_type = MimeType.new(symbol, accept)
|
39
|
+
|
40
|
+
# We need a fallback to ensure that we actually do render something. Outside of Rails, there's no situation where
|
41
|
+
# we would want to pass through on a handled exception.
|
42
|
+
fallback = MimeType.new(:json, CONTENT_TYPES[:json])
|
43
|
+
if @debug
|
44
|
+
response = RescueRegistry.response_for_debugging(content_type, exception, traces: { "Full Trace" => exception.backtrace }, fallback: fallback)
|
45
|
+
else
|
46
|
+
response = RescueRegistry.response_for_public(content_type, exception, fallback: fallback)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
if response
|
51
|
+
status, body, format = response
|
52
|
+
[status, { "Content-Type" => "#{format}", "Content-Length" => body.bytesize.to_s }, [body]]
|
53
|
+
else
|
54
|
+
# Let next middleware handle
|
55
|
+
raise exception
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
metadata
CHANGED
@@ -1,25 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rescue_registry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Wagenet
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '5.0'
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '7.0'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,9 +24,6 @@ dependencies:
|
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '5.0'
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '7.0'
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: appraisal
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,34 +66,6 @@ dependencies:
|
|
72
66
|
- - "~>"
|
73
67
|
- !ruby/object:Gem::Version
|
74
68
|
version: '3.3'
|
75
|
-
- !ruby/object:Gem::Dependency
|
76
|
-
name: rspec-rails
|
77
|
-
requirement: !ruby/object:Gem::Requirement
|
78
|
-
requirements:
|
79
|
-
- - "~>"
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
version: '3.8'
|
82
|
-
type: :development
|
83
|
-
prerelease: false
|
84
|
-
version_requirements: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - "~>"
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '3.8'
|
89
|
-
- !ruby/object:Gem::Dependency
|
90
|
-
name: sqlite3
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
|
-
requirements:
|
93
|
-
- - "~>"
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '1.3'
|
96
|
-
type: :development
|
97
|
-
prerelease: false
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '1.3'
|
103
69
|
- !ruby/object:Gem::Dependency
|
104
70
|
name: yard
|
105
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -127,11 +93,17 @@ files:
|
|
127
93
|
- Rakefile
|
128
94
|
- lib/rescue_registry.rb
|
129
95
|
- lib/rescue_registry/action_dispatch.rb
|
96
|
+
- lib/rescue_registry/action_dispatch/debug_exceptions.rb
|
97
|
+
- lib/rescue_registry/action_dispatch/exception_wrapper.rb
|
98
|
+
- lib/rescue_registry/action_dispatch/show_exceptions.rb
|
99
|
+
- lib/rescue_registry/context.rb
|
130
100
|
- lib/rescue_registry/controller.rb
|
131
101
|
- lib/rescue_registry/exception_handler.rb
|
132
102
|
- lib/rescue_registry/exceptions_app.rb
|
133
103
|
- lib/rescue_registry/railtie.rb
|
134
104
|
- lib/rescue_registry/registry.rb
|
105
|
+
- lib/rescue_registry/reset_context.rb
|
106
|
+
- lib/rescue_registry/show_exceptions.rb
|
135
107
|
- lib/rescue_registry/version.rb
|
136
108
|
- lib/tasks/rescue_registry_tasks.rake
|
137
109
|
homepage: https://github.com/wagenet/rescue_registry
|