actionpack 6.0.0.beta1 → 6.0.1.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +125 -13
  3. data/README.rdoc +2 -1
  4. data/lib/abstract_controller/caching/fragments.rb +0 -1
  5. data/lib/abstract_controller/translation.rb +1 -0
  6. data/lib/action_controller.rb +4 -1
  7. data/lib/action_controller/metal.rb +3 -3
  8. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  9. data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
  10. data/lib/action_controller/metal/exceptions.rb +2 -2
  11. data/lib/action_controller/metal/force_ssl.rb +1 -2
  12. data/lib/action_controller/metal/helpers.rb +2 -2
  13. data/lib/action_controller/metal/implicit_render.rb +2 -2
  14. data/lib/action_controller/metal/live.rb +2 -2
  15. data/lib/action_controller/metal/mime_responds.rb +1 -1
  16. data/lib/action_controller/metal/params_wrapper.rb +2 -2
  17. data/lib/action_controller/metal/redirecting.rb +6 -27
  18. data/lib/action_controller/metal/renderers.rb +4 -4
  19. data/lib/action_controller/metal/rendering.rb +1 -1
  20. data/lib/action_controller/metal/request_forgery_protection.rb +2 -2
  21. data/lib/action_controller/metal/strong_parameters.rb +6 -12
  22. data/lib/action_controller/renderer.rb +2 -2
  23. data/lib/action_controller/template_assertions.rb +1 -1
  24. data/lib/action_controller/test_case.rb +3 -2
  25. data/lib/action_dispatch.rb +1 -1
  26. data/lib/action_dispatch/http/content_security_policy.rb +20 -9
  27. data/lib/action_dispatch/http/mime_negotiation.rb +5 -0
  28. data/lib/action_dispatch/http/mime_type.rb +13 -1
  29. data/lib/action_dispatch/http/response.rb +27 -7
  30. data/lib/action_dispatch/http/upload.rb +4 -1
  31. data/lib/action_dispatch/journey/formatter.rb +1 -1
  32. data/lib/action_dispatch/journey/path/pattern.rb +6 -1
  33. data/lib/action_dispatch/journey/route.rb +5 -4
  34. data/lib/action_dispatch/journey/routes.rb +0 -1
  35. data/lib/action_dispatch/middleware/actionable_exceptions.rb +39 -0
  36. data/lib/action_dispatch/middleware/cookies.rb +9 -10
  37. data/lib/action_dispatch/middleware/debug_exceptions.rb +8 -2
  38. data/lib/action_dispatch/middleware/debug_view.rb +19 -1
  39. data/lib/action_dispatch/middleware/exception_wrapper.rb +15 -10
  40. data/lib/action_dispatch/middleware/host_authorization.rb +2 -2
  41. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -2
  42. data/lib/action_dispatch/middleware/remote_ip.rb +3 -3
  43. data/lib/action_dispatch/middleware/session/cookie_store.rb +4 -3
  44. data/lib/action_dispatch/middleware/show_exceptions.rb +1 -1
  45. data/lib/action_dispatch/middleware/stack.rb +34 -2
  46. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  47. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  48. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  49. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  50. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +2 -2
  51. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +2 -2
  52. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +6 -2
  53. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  54. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -1
  55. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -1
  56. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
  57. data/lib/action_dispatch/railtie.rb +6 -2
  58. data/lib/action_dispatch/routing.rb +18 -18
  59. data/lib/action_dispatch/routing/mapper.rb +26 -11
  60. data/lib/action_dispatch/routing/route_set.rb +13 -15
  61. data/lib/action_dispatch/system_test_case.rb +43 -5
  62. data/lib/action_dispatch/system_testing/browser.rb +38 -7
  63. data/lib/action_dispatch/system_testing/driver.rb +10 -1
  64. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +3 -2
  65. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
  66. data/lib/action_dispatch/testing/assertions.rb +1 -1
  67. data/lib/action_dispatch/testing/assertions/routing.rb +8 -1
  68. data/lib/action_dispatch/testing/integration.rb +2 -2
  69. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  70. data/lib/action_dispatch/testing/test_response.rb +1 -1
  71. data/lib/action_pack/gem_version.rb +2 -2
  72. metadata +20 -15
  73. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -56,7 +56,6 @@ module ActionDispatch
56
56
  end
57
57
 
58
58
  def simulator
59
- return if ast.nil?
60
59
  @simulator ||= begin
61
60
  gtg = GTG::Builder.new(ast).transition_table
62
61
  GTG::Simulator.new(gtg)
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "action_dispatch/http/request"
5
+ require "active_support/actionable_error"
6
+
7
+ module ActionDispatch
8
+ class ActionableExceptions # :nodoc:
9
+ cattr_accessor :endpoint, default: "/rails/actions"
10
+
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ request = ActionDispatch::Request.new(env)
17
+ return @app.call(env) unless actionable_request?(request)
18
+
19
+ ActiveSupport::ActionableError.dispatch(request.params[:error].to_s.safe_constantize, request.params[:action])
20
+
21
+ redirect_to request.params[:location]
22
+ end
23
+
24
+ private
25
+ def actionable_request?(request)
26
+ request.show_exceptions? && request.post? && request.path == endpoint
27
+ end
28
+
29
+ def redirect_to(location)
30
+ body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
31
+
32
+ [302, {
33
+ "Content-Type" => "text/html; charset=#{Response.default_charset}",
34
+ "Content-Length" => body.bytesize.to_s,
35
+ "Location" => location,
36
+ }, [body]]
37
+ end
38
+ end
39
+ end
@@ -338,7 +338,7 @@ module ActionDispatch
338
338
 
339
339
  def update_cookies_from_jar
340
340
  request_jar = @request.cookie_jar.instance_variable_get(:@cookies)
341
- set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) }
341
+ set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) || @set_cookies.key?(k) }
342
342
 
343
343
  @cookies.update set_cookies if set_cookies
344
344
  end
@@ -488,13 +488,8 @@ module ActionDispatch
488
488
  end
489
489
 
490
490
  def cookie_metadata(name, options)
491
- if request.use_cookies_with_metadata
492
- metadata = expiry_options(options)
493
- metadata[:purpose] = "cookie.#{name}"
494
-
495
- metadata
496
- else
497
- {}
491
+ expiry_options(options).tap do |metadata|
492
+ metadata[:purpose] = "cookie.#{name}" if request.use_cookies_with_metadata
498
493
  end
499
494
  end
500
495
 
@@ -539,9 +534,13 @@ module ActionDispatch
539
534
  if value
540
535
  case
541
536
  when needs_migration?(value)
542
- self[name] = Marshal.load(value)
537
+ Marshal.load(value).tap do |v|
538
+ self[name] = { value: v }
539
+ end
543
540
  when rotate
544
- self[name] = serializer.load(value)
541
+ serializer.load(value).tap do |v|
542
+ self[name] = { value: v }
543
+ end
545
544
  else
546
545
  serializer.load(value)
547
546
  end
@@ -4,6 +4,8 @@ require "action_dispatch/http/request"
4
4
  require "action_dispatch/middleware/exception_wrapper"
5
5
  require "action_dispatch/routing/inspector"
6
6
 
7
+ require "active_support/actionable_error"
8
+
7
9
  require "action_view"
8
10
  require "action_view/base"
9
11
 
@@ -60,7 +62,11 @@ module ActionDispatch
60
62
  log_error(request, wrapper)
61
63
 
62
64
  if request.get_header("action_dispatch.show_detailed_exceptions")
63
- content_type = request.formats.first
65
+ begin
66
+ content_type = request.formats.first
67
+ rescue Mime::Type::InvalidMimeType
68
+ render_for_api_request(Mime[:text], wrapper)
69
+ end
64
70
 
65
71
  if api_request?(content_type)
66
72
  render_for_api_request(content_type, wrapper)
@@ -142,7 +148,7 @@ module ActionDispatch
142
148
  message = []
143
149
  message << " "
144
150
  message << "#{exception.class} (#{exception.message}):"
145
- message.concat(exception.annoted_source_code) if exception.respond_to?(:annoted_source_code)
151
+ message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
146
152
  message << " "
147
153
  message.concat(trace)
148
154
 
@@ -10,7 +10,13 @@ module ActionDispatch
10
10
  RESCUES_TEMPLATE_PATH = File.expand_path("templates", __dir__)
11
11
 
12
12
  def initialize(assigns)
13
- super([RESCUES_TEMPLATE_PATH], assigns)
13
+ paths = [RESCUES_TEMPLATE_PATH]
14
+ lookup_context = ActionView::LookupContext.new(paths)
15
+ super(lookup_context, assigns)
16
+ end
17
+
18
+ def compiled_method_container
19
+ self.class
14
20
  end
15
21
 
16
22
  def debug_params(params)
@@ -46,5 +52,17 @@ module ActionDispatch
46
52
  super
47
53
  end
48
54
  end
55
+
56
+ def protect_against_forgery?
57
+ false
58
+ end
59
+
60
+ def params_valid?
61
+ begin
62
+ @request.parameters
63
+ rescue ActionController::BadRequest
64
+ false
65
+ end
66
+ end
49
67
  end
50
68
  end
@@ -12,6 +12,7 @@ module ActionDispatch
12
12
  "ActionController::UnknownHttpMethod" => :method_not_allowed,
13
13
  "ActionController::NotImplemented" => :not_implemented,
14
14
  "ActionController::UnknownFormat" => :not_acceptable,
15
+ "Mime::Type::InvalidMimeType" => :not_acceptable,
15
16
  "ActionController::MissingExactTemplate" => :not_acceptable,
16
17
  "ActionController::InvalidAuthenticityToken" => :unprocessable_entity,
17
18
  "ActionController::InvalidCrossOriginRequest" => :unprocessable_entity,
@@ -31,22 +32,34 @@ module ActionDispatch
31
32
  "ActionController::MissingExactTemplate" => "missing_exact_template",
32
33
  )
33
34
 
35
+ cattr_accessor :wrapper_exceptions, default: [
36
+ "ActionView::Template::Error"
37
+ ]
38
+
34
39
  attr_reader :backtrace_cleaner, :exception, :wrapped_causes, :line_number, :file
35
40
 
36
41
  def initialize(backtrace_cleaner, exception)
37
42
  @backtrace_cleaner = backtrace_cleaner
38
- @exception = original_exception(exception)
43
+ @exception = exception
39
44
  @wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
40
45
 
41
46
  expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
42
47
  end
43
48
 
49
+ def unwrapped_exception
50
+ if wrapper_exceptions.include?(exception.class.to_s)
51
+ exception.cause
52
+ else
53
+ exception
54
+ end
55
+ end
56
+
44
57
  def rescue_template
45
58
  @@rescue_templates[@exception.class.name]
46
59
  end
47
60
 
48
61
  def status_code
49
- self.class.status_code_for_exception(@exception.class.name)
62
+ self.class.status_code_for_exception(unwrapped_exception.class.name)
50
63
  end
51
64
 
52
65
  def application_trace
@@ -122,14 +135,6 @@ module ActionDispatch
122
135
  Array(@exception.backtrace)
123
136
  end
124
137
 
125
- def original_exception(exception)
126
- if @@rescue_responses.has_key?(exception.cause.class.name)
127
- exception.cause
128
- else
129
- exception
130
- end
131
- end
132
-
133
138
  def causes_for(exception)
134
139
  return enum_for(__method__, exception) unless block_given?
135
140
 
@@ -3,8 +3,8 @@
3
3
  require "action_dispatch/http/request"
4
4
 
5
5
  module ActionDispatch
6
- # This middleware guards from DNS rebinding attacks by white-listing the
7
- # hosts a request can be sent to.
6
+ # This middleware guards from DNS rebinding attacks by explicitly permitting
7
+ # the hosts a request can be sent to.
8
8
  #
9
9
  # When a request comes to an unauthorized host, the +response_app+
10
10
  # application will be executed and rendered. If no +response_app+ is given, a
@@ -21,8 +21,12 @@ module ActionDispatch
21
21
  def call(env)
22
22
  request = ActionDispatch::Request.new(env)
23
23
  status = request.path_info[1..-1].to_i
24
- content_type = request.formats.first
25
- body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
24
+ begin
25
+ content_type = request.formats.first
26
+ rescue Mime::Type::InvalidMimeType
27
+ content_type = Mime[:text]
28
+ end
29
+ body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
26
30
 
27
31
  render(status, content_type, body)
28
32
  end
@@ -8,13 +8,13 @@ module ActionDispatch
8
8
  # contain the address, and then picking the last-set address that is not
9
9
  # on the list of trusted IPs. This follows the precedent set by e.g.
10
10
  # {the Tomcat server}[https://issues.apache.org/bugzilla/show_bug.cgi?id=50453],
11
- # with {reasoning explained at length}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection]
11
+ # with {reasoning explained at length}[https://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection]
12
12
  # by @gingerlime. A more detailed explanation of the algorithm is given
13
13
  # at GetIp#calculate_ip.
14
14
  #
15
15
  # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
16
16
  # requires. Some Rack servers simply drop preceding headers, and only report
17
- # the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
17
+ # the value that was {given in the last header}[https://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
18
18
  # If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
19
19
  # then you should test your Rack server to make sure your data is good.
20
20
  #
@@ -102,7 +102,7 @@ module ActionDispatch
102
102
  # proxies, that header may contain a list of IPs. Other proxy services
103
103
  # set the Client-Ip header instead, so we check that too.
104
104
  #
105
- # As discussed in {this post about Rails IP Spoofing}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/],
105
+ # As discussed in {this post about Rails IP Spoofing}[https://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/],
106
106
  # while the first IP in the list is likely to be the "originating" IP,
107
107
  # it could also have been set by the client maliciously.
108
108
  #
@@ -24,9 +24,10 @@ module ActionDispatch
24
24
  #
25
25
  # Rails.application.config.session_store :cookie_store, key: '_your_app_session'
26
26
  #
27
- # By default, your secret key base is derived from your application name in
28
- # the test and development environments. In all other environments, it is stored
29
- # encrypted in the <tt>config/credentials.yml.enc</tt> file.
27
+ # In the development and test environments your application's secret key base is
28
+ # generated by Rails and stored in a temporary file in <tt>tmp/development_secret.txt</tt>.
29
+ # In all other environments, it is stored encrypted in the
30
+ # <tt>config/credentials.yml.enc</tt> file.
30
31
  #
31
32
  # If your application was not updated to Rails 5.2 defaults, the secret_key_base
32
33
  # will be found in the old <tt>config/secrets.yml</tt> file.
@@ -45,7 +45,7 @@ module ActionDispatch
45
45
  backtrace_cleaner = request.get_header "action_dispatch.backtrace_cleaner"
46
46
  wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
47
47
  status = wrapper.status_code
48
- request.set_header "action_dispatch.exception", wrapper.exception
48
+ request.set_header "action_dispatch.exception", wrapper.unwrapped_exception
49
49
  request.set_header "action_dispatch.original_path", request.path_info
50
50
  request.path_info = "/#{status}"
51
51
  response = @exceptions_app.call(request.env)
@@ -36,6 +36,31 @@ module ActionDispatch
36
36
  def build(app)
37
37
  klass.new(app, *args, &block)
38
38
  end
39
+
40
+ def build_instrumented(app)
41
+ InstrumentationProxy.new(build(app), inspect)
42
+ end
43
+ end
44
+
45
+ # This class is used to instrument the execution of a single middleware.
46
+ # It proxies the `call` method transparently and instruments the method
47
+ # call.
48
+ class InstrumentationProxy
49
+ EVENT_NAME = "process_middleware.action_dispatch"
50
+
51
+ def initialize(middleware, class_name)
52
+ @middleware = middleware
53
+
54
+ @payload = {
55
+ middleware: class_name,
56
+ }
57
+ end
58
+
59
+ def call(env)
60
+ ActiveSupport::Notifications.instrument(EVENT_NAME, @payload) do
61
+ @middleware.call(env)
62
+ end
63
+ end
39
64
  end
40
65
 
41
66
  include Enumerable
@@ -97,8 +122,15 @@ module ActionDispatch
97
122
  middlewares.push(build_middleware(klass, args, block))
98
123
  end
99
124
 
100
- def build(app = Proc.new)
101
- middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
125
+ def build(app = nil, &block)
126
+ instrumenting = ActiveSupport::Notifications.notifier.listening?(InstrumentationProxy::EVENT_NAME)
127
+ middlewares.freeze.reverse.inject(app || block) do |a, e|
128
+ if instrumenting
129
+ e.build_instrumented(a)
130
+ else
131
+ e.build(a)
132
+ end
133
+ end
102
134
  end
103
135
 
104
136
  private
@@ -0,0 +1,13 @@
1
+ <% actions = ActiveSupport::ActionableError.actions(exception) %>
2
+
3
+ <% if actions.any? %>
4
+ <div class="actions">
5
+ <% actions.each do |action, _| %>
6
+ <%= button_to action, ActionDispatch::ActionableExceptions.endpoint, params: {
7
+ error: exception.class.name,
8
+ action: action,
9
+ location: request.path
10
+ } %>
11
+ <% end %>
12
+ </div>
13
+ <% end %>
@@ -6,7 +6,9 @@
6
6
  <% end %>
7
7
 
8
8
  <h2 style="margin-top: 30px">Request</h2>
9
- <p><b>Parameters</b>:</p> <pre><%= debug_params(@request.filtered_parameters) %></pre>
9
+ <% if params_valid? %>
10
+ <p><b>Parameters</b>:</p> <pre><%= debug_params(@request.filtered_parameters) %></pre>
11
+ <% end %>
10
12
 
11
13
  <div class="details">
12
14
  <div class="summary"><a href="#" onclick="return toggleSessionDump()">Toggle session dump</a></div>
@@ -1,5 +1,5 @@
1
1
  <%
2
- clean_params = @request.filtered_parameters.clone
2
+ clean_params = params_valid? ? @request.filtered_parameters.clone : {}
3
3
  clean_params.delete("action")
4
4
  clean_params.delete("controller")
5
5
 
@@ -2,6 +2,6 @@
2
2
  <h1>Blocked host: <%= @host %></h1>
3
3
  </header>
4
4
  <div id="container">
5
- <h2>To allow requests to <%= @host %>, add the following configuration:</h2>
6
- <pre>Rails.application.config.hosts &lt;&lt; "<%= @host %>"</pre>
5
+ <h2>To allow requests to <%= @host %>, add the following to your environment configuration:</h2>
6
+ <pre>config.hosts &lt;&lt; "<%= @host %>"</pre>
7
7
  </div>
@@ -1,5 +1,5 @@
1
1
  Blocked host: <%= @host %>
2
2
 
3
- To allow requests to <%= @host %>, add the following configuration:
3
+ To allow requests to <%= @host %>, add the following to your environment configuration:
4
4
 
5
- Rails.application.config.hosts << "<%= @host %>"
5
+ config.hosts << "<%= @host %>"
@@ -1,14 +1,18 @@
1
1
  <header>
2
2
  <h1>
3
3
  <%= @exception.class.to_s %>
4
- <% if @request.parameters['controller'] %>
4
+ <% if params_valid? && @request.parameters['controller'] %>
5
5
  in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %>
6
6
  <% end %>
7
7
  </h1>
8
8
  </header>
9
9
 
10
10
  <div id="container">
11
- <h2><%= h @exception.message %></h2>
11
+ <h2>
12
+ <%= h @exception.message %>
13
+
14
+ <%= render "rescues/actions", exception: @exception, request: @request %>
15
+ </h2>
12
16
 
13
17
  <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx, error_index: 0 %>
14
18
  <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show, error_index: 0 %>
@@ -1,5 +1,5 @@
1
1
  <%= @exception.class.to_s %><%
2
- if @request.parameters['controller']
2
+ if params_valid? && @request.parameters['controller']
3
3
  %> in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %>
4
4
  <% end %>
5
5
 
@@ -10,9 +10,12 @@
10
10
  <div id="container">
11
11
  <h2>
12
12
  <%= h @exception.message %>
13
- <% if @exception.message.match? %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}} %>
13
+ <% if defined?(ActiveStorage) && @exception.message.match?(%r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}) %>
14
14
  <br />To resolve this issue run: rails active_storage:install
15
15
  <% end %>
16
+ <% if defined?(ActionMailbox) && @exception.message.match?(%r{#{ActionMailbox::InboundEmail.table_name}}) %>
17
+ <br />To resolve this issue run: rails action_mailbox:install
18
+ <% end %>
16
19
  </h2>
17
20
 
18
21
  <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
@@ -4,8 +4,10 @@
4
4
  <% end %>
5
5
 
6
6
  <%= @exception.message %>
7
- <% if @exception.message.match? %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}} %>
7
+ <% if defined?(ActiveStorage) && @exception.message.match?(%r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}) %>
8
8
  To resolve this issue run: rails active_storage:install
9
+ <% if defined?(ActionMailbox) && @exception.message.match?(%r{#{ActionMailbox::InboundEmail.table_name}}) %>
10
+ To resolve this issue run: rails action_mailbox:install
9
11
  <% end %>
10
12
 
11
13
  <%= render template: "rescues/_source" %>