actionpack 4.2.10 → 7.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (202) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +86 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -14
  5. data/lib/abstract_controller/asset_paths.rb +5 -1
  6. data/lib/abstract_controller/base.rb +166 -136
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +126 -57
  10. data/lib/abstract_controller/collector.rb +13 -15
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +181 -132
  14. data/lib/abstract_controller/logger.rb +5 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
  16. data/lib/abstract_controller/rendering.rb +56 -56
  17. data/lib/abstract_controller/translation.rb +29 -15
  18. data/lib/abstract_controller/url_for.rb +15 -11
  19. data/lib/abstract_controller.rb +21 -5
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +154 -0
  22. data/lib/action_controller/base.rb +219 -155
  23. data/lib/action_controller/caching.rb +28 -68
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +35 -22
  27. data/lib/action_controller/metal/allow_browser.rb +119 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +259 -122
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +9 -5
  32. data/lib/action_controller/metal/data_streaming.rb +87 -104
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
  36. data/lib/action_controller/metal/exceptions.rb +71 -24
  37. data/lib/action_controller/metal/flash.rb +26 -19
  38. data/lib/action_controller/metal/head.rb +45 -36
  39. data/lib/action_controller/metal/helpers.rb +80 -64
  40. data/lib/action_controller/metal/http_authentication.rb +297 -244
  41. data/lib/action_controller/metal/implicit_render.rb +57 -9
  42. data/lib/action_controller/metal/instrumentation.rb +76 -64
  43. data/lib/action_controller/metal/live.rb +238 -176
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +177 -166
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +145 -118
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +203 -64
  51. data/lib/action_controller/metal/renderers.rb +108 -65
  52. data/lib/action_controller/metal/rendering.rb +216 -56
  53. data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
  54. data/lib/action_controller/metal/rescue.rb +19 -21
  55. data/lib/action_controller/metal/streaming.rb +179 -138
  56. data/lib/action_controller/metal/strong_parameters.rb +1058 -382
  57. data/lib/action_controller/metal/testing.rb +11 -17
  58. data/lib/action_controller/metal/url_for.rb +37 -21
  59. data/lib/action_controller/metal.rb +236 -138
  60. data/lib/action_controller/railtie.rb +89 -11
  61. data/lib/action_controller/railties/helpers.rb +5 -1
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +425 -497
  65. data/lib/action_controller.rb +44 -22
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +119 -63
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +364 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +36 -34
  72. data/lib/action_dispatch/http/filter_redirect.rb +24 -12
  73. data/lib/action_dispatch/http/headers.rb +66 -31
  74. data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
  75. data/lib/action_dispatch/http/mime_type.rb +196 -136
  76. data/lib/action_dispatch/http/mime_types.rb +25 -7
  77. data/lib/action_dispatch/http/parameters.rb +97 -45
  78. data/lib/action_dispatch/http/permissions_policy.rb +187 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +6 -0
  80. data/lib/action_dispatch/http/request.rb +299 -170
  81. data/lib/action_dispatch/http/response.rb +311 -160
  82. data/lib/action_dispatch/http/upload.rb +52 -23
  83. data/lib/action_dispatch/http/url.rb +201 -125
  84. data/lib/action_dispatch/journey/formatter.rb +110 -50
  85. data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
  88. data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
  89. data/lib/action_dispatch/journey/nodes/node.rb +100 -20
  90. data/lib/action_dispatch/journey/parser.rb +19 -17
  91. data/lib/action_dispatch/journey/parser.y +4 -3
  92. data/lib/action_dispatch/journey/parser_extras.rb +14 -4
  93. data/lib/action_dispatch/journey/path/pattern.rb +79 -63
  94. data/lib/action_dispatch/journey/route.rb +108 -44
  95. data/lib/action_dispatch/journey/router/utils.rb +41 -29
  96. data/lib/action_dispatch/journey/router.rb +64 -57
  97. data/lib/action_dispatch/journey/routes.rb +23 -21
  98. data/lib/action_dispatch/journey/scanner.rb +28 -17
  99. data/lib/action_dispatch/journey/visitors.rb +100 -54
  100. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  101. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  102. data/lib/action_dispatch/journey.rb +7 -5
  103. data/lib/action_dispatch/log_subscriber.rb +25 -0
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  106. data/lib/action_dispatch/middleware/callbacks.rb +7 -6
  107. data/lib/action_dispatch/middleware/cookies.rb +471 -328
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
  109. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  110. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
  112. data/lib/action_dispatch/middleware/executor.rb +32 -0
  113. data/lib/action_dispatch/middleware/flash.rb +143 -101
  114. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
  116. data/lib/action_dispatch/middleware/reloader.rb +10 -92
  117. data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
  118. data/lib/action_dispatch/middleware/request_id.rb +29 -15
  119. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
  125. data/lib/action_dispatch/middleware/ssl.rb +134 -36
  126. data/lib/action_dispatch/middleware/stack.rb +109 -44
  127. data/lib/action_dispatch/middleware/static.rb +159 -90
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
  132. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +6 -6
  146. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
  147. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
  148. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  149. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  150. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  151. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
  152. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
  153. data/lib/action_dispatch/railtie.rb +44 -16
  154. data/lib/action_dispatch/request/session.rb +159 -69
  155. data/lib/action_dispatch/request/utils.rb +97 -23
  156. data/lib/action_dispatch/routing/endpoint.rb +11 -2
  157. data/lib/action_dispatch/routing/inspector.rb +195 -106
  158. data/lib/action_dispatch/routing/mapper.rb +1338 -955
  159. data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
  160. data/lib/action_dispatch/routing/redirection.rb +78 -51
  161. data/lib/action_dispatch/routing/route_set.rb +460 -374
  162. data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
  163. data/lib/action_dispatch/routing/url_for.rb +172 -124
  164. data/lib/action_dispatch/routing.rb +159 -158
  165. data/lib/action_dispatch/system_test_case.rb +206 -0
  166. data/lib/action_dispatch/system_testing/browser.rb +84 -0
  167. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  168. data/lib/action_dispatch/system_testing/server.rb +33 -0
  169. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  170. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  171. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  172. data/lib/action_dispatch/testing/assertions/response.rb +71 -39
  173. data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
  174. data/lib/action_dispatch/testing/assertions.rb +9 -6
  175. data/lib/action_dispatch/testing/integration.rb +486 -306
  176. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  177. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  178. data/lib/action_dispatch/testing/test_process.rb +35 -22
  179. data/lib/action_dispatch/testing/test_request.rb +29 -34
  180. data/lib/action_dispatch/testing/test_response.rb +48 -15
  181. data/lib/action_dispatch.rb +82 -40
  182. data/lib/action_pack/gem_version.rb +8 -4
  183. data/lib/action_pack/version.rb +6 -2
  184. data/lib/action_pack.rb +21 -18
  185. metadata +146 -56
  186. data/lib/action_controller/caching/fragments.rb +0 -103
  187. data/lib/action_controller/metal/force_ssl.rb +0 -97
  188. data/lib/action_controller/metal/hide_actions.rb +0 -40
  189. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  190. data/lib/action_controller/middleware.rb +0 -39
  191. data/lib/action_controller/model_naming.rb +0 -12
  192. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  193. data/lib/action_dispatch/journey/backwards.rb +0 -5
  194. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  195. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  196. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  197. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  199. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
  200. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  201. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  202. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,48 +1,135 @@
1
- require 'action_controller/metal/exceptions'
2
- require 'active_support/core_ext/module/attribute_accessors'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+ require "active_support/syntax_error_proxy"
7
+ require "active_support/core_ext/thread/backtrace/location"
8
+ require "rack/utils"
3
9
 
4
10
  module ActionDispatch
5
11
  class ExceptionWrapper
6
- cattr_accessor :rescue_responses
7
- @@rescue_responses = Hash.new(:internal_server_error)
8
- @@rescue_responses.merge!(
9
- 'ActionController::RoutingError' => :not_found,
10
- 'AbstractController::ActionNotFound' => :not_found,
11
- 'ActionController::MethodNotAllowed' => :method_not_allowed,
12
- 'ActionController::UnknownHttpMethod' => :method_not_allowed,
13
- 'ActionController::NotImplemented' => :not_implemented,
14
- 'ActionController::UnknownFormat' => :not_acceptable,
15
- 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
16
- 'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity,
17
- 'ActionDispatch::ParamsParser::ParseError' => :bad_request,
18
- 'ActionController::BadRequest' => :bad_request,
19
- 'ActionController::ParameterMissing' => :bad_request
12
+ cattr_accessor :rescue_responses, default: Hash.new(:internal_server_error).merge!(
13
+ "ActionController::RoutingError" => :not_found,
14
+ "AbstractController::ActionNotFound" => :not_found,
15
+ "ActionController::MethodNotAllowed" => :method_not_allowed,
16
+ "ActionController::UnknownHttpMethod" => :method_not_allowed,
17
+ "ActionController::NotImplemented" => :not_implemented,
18
+ "ActionController::UnknownFormat" => :not_acceptable,
19
+ "ActionDispatch::Http::MimeNegotiation::InvalidType" => :not_acceptable,
20
+ "ActionController::MissingExactTemplate" => :not_acceptable,
21
+ "ActionController::InvalidAuthenticityToken" => :unprocessable_entity,
22
+ "ActionController::InvalidCrossOriginRequest" => :unprocessable_entity,
23
+ "ActionDispatch::Http::Parameters::ParseError" => :bad_request,
24
+ "ActionController::BadRequest" => :bad_request,
25
+ "ActionController::ParameterMissing" => :bad_request,
26
+ "Rack::QueryParser::ParameterTypeError" => :bad_request,
27
+ "Rack::QueryParser::InvalidParameterError" => :bad_request
20
28
  )
21
29
 
22
- cattr_accessor :rescue_templates
23
- @@rescue_templates = Hash.new('diagnostics')
24
- @@rescue_templates.merge!(
25
- 'ActionView::MissingTemplate' => 'missing_template',
26
- 'ActionController::RoutingError' => 'routing_error',
27
- 'AbstractController::ActionNotFound' => 'unknown_action',
28
- 'ActionView::Template::Error' => 'template_error'
30
+ cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
31
+ "ActionView::MissingTemplate" => "missing_template",
32
+ "ActionController::RoutingError" => "routing_error",
33
+ "AbstractController::ActionNotFound" => "unknown_action",
34
+ "ActiveRecord::StatementInvalid" => "invalid_statement",
35
+ "ActionView::Template::Error" => "template_error",
36
+ "ActionController::MissingExactTemplate" => "missing_exact_template",
29
37
  )
30
38
 
31
- attr_reader :env, :exception, :line_number, :file
39
+ cattr_accessor :wrapper_exceptions, default: [
40
+ "ActionView::Template::Error"
41
+ ]
32
42
 
33
- def initialize(env, exception)
34
- @env = env
35
- @exception = original_exception(exception)
43
+ cattr_accessor :silent_exceptions, default: [
44
+ "ActionController::RoutingError",
45
+ "ActionDispatch::Http::MimeNegotiation::InvalidType"
46
+ ]
36
47
 
37
- expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError)
48
+ attr_reader :backtrace_cleaner, :wrapped_causes, :exception_class_name, :exception
49
+
50
+ def initialize(backtrace_cleaner, exception)
51
+ @backtrace_cleaner = backtrace_cleaner
52
+ @exception_class_name = exception.class.name
53
+ @wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
54
+ @exception = exception
55
+ if exception.is_a?(SyntaxError)
56
+ @exception = ActiveSupport::SyntaxErrorProxy.new(exception)
57
+ end
58
+ @backtrace = build_backtrace
59
+ end
60
+
61
+ def routing_error?
62
+ @exception.is_a?(ActionController::RoutingError)
63
+ end
64
+
65
+ def template_error?
66
+ @exception.is_a?(ActionView::Template::Error)
67
+ end
68
+
69
+ def sub_template_message
70
+ @exception.sub_template_message
71
+ end
72
+
73
+ def has_cause?
74
+ @exception.cause
75
+ end
76
+
77
+ def failures
78
+ @exception.failures
79
+ end
80
+
81
+ def has_corrections?
82
+ @exception.respond_to?(:original_message) && @exception.respond_to?(:corrections)
83
+ end
84
+
85
+ def original_message
86
+ @exception.original_message
87
+ end
88
+
89
+ def corrections
90
+ @exception.corrections
91
+ end
92
+
93
+ def file_name
94
+ @exception.file_name
95
+ end
96
+
97
+ def line_number
98
+ @exception.line_number
99
+ end
100
+
101
+ def actions
102
+ ActiveSupport::ActionableError.actions(@exception)
103
+ end
104
+
105
+ def unwrapped_exception
106
+ if wrapper_exceptions.include?(@exception_class_name)
107
+ @exception.cause
108
+ else
109
+ @exception
110
+ end
111
+ end
112
+
113
+ def annotated_source_code
114
+ if exception.respond_to?(:annotated_source_code)
115
+ exception.annotated_source_code
116
+ else
117
+ []
118
+ end
38
119
  end
39
120
 
40
121
  def rescue_template
41
- @@rescue_templates[@exception.class.name]
122
+ @@rescue_templates[@exception_class_name]
42
123
  end
43
124
 
44
125
  def status_code
45
- self.class.status_code_for_exception(@exception.class.name)
126
+ self.class.status_code_for_exception(unwrapped_exception.class.name)
127
+ end
128
+
129
+ def exception_trace
130
+ trace = application_trace
131
+ trace = framework_trace if trace.empty? && !silent_exceptions.include?(@exception_class_name)
132
+ trace
46
133
  end
47
134
 
48
135
  def application_trace
@@ -58,15 +145,19 @@ module ActionDispatch
58
145
  end
59
146
 
60
147
  def traces
61
- appplication_trace_with_ids = []
148
+ application_trace_with_ids = []
62
149
  framework_trace_with_ids = []
63
150
  full_trace_with_ids = []
64
151
 
65
152
  full_trace.each_with_index do |trace, idx|
66
- trace_with_id = { id: idx, trace: trace }
153
+ trace_with_id = {
154
+ exception_object_id: @exception.object_id,
155
+ id: idx,
156
+ trace: trace
157
+ }
67
158
 
68
159
  if application_trace.include?(trace)
69
- appplication_trace_with_ids << trace_with_id
160
+ application_trace_with_ids << trace_with_id
70
161
  else
71
162
  framework_trace_with_ids << trace_with_id
72
163
  end
@@ -75,7 +166,7 @@ module ActionDispatch
75
166
  end
76
167
 
77
168
  {
78
- "Application Trace" => appplication_trace_with_ids,
169
+ "Application Trace" => application_trace_with_ids,
79
170
  "Framework Trace" => framework_trace_with_ids,
80
171
  "Full Trace" => full_trace_with_ids
81
172
  }
@@ -85,64 +176,175 @@ module ActionDispatch
85
176
  Rack::Utils.status_code(@@rescue_responses[class_name])
86
177
  end
87
178
 
88
- def source_extracts
89
- backtrace.map do |trace|
90
- file, line = trace.split(":")
91
- line_number = line.to_i
179
+ def show?(request)
180
+ # We're treating `nil` as "unset", and we want the default setting to be `:all`.
181
+ # This logic should be extracted to `env_config` and calculated once.
182
+ config = request.get_header("action_dispatch.show_exceptions")
92
183
 
93
- {
94
- code: source_fragment(file, line_number),
95
- line_number: line_number
96
- }
184
+ case config
185
+ when :none
186
+ false
187
+ when :rescuable
188
+ rescue_response?
189
+ else
190
+ true
97
191
  end
98
192
  end
99
193
 
100
- private
101
-
102
- def backtrace
103
- Array(@exception.backtrace)
194
+ def rescue_response?
195
+ @@rescue_responses.key?(exception.class.name)
104
196
  end
105
197
 
106
- def original_exception(exception)
107
- if registered_original_exception?(exception)
108
- exception.original_exception
109
- else
110
- exception
198
+ def source_extracts
199
+ backtrace.map do |trace|
200
+ extract_source(trace)
111
201
  end
112
202
  end
113
203
 
114
- def registered_original_exception?(exception)
115
- exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
204
+ def error_highlight_available?
205
+ # ErrorHighlight.spot with backtrace_location keyword is available since
206
+ # error_highlight 0.4.0
207
+ defined?(ErrorHighlight) && Gem::Version.new(ErrorHighlight::VERSION) >= Gem::Version.new("0.4.0")
116
208
  end
117
209
 
118
- def clean_backtrace(*args)
119
- if backtrace_cleaner
120
- backtrace_cleaner.clean(backtrace, *args)
210
+ def trace_to_show
211
+ if traces["Application Trace"].empty? && rescue_template != "routing_error"
212
+ "Full Trace"
121
213
  else
122
- backtrace
214
+ "Application Trace"
123
215
  end
124
216
  end
125
217
 
126
- def backtrace_cleaner
127
- @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
218
+ def source_to_show_id
219
+ (traces[trace_to_show].first || {})[:id]
128
220
  end
129
221
 
130
- def source_fragment(path, line)
131
- return unless Rails.respond_to?(:root) && Rails.root
132
- full_path = Rails.root.join(path)
133
- if File.exist?(full_path)
134
- File.open(full_path, "r") do |file|
135
- start = [line - 3, 0].max
136
- lines = file.each_line.drop(start).take(6)
137
- Hash[*(start+1..(lines.count+start)).zip(lines).flatten]
138
- end
139
- end
222
+ def exception_name
223
+ exception.cause.class.to_s
224
+ end
225
+
226
+ def message
227
+ exception.message
228
+ end
229
+
230
+ def exception_inspect
231
+ exception.inspect
140
232
  end
141
233
 
142
- def expand_backtrace
143
- @exception.backtrace.unshift(
144
- @exception.to_s.split("\n")
145
- ).flatten!
234
+ def exception_id
235
+ exception.object_id
146
236
  end
237
+
238
+ private
239
+ class SourceMapLocation < DelegateClass(Thread::Backtrace::Location) # :nodoc:
240
+ def initialize(location, template)
241
+ super(location)
242
+ @template = template
243
+ end
244
+
245
+ def spot(exc)
246
+ if RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) && __getobj__.is_a?(Thread::Backtrace::Location)
247
+ location = @template.spot(__getobj__)
248
+ else
249
+ location = super
250
+ end
251
+
252
+ if location
253
+ @template.translate_location(__getobj__, location)
254
+ end
255
+ end
256
+ end
257
+
258
+ attr_reader :backtrace
259
+
260
+ def build_backtrace
261
+ built_methods = {}
262
+
263
+ ActionView::PathRegistry.all_resolvers.each do |resolver|
264
+ resolver.built_templates.each do |template|
265
+ built_methods[template.method_name] = template
266
+ end
267
+ end
268
+
269
+ (@exception.backtrace_locations || []).map do |loc|
270
+ if built_methods.key?(loc.label.to_s)
271
+ thread_backtrace_location = if loc.respond_to?(:__getobj__)
272
+ loc.__getobj__
273
+ else
274
+ loc
275
+ end
276
+ SourceMapLocation.new(thread_backtrace_location, built_methods[loc.label.to_s])
277
+ else
278
+ loc
279
+ end
280
+ end
281
+ end
282
+
283
+ def causes_for(exception)
284
+ return enum_for(__method__, exception) unless block_given?
285
+
286
+ yield exception while exception = exception.cause
287
+ end
288
+
289
+ def wrapped_causes_for(exception, backtrace_cleaner)
290
+ causes_for(exception).map { |cause| self.class.new(backtrace_cleaner, cause) }
291
+ end
292
+
293
+ def clean_backtrace(*args)
294
+ if backtrace_cleaner
295
+ backtrace_cleaner.clean(backtrace, *args)
296
+ else
297
+ backtrace
298
+ end
299
+ end
300
+
301
+ def extract_source(trace)
302
+ spot = trace.spot(@exception)
303
+
304
+ if spot
305
+ line = spot[:first_lineno]
306
+ code = extract_source_fragment_lines(spot[:script_lines], line)
307
+
308
+ if line == spot[:last_lineno]
309
+ code[line] = [
310
+ code[line][0, spot[:first_column]],
311
+ code[line][spot[:first_column]...spot[:last_column]],
312
+ code[line][spot[:last_column]..-1],
313
+ ]
314
+ end
315
+
316
+ return {
317
+ code: code,
318
+ line_number: line
319
+ }
320
+ end
321
+
322
+ file, line_number = extract_file_and_line_number(trace)
323
+
324
+ {
325
+ code: source_fragment(file, line_number),
326
+ line_number: line_number
327
+ }
328
+ end
329
+
330
+ def extract_source_fragment_lines(source_lines, line)
331
+ start = [line - 3, 0].max
332
+ lines = source_lines.drop(start).take(6)
333
+ Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
334
+ end
335
+
336
+ def source_fragment(path, line)
337
+ return unless Rails.respond_to?(:root) && Rails.root
338
+ full_path = Rails.root.join(path)
339
+ if File.exist?(full_path)
340
+ File.open(full_path, "r") do |file|
341
+ extract_source_fragment_lines(file.each_line, line)
342
+ end
343
+ end
344
+ end
345
+
346
+ def extract_file_and_line_number(trace)
347
+ [trace.path, trace.lineno]
348
+ end
147
349
  end
148
350
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "rack/body_proxy"
6
+
7
+ module ActionDispatch
8
+ class Executor
9
+ def initialize(app, executor)
10
+ @app, @executor = app, executor
11
+ end
12
+
13
+ def call(env)
14
+ state = @executor.run!(reset: true)
15
+ begin
16
+ response = @app.call(env)
17
+
18
+ if env["action_dispatch.report_exception"]
19
+ error = env["action_dispatch.exception"]
20
+ @executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
21
+ end
22
+
23
+ returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
24
+ rescue => error
25
+ @executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
26
+ raise
27
+ ensure
28
+ state.complete! unless returned
29
+ end
30
+ end
31
+ end
32
+ end