actionpack 6.1.7.5 → 7.1.3.1
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.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +355 -435
- data/MIT-LICENSE +2 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +33 -37
- data/lib/abstract_controller/caching/fragments.rb +4 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +50 -11
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +78 -30
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +26 -7
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +12 -10
- data/lib/action_controller/base.rb +8 -21
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +4 -2
- data/lib/action_controller/log_subscriber.rb +20 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +137 -102
- data/lib/action_controller/metal/content_security_policy.rb +37 -3
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +25 -31
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +27 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +5 -16
- data/lib/action_controller/metal/http_authentication.rb +78 -42
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +62 -50
- data/lib/action_controller/metal/live.rb +67 -2
- data/lib/action_controller/metal/mime_responds.rb +5 -5
- data/lib/action_controller/metal/params_wrapper.rb +24 -13
- data/lib/action_controller/metal/permissions_policy.rb +20 -29
- data/lib/action_controller/metal/redirecting.rb +96 -23
- data/lib/action_controller/metal/renderers.rb +14 -15
- data/lib/action_controller/metal/rendering.rb +121 -16
- data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
- data/lib/action_controller/metal/rescue.rb +7 -4
- data/lib/action_controller/metal/streaming.rb +74 -36
- data/lib/action_controller/metal/strong_parameters.rb +254 -151
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +10 -5
- data/lib/action_controller/metal.rb +89 -34
- data/lib/action_controller/railtie.rb +66 -9
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +42 -11
- data/lib/action_controller.rb +10 -6
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +21 -16
- data/lib/action_dispatch/http/content_security_policy.rb +122 -44
- data/lib/action_dispatch/http/filter_parameters.rb +14 -23
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
- data/lib/action_dispatch/http/mime_type.rb +43 -22
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +6 -6
- data/lib/action_dispatch/http/permissions_policy.rb +57 -19
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +75 -51
- data/lib/action_dispatch/http/response.rb +81 -77
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +36 -27
- data/lib/action_dispatch/journey/route.rb +8 -14
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +10 -9
- data/lib/action_dispatch/journey/routes.rb +5 -5
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +97 -107
- data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
- data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +24 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
- data/lib/action_dispatch/middleware/request_id.rb +5 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +34 -11
- data/lib/action_dispatch/middleware/static.rb +16 -16
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
- data/lib/action_dispatch/railtie.rb +20 -4
- data/lib/action_dispatch/request/session.rb +59 -19
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +55 -7
- data/lib/action_dispatch/routing/mapper.rb +117 -107
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +20 -8
- data/lib/action_dispatch/routing/route_set.rb +67 -27
- data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
- data/lib/action_dispatch/routing/url_for.rb +29 -26
- data/lib/action_dispatch/routing.rb +12 -13
- data/lib/action_dispatch/system_test_case.rb +8 -8
- data/lib/action_dispatch/system_testing/browser.rb +20 -29
- data/lib/action_dispatch/system_testing/driver.rb +34 -18
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
- data/lib/action_dispatch/testing/assertions.rb +3 -4
- data/lib/action_dispatch/testing/integration.rb +33 -25
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +5 -30
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +34 -2
- data/lib/action_dispatch.rb +38 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +67 -30
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "action_dispatch/http/request"
|
4
3
|
require "action_dispatch/middleware/exception_wrapper"
|
5
4
|
require "action_dispatch/routing/inspector"
|
6
5
|
|
7
6
|
require "action_view"
|
8
7
|
|
9
8
|
module ActionDispatch
|
9
|
+
# = Action Dispatch \DebugExceptions
|
10
|
+
#
|
10
11
|
# This middleware is responsible for logging exceptions and
|
11
12
|
# showing a debugging page in case the request is local.
|
12
13
|
class DebugExceptions
|
@@ -25,26 +26,26 @@ module ActionDispatch
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def call(env)
|
28
|
-
request = ActionDispatch::Request.new env
|
29
29
|
_, headers, body = response = @app.call(env)
|
30
30
|
|
31
|
-
if headers[
|
31
|
+
if headers[Constants::X_CASCADE] == "pass"
|
32
32
|
body.close if body.respond_to?(:close)
|
33
33
|
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
|
34
34
|
end
|
35
35
|
|
36
36
|
response
|
37
37
|
rescue Exception => exception
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
request = ActionDispatch::Request.new env
|
39
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
40
|
+
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
41
|
+
|
42
|
+
invoke_interceptors(request, exception, wrapper)
|
43
|
+
raise exception unless wrapper.show?(request)
|
44
|
+
render_exception(request, exception, wrapper)
|
41
45
|
end
|
42
46
|
|
43
47
|
private
|
44
|
-
def invoke_interceptors(request, exception)
|
45
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
46
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
47
|
-
|
48
|
+
def invoke_interceptors(request, exception, wrapper)
|
48
49
|
@interceptors.each do |interceptor|
|
49
50
|
interceptor.call(request, exception)
|
50
51
|
rescue Exception
|
@@ -52,9 +53,7 @@ module ActionDispatch
|
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
55
|
-
def render_exception(request, exception)
|
56
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
57
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
56
|
+
def render_exception(request, exception, wrapper)
|
58
57
|
log_error(request, wrapper)
|
59
58
|
|
60
59
|
if request.get_header("action_dispatch.show_detailed_exceptions")
|
@@ -95,7 +94,7 @@ module ActionDispatch
|
|
95
94
|
wrapper.status_code,
|
96
95
|
Rack::Utils::HTTP_STATUS_CODES[500]
|
97
96
|
),
|
98
|
-
exception: wrapper.
|
97
|
+
exception: wrapper.exception_inspect,
|
99
98
|
traces: wrapper.traces
|
100
99
|
}
|
101
100
|
|
@@ -116,48 +115,48 @@ module ActionDispatch
|
|
116
115
|
DebugView.new(
|
117
116
|
request: request,
|
118
117
|
exception_wrapper: wrapper,
|
118
|
+
# Everything should use the wrapper, but we need to pass
|
119
|
+
# `exception` for legacy code.
|
119
120
|
exception: wrapper.exception,
|
120
121
|
traces: wrapper.traces,
|
121
122
|
show_source_idx: wrapper.source_to_show_id,
|
122
123
|
trace_to_show: wrapper.trace_to_show,
|
123
|
-
routes_inspector: routes_inspector(wrapper
|
124
|
+
routes_inspector: routes_inspector(wrapper),
|
124
125
|
source_extracts: wrapper.source_extracts,
|
125
|
-
line_number: wrapper.line_number,
|
126
|
-
file: wrapper.file
|
127
126
|
)
|
128
127
|
end
|
129
128
|
|
130
129
|
def render(status, body, format)
|
131
|
-
[status, {
|
130
|
+
[status, { Rack::CONTENT_TYPE => "#{format}; charset=#{Response.default_charset}", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [body]]
|
132
131
|
end
|
133
132
|
|
134
133
|
def log_error(request, wrapper)
|
135
134
|
logger = logger(request)
|
136
135
|
|
137
136
|
return unless logger
|
137
|
+
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
138
138
|
|
139
|
-
exception = wrapper.exception
|
140
139
|
trace = wrapper.exception_trace
|
141
140
|
|
142
141
|
message = []
|
143
142
|
message << " "
|
144
|
-
message << "#{
|
145
|
-
message.concat(
|
143
|
+
message << "#{wrapper.exception_class_name} (#{wrapper.message}):"
|
144
|
+
message.concat(wrapper.annotated_source_code)
|
146
145
|
message << " "
|
147
146
|
message.concat(trace)
|
148
147
|
|
149
|
-
log_array(logger, message)
|
148
|
+
log_array(logger, message, request)
|
150
149
|
end
|
151
150
|
|
152
|
-
def log_array(logger,
|
153
|
-
lines = Array(array)
|
154
|
-
|
151
|
+
def log_array(logger, lines, request)
|
155
152
|
return if lines.empty?
|
156
153
|
|
154
|
+
level = request.get_header("action_dispatch.debug_exception_log_level")
|
155
|
+
|
157
156
|
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
158
|
-
logger.
|
157
|
+
logger.add(level, lines.join("\n#{logger.formatter.tags_text}"))
|
159
158
|
else
|
160
|
-
logger.
|
159
|
+
logger.add(level, lines.join("\n"))
|
161
160
|
end
|
162
161
|
end
|
163
162
|
|
@@ -170,7 +169,7 @@ module ActionDispatch
|
|
170
169
|
end
|
171
170
|
|
172
171
|
def routes_inspector(exception)
|
173
|
-
if @routes_app.respond_to?(:routes) && (exception.
|
172
|
+
if @routes_app.respond_to?(:routes) && (exception.routing_error? || exception.template_error?)
|
174
173
|
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
|
175
174
|
end
|
176
175
|
end
|
@@ -178,5 +177,9 @@ module ActionDispatch
|
|
178
177
|
def api_request?(content_type)
|
179
178
|
@response_format == :api && !content_type.html?
|
180
179
|
end
|
180
|
+
|
181
|
+
def log_rescued_responses?(request)
|
182
|
+
request.get_header("action_dispatch.log_rescued_responses")
|
183
|
+
end
|
181
184
|
end
|
182
185
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
|
+
# = Action Dispatch \DebugLocks
|
5
|
+
#
|
4
6
|
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
5
7
|
#
|
6
8
|
# To use it, insert it near the top of the middleware stack, using
|
@@ -9,9 +11,9 @@ module ActionDispatch
|
|
9
11
|
# config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
|
10
12
|
#
|
11
13
|
# After restarting the application and re-triggering the deadlock condition,
|
12
|
-
# <tt>/rails/locks</tt> will show a summary of all threads currently
|
13
|
-
# the interlock, which lock level they are holding or awaiting, and
|
14
|
-
# current backtrace.
|
14
|
+
# the route <tt>/rails/locks</tt> will show a summary of all threads currently
|
15
|
+
# known to the interlock, which lock level they are holding or awaiting, and
|
16
|
+
# their current backtrace.
|
15
17
|
#
|
16
18
|
# Generally a deadlock will be caused by the interlock conflicting with some
|
17
19
|
# other external lock or blocking I/O call. These cannot be automatically
|
@@ -97,7 +99,8 @@ module ActionDispatch
|
|
97
99
|
msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
|
98
100
|
end.join("\n\n---\n\n\n")
|
99
101
|
|
100
|
-
[200, {
|
102
|
+
[200, { Rack::CONTENT_TYPE => "text/plain; charset=#{ActionDispatch::Response.default_charset}",
|
103
|
+
Rack::CONTENT_LENGTH => str.size.to_s }, [str]]
|
101
104
|
end
|
102
105
|
|
103
106
|
def blocked_by?(victim, blocker, all_threads)
|
@@ -7,18 +7,23 @@ require "action_view/base"
|
|
7
7
|
|
8
8
|
module ActionDispatch
|
9
9
|
class DebugView < ActionView::Base # :nodoc:
|
10
|
-
|
10
|
+
RESCUES_TEMPLATE_PATHS = [File.expand_path("templates", __dir__)]
|
11
11
|
|
12
12
|
def initialize(assigns)
|
13
|
-
paths =
|
13
|
+
paths = RESCUES_TEMPLATE_PATHS.dup
|
14
14
|
lookup_context = ActionView::LookupContext.new(paths)
|
15
15
|
super(lookup_context, assigns, nil)
|
16
|
+
@exception_wrapper = assigns[:exception_wrapper]
|
16
17
|
end
|
17
18
|
|
18
19
|
def compiled_method_container
|
19
20
|
self.class
|
20
21
|
end
|
21
22
|
|
23
|
+
def error_highlight_available?
|
24
|
+
@exception_wrapper.error_highlight_available?
|
25
|
+
end
|
26
|
+
|
22
27
|
def debug_params(params)
|
23
28
|
clean_params = params.clone
|
24
29
|
clean_params.delete("action")
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/module/attribute_accessors"
|
4
|
+
require "active_support/syntax_error_proxy"
|
5
|
+
require "active_support/core_ext/thread/backtrace/location"
|
4
6
|
require "rack/utils"
|
5
7
|
|
6
8
|
module ActionDispatch
|
@@ -41,22 +43,76 @@ module ActionDispatch
|
|
41
43
|
"ActionDispatch::Http::MimeNegotiation::InvalidType"
|
42
44
|
]
|
43
45
|
|
44
|
-
attr_reader :backtrace_cleaner, :
|
46
|
+
attr_reader :backtrace_cleaner, :wrapped_causes, :exception_class_name, :exception
|
45
47
|
|
46
48
|
def initialize(backtrace_cleaner, exception)
|
47
49
|
@backtrace_cleaner = backtrace_cleaner
|
48
|
-
@
|
49
|
-
@exception_class_name = @exception.class.name
|
50
|
+
@exception_class_name = exception.class.name
|
50
51
|
@wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
|
52
|
+
@exception = exception
|
53
|
+
if exception.is_a?(SyntaxError)
|
54
|
+
@exception = ActiveSupport::SyntaxErrorProxy.new(exception)
|
55
|
+
end
|
56
|
+
@backtrace = build_backtrace
|
57
|
+
end
|
58
|
+
|
59
|
+
def routing_error?
|
60
|
+
@exception.is_a?(ActionController::RoutingError)
|
61
|
+
end
|
62
|
+
|
63
|
+
def template_error?
|
64
|
+
@exception.is_a?(ActionView::Template::Error)
|
65
|
+
end
|
66
|
+
|
67
|
+
def sub_template_message
|
68
|
+
@exception.sub_template_message
|
69
|
+
end
|
70
|
+
|
71
|
+
def has_cause?
|
72
|
+
@exception.cause
|
73
|
+
end
|
74
|
+
|
75
|
+
def failures
|
76
|
+
@exception.failures
|
77
|
+
end
|
78
|
+
|
79
|
+
def has_corrections?
|
80
|
+
@exception.respond_to?(:original_message) && @exception.respond_to?(:corrections)
|
81
|
+
end
|
82
|
+
|
83
|
+
def original_message
|
84
|
+
@exception.original_message
|
85
|
+
end
|
86
|
+
|
87
|
+
def corrections
|
88
|
+
@exception.corrections
|
89
|
+
end
|
90
|
+
|
91
|
+
def file_name
|
92
|
+
@exception.file_name
|
93
|
+
end
|
94
|
+
|
95
|
+
def line_number
|
96
|
+
@exception.line_number
|
97
|
+
end
|
51
98
|
|
52
|
-
|
99
|
+
def actions
|
100
|
+
ActiveSupport::ActionableError.actions(@exception)
|
53
101
|
end
|
54
102
|
|
55
103
|
def unwrapped_exception
|
56
104
|
if wrapper_exceptions.include?(@exception_class_name)
|
57
|
-
exception.cause
|
105
|
+
@exception.cause
|
58
106
|
else
|
59
|
-
exception
|
107
|
+
@exception
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def annotated_source_code
|
112
|
+
if exception.respond_to?(:annotated_source_code)
|
113
|
+
exception.annotated_source_code
|
114
|
+
else
|
115
|
+
[]
|
60
116
|
end
|
61
117
|
end
|
62
118
|
|
@@ -118,17 +174,44 @@ module ActionDispatch
|
|
118
174
|
Rack::Utils.status_code(@@rescue_responses[class_name])
|
119
175
|
end
|
120
176
|
|
177
|
+
def show?(request)
|
178
|
+
# We're treating `nil` as "unset", and we want the default setting to be
|
179
|
+
# `:all`. This logic should be extracted to `env_config` and calculated
|
180
|
+
# once.
|
181
|
+
config = request.get_header("action_dispatch.show_exceptions")
|
182
|
+
|
183
|
+
# Include true and false for backwards compatibility.
|
184
|
+
case config
|
185
|
+
when :none
|
186
|
+
false
|
187
|
+
when :rescuable
|
188
|
+
rescue_response?
|
189
|
+
when true
|
190
|
+
ActionDispatch.deprecator.warn("Setting action_dispatch.show_exceptions to true is deprecated. Set to :all instead.")
|
191
|
+
true
|
192
|
+
when false
|
193
|
+
ActionDispatch.deprecator.warn("Setting action_dispatch.show_exceptions to false is deprecated. Set to :none instead.")
|
194
|
+
false
|
195
|
+
else
|
196
|
+
true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def rescue_response?
|
201
|
+
@@rescue_responses.key?(exception.class.name)
|
202
|
+
end
|
203
|
+
|
121
204
|
def source_extracts
|
122
205
|
backtrace.map do |trace|
|
123
|
-
|
124
|
-
|
125
|
-
{
|
126
|
-
code: source_fragment(file, line_number),
|
127
|
-
line_number: line_number
|
128
|
-
}
|
206
|
+
extract_source(trace)
|
129
207
|
end
|
130
208
|
end
|
131
209
|
|
210
|
+
def error_highlight_available?
|
211
|
+
# ErrorHighlight.spot with backtrace_location keyword is available since error_highlight 0.4.0
|
212
|
+
defined?(ErrorHighlight) && Gem::Version.new(ErrorHighlight::VERSION) >= Gem::Version.new("0.4.0")
|
213
|
+
end
|
214
|
+
|
132
215
|
def trace_to_show
|
133
216
|
if traces["Application Trace"].empty? && rescue_template != "routing_error"
|
134
217
|
"Full Trace"
|
@@ -141,9 +224,65 @@ module ActionDispatch
|
|
141
224
|
(traces[trace_to_show].first || {})[:id]
|
142
225
|
end
|
143
226
|
|
227
|
+
def exception_name
|
228
|
+
exception.cause.class.to_s
|
229
|
+
end
|
230
|
+
|
231
|
+
def message
|
232
|
+
exception.message
|
233
|
+
end
|
234
|
+
|
235
|
+
def exception_inspect
|
236
|
+
exception.inspect
|
237
|
+
end
|
238
|
+
|
239
|
+
def exception_id
|
240
|
+
exception.object_id
|
241
|
+
end
|
242
|
+
|
144
243
|
private
|
145
|
-
|
146
|
-
|
244
|
+
class SourceMapLocation < DelegateClass(Thread::Backtrace::Location) # :nodoc:
|
245
|
+
def initialize(location, template)
|
246
|
+
super(location)
|
247
|
+
@template = template
|
248
|
+
end
|
249
|
+
|
250
|
+
def spot(exc)
|
251
|
+
if RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) && __getobj__.is_a?(Thread::Backtrace::Location)
|
252
|
+
location = @template.spot(__getobj__)
|
253
|
+
else
|
254
|
+
location = super
|
255
|
+
end
|
256
|
+
|
257
|
+
if location
|
258
|
+
@template.translate_location(__getobj__, location)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
attr_reader :backtrace
|
264
|
+
|
265
|
+
def build_backtrace
|
266
|
+
built_methods = {}
|
267
|
+
|
268
|
+
ActionView::PathRegistry.all_resolvers.each do |resolver|
|
269
|
+
resolver.built_templates.each do |template|
|
270
|
+
built_methods[template.method_name] = template
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
(@exception.backtrace_locations || []).map do |loc|
|
275
|
+
if built_methods.key?(loc.label.to_s)
|
276
|
+
thread_backtrace_location = if loc.respond_to?(:__getobj__)
|
277
|
+
loc.__getobj__
|
278
|
+
else
|
279
|
+
loc
|
280
|
+
end
|
281
|
+
SourceMapLocation.new(thread_backtrace_location, built_methods[loc.label.to_s])
|
282
|
+
else
|
283
|
+
loc
|
284
|
+
end
|
285
|
+
end
|
147
286
|
end
|
148
287
|
|
149
288
|
def causes_for(exception)
|
@@ -164,29 +303,53 @@ module ActionDispatch
|
|
164
303
|
end
|
165
304
|
end
|
166
305
|
|
306
|
+
def extract_source(trace)
|
307
|
+
spot = trace.spot(@exception)
|
308
|
+
|
309
|
+
if spot
|
310
|
+
line = spot[:first_lineno]
|
311
|
+
code = extract_source_fragment_lines(spot[:script_lines], line)
|
312
|
+
|
313
|
+
if line == spot[:last_lineno]
|
314
|
+
code[line] = [
|
315
|
+
code[line][0, spot[:first_column]],
|
316
|
+
code[line][spot[:first_column]...spot[:last_column]],
|
317
|
+
code[line][spot[:last_column]..-1],
|
318
|
+
]
|
319
|
+
end
|
320
|
+
|
321
|
+
return {
|
322
|
+
code: code,
|
323
|
+
line_number: line
|
324
|
+
}
|
325
|
+
end
|
326
|
+
|
327
|
+
file, line_number = extract_file_and_line_number(trace)
|
328
|
+
|
329
|
+
{
|
330
|
+
code: source_fragment(file, line_number),
|
331
|
+
line_number: line_number
|
332
|
+
}
|
333
|
+
end
|
334
|
+
|
335
|
+
def extract_source_fragment_lines(source_lines, line)
|
336
|
+
start = [line - 3, 0].max
|
337
|
+
lines = source_lines.drop(start).take(6)
|
338
|
+
Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
|
339
|
+
end
|
340
|
+
|
167
341
|
def source_fragment(path, line)
|
168
342
|
return unless Rails.respond_to?(:root) && Rails.root
|
169
343
|
full_path = Rails.root.join(path)
|
170
344
|
if File.exist?(full_path)
|
171
345
|
File.open(full_path, "r") do |file|
|
172
|
-
|
173
|
-
lines = file.each_line.drop(start).take(6)
|
174
|
-
Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
|
346
|
+
extract_source_fragment_lines(file.each_line, line)
|
175
347
|
end
|
176
348
|
end
|
177
349
|
end
|
178
350
|
|
179
351
|
def extract_file_and_line_number(trace)
|
180
|
-
|
181
|
-
# Windows and Unix path styles.
|
182
|
-
file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
|
183
|
-
[file, line.to_i]
|
184
|
-
end
|
185
|
-
|
186
|
-
def expand_backtrace
|
187
|
-
@exception.backtrace.unshift(
|
188
|
-
@exception.to_s.split("\n")
|
189
|
-
).flatten!
|
352
|
+
[trace.path, trace.lineno]
|
190
353
|
end
|
191
354
|
end
|
192
355
|
end
|
@@ -13,6 +13,9 @@ module ActionDispatch
|
|
13
13
|
begin
|
14
14
|
response = @app.call(env)
|
15
15
|
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
16
|
+
rescue => error
|
17
|
+
@executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
|
18
|
+
raise
|
16
19
|
ensure
|
17
20
|
state.complete! unless returned
|
18
21
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require "active_support/core_ext/hash/keys"
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
|
+
# = Action Dispatch \Flash
|
7
|
+
#
|
6
8
|
# The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
|
7
9
|
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
|
8
10
|
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
|
@@ -20,10 +22,11 @@ module ActionDispatch
|
|
20
22
|
# end
|
21
23
|
# end
|
22
24
|
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
25
|
+
# Then in +show.html.erb+:
|
26
|
+
#
|
27
|
+
# <% if flash[:notice] %>
|
28
|
+
# <div class="notice"><%= flash[:notice] %></div>
|
29
|
+
# <% end %>
|
27
30
|
#
|
28
31
|
# Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
|
29
32
|
#
|
@@ -41,9 +44,9 @@ module ActionDispatch
|
|
41
44
|
KEY = "action_dispatch.request.flash_hash"
|
42
45
|
|
43
46
|
module RequestMethods
|
44
|
-
# Access the contents of the flash.
|
45
|
-
#
|
46
|
-
#
|
47
|
+
# Access the contents of the flash. Returns a ActionDispatch::Flash::FlashHash.
|
48
|
+
#
|
49
|
+
# See ActionDispatch::Flash for example usage.
|
47
50
|
def flash
|
48
51
|
flash = flash_hash
|
49
52
|
return flash if flash
|
@@ -59,16 +62,14 @@ module ActionDispatch
|
|
59
62
|
end
|
60
63
|
|
61
64
|
def commit_flash # :nodoc:
|
62
|
-
session
|
63
|
-
flash_hash = self.flash_hash
|
65
|
+
return unless session.enabled?
|
64
66
|
|
65
67
|
if flash_hash && (flash_hash.present? || session.key?("flash"))
|
66
68
|
session["flash"] = flash_hash.to_session_value
|
67
69
|
self.flash = flash_hash.dup
|
68
70
|
end
|
69
71
|
|
70
|
-
if
|
71
|
-
session.key?("flash") && session["flash"].nil?
|
72
|
+
if session.loaded? && session.key?("flash") && session["flash"].nil?
|
72
73
|
session.delete("flash")
|
73
74
|
end
|
74
75
|
end
|
@@ -79,7 +80,7 @@ module ActionDispatch
|
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
82
|
-
class FlashNow
|
83
|
+
class FlashNow # :nodoc:
|
83
84
|
attr_accessor :flash
|
84
85
|
|
85
86
|
def initialize(flash)
|
@@ -111,7 +112,7 @@ module ActionDispatch
|
|
111
112
|
class FlashHash
|
112
113
|
include Enumerable
|
113
114
|
|
114
|
-
def self.from_session_value(value)
|
115
|
+
def self.from_session_value(value) # :nodoc:
|
115
116
|
case value
|
116
117
|
when FlashHash # Rails 3.1, 3.2
|
117
118
|
flashes = value.instance_variable_get(:@flashes)
|
@@ -132,13 +133,13 @@ module ActionDispatch
|
|
132
133
|
|
133
134
|
# Builds a hash containing the flashes to keep for the next request.
|
134
135
|
# If there are none to keep, returns +nil+.
|
135
|
-
def to_session_value
|
136
|
+
def to_session_value # :nodoc:
|
136
137
|
flashes_to_keep = @flashes.except(*@discard)
|
137
138
|
return nil if flashes_to_keep.empty?
|
138
139
|
{ "discard" => [], "flashes" => flashes_to_keep }
|
139
140
|
end
|
140
141
|
|
141
|
-
def initialize(flashes = {}, discard = [])
|
142
|
+
def initialize(flashes = {}, discard = []) # :nodoc:
|
142
143
|
@discard = Set.new(stringify_array(discard))
|
143
144
|
@flashes = flashes.stringify_keys
|
144
145
|
@now = nil
|
@@ -162,7 +163,7 @@ module ActionDispatch
|
|
162
163
|
@flashes[k.to_s]
|
163
164
|
end
|
164
165
|
|
165
|
-
def update(h)
|
166
|
+
def update(h) # :nodoc:
|
166
167
|
@discard.subtract stringify_array(h.keys)
|
167
168
|
@flashes.update h.stringify_keys
|
168
169
|
self
|
@@ -176,6 +177,8 @@ module ActionDispatch
|
|
176
177
|
@flashes.key? name.to_s
|
177
178
|
end
|
178
179
|
|
180
|
+
# Immediately deletes the single flash entry. Use this method when you
|
181
|
+
# want remove the message within the current action. See also #discard.
|
179
182
|
def delete(key)
|
180
183
|
key = key.to_s
|
181
184
|
@discard.delete key
|
@@ -202,7 +205,7 @@ module ActionDispatch
|
|
202
205
|
|
203
206
|
alias :merge! :update
|
204
207
|
|
205
|
-
def replace(h)
|
208
|
+
def replace(h) # :nodoc:
|
206
209
|
@discard.clear
|
207
210
|
@flashes.replace h.stringify_keys
|
208
211
|
self
|
@@ -244,6 +247,9 @@ module ActionDispatch
|
|
244
247
|
#
|
245
248
|
# flash.discard # discard the entire flash at the end of the current action
|
246
249
|
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
|
250
|
+
#
|
251
|
+
# Use this method when you want to display the message in the current
|
252
|
+
# action but not in the next one. See also #delete.
|
247
253
|
def discard(k = nil)
|
248
254
|
k = k.to_s if k
|
249
255
|
@discard.merge Array(k || keys)
|
@@ -253,7 +259,7 @@ module ActionDispatch
|
|
253
259
|
# Mark for removal entries that were kept, and delete unkept ones.
|
254
260
|
#
|
255
261
|
# This method is called automatically by filters, so you generally don't need to care about it.
|
256
|
-
def sweep
|
262
|
+
def sweep # :nodoc:
|
257
263
|
@discard.each { |k| @flashes.delete k }
|
258
264
|
@discard.replace @flashes.keys
|
259
265
|
end
|