actionpack 7.0.4.3 → 7.1.3.4

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 (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +380 -284
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/abstract_controller/base.rb +20 -11
  6. data/lib/abstract_controller/caching/fragments.rb +2 -0
  7. data/lib/abstract_controller/callbacks.rb +31 -6
  8. data/lib/abstract_controller/deprecator.rb +7 -0
  9. data/lib/abstract_controller/helpers.rb +75 -28
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
  11. data/lib/abstract_controller/rendering.rb +12 -14
  12. data/lib/abstract_controller/translation.rb +9 -6
  13. data/lib/abstract_controller/url_for.rb +2 -0
  14. data/lib/abstract_controller.rb +6 -0
  15. data/lib/action_controller/api.rb +6 -4
  16. data/lib/action_controller/base.rb +3 -17
  17. data/lib/action_controller/caching.rb +2 -0
  18. data/lib/action_controller/deprecator.rb +7 -0
  19. data/lib/action_controller/form_builder.rb +2 -0
  20. data/lib/action_controller/log_subscriber.rb +16 -4
  21. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  22. data/lib/action_controller/metal/conditional_get.rb +121 -123
  23. data/lib/action_controller/metal/content_security_policy.rb +5 -5
  24. data/lib/action_controller/metal/data_streaming.rb +20 -18
  25. data/lib/action_controller/metal/default_headers.rb +2 -0
  26. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  27. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  28. data/lib/action_controller/metal/exceptions.rb +8 -0
  29. data/lib/action_controller/metal/head.rb +9 -7
  30. data/lib/action_controller/metal/helpers.rb +3 -14
  31. data/lib/action_controller/metal/http_authentication.rb +17 -8
  32. data/lib/action_controller/metal/implicit_render.rb +5 -3
  33. data/lib/action_controller/metal/instrumentation.rb +8 -1
  34. data/lib/action_controller/metal/live.rb +25 -1
  35. data/lib/action_controller/metal/mime_responds.rb +2 -2
  36. data/lib/action_controller/metal/params_wrapper.rb +4 -2
  37. data/lib/action_controller/metal/permissions_policy.rb +2 -2
  38. data/lib/action_controller/metal/redirecting.rb +25 -8
  39. data/lib/action_controller/metal/renderers.rb +4 -4
  40. data/lib/action_controller/metal/rendering.rb +114 -9
  41. data/lib/action_controller/metal/request_forgery_protection.rb +144 -53
  42. data/lib/action_controller/metal/rescue.rb +6 -3
  43. data/lib/action_controller/metal/streaming.rb +71 -31
  44. data/lib/action_controller/metal/strong_parameters.rb +158 -101
  45. data/lib/action_controller/metal/url_for.rb +9 -4
  46. data/lib/action_controller/metal.rb +79 -21
  47. data/lib/action_controller/railtie.rb +24 -10
  48. data/lib/action_controller/renderer.rb +99 -85
  49. data/lib/action_controller/test_case.rb +15 -5
  50. data/lib/action_controller.rb +8 -1
  51. data/lib/action_dispatch/constants.rb +32 -0
  52. data/lib/action_dispatch/deprecator.rb +7 -0
  53. data/lib/action_dispatch/http/cache.rb +8 -10
  54. data/lib/action_dispatch/http/content_security_policy.rb +14 -9
  55. data/lib/action_dispatch/http/filter_parameters.rb +14 -28
  56. data/lib/action_dispatch/http/headers.rb +3 -1
  57. data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
  58. data/lib/action_dispatch/http/mime_type.rb +35 -12
  59. data/lib/action_dispatch/http/mime_types.rb +3 -1
  60. data/lib/action_dispatch/http/parameters.rb +1 -1
  61. data/lib/action_dispatch/http/permissions_policy.rb +38 -23
  62. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  63. data/lib/action_dispatch/http/request.rb +63 -30
  64. data/lib/action_dispatch/http/response.rb +80 -63
  65. data/lib/action_dispatch/http/upload.rb +15 -2
  66. data/lib/action_dispatch/journey/formatter.rb +8 -2
  67. data/lib/action_dispatch/journey/path/pattern.rb +14 -14
  68. data/lib/action_dispatch/journey/route.rb +3 -2
  69. data/lib/action_dispatch/journey/router.rb +9 -8
  70. data/lib/action_dispatch/journey/routes.rb +2 -2
  71. data/lib/action_dispatch/log_subscriber.rb +23 -0
  72. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
  73. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  74. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  75. data/lib/action_dispatch/middleware/cookies.rb +85 -102
  76. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
  77. data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
  78. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  79. data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
  80. data/lib/action_dispatch/middleware/executor.rb +1 -1
  81. data/lib/action_dispatch/middleware/flash.rb +7 -0
  82. data/lib/action_dispatch/middleware/host_authorization.rb +18 -8
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  84. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  85. data/lib/action_dispatch/middleware/remote_ip.rb +21 -20
  86. data/lib/action_dispatch/middleware/request_id.rb +4 -2
  87. data/lib/action_dispatch/middleware/server_timing.rb +4 -4
  88. data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
  89. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  90. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
  91. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  92. data/lib/action_dispatch/middleware/show_exceptions.rb +25 -18
  93. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  94. data/lib/action_dispatch/middleware/stack.rb +7 -2
  95. data/lib/action_dispatch/middleware/static.rb +14 -10
  96. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  97. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
  98. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  99. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -3
  100. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -3
  101. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
  102. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  103. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
  105. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
  106. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  107. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
  108. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  109. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  110. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  111. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -41
  112. data/lib/action_dispatch/railtie.rb +14 -4
  113. data/lib/action_dispatch/request/session.rb +16 -6
  114. data/lib/action_dispatch/request/utils.rb +8 -3
  115. data/lib/action_dispatch/routing/inspector.rb +54 -6
  116. data/lib/action_dispatch/routing/mapper.rb +58 -24
  117. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  118. data/lib/action_dispatch/routing/redirection.rb +15 -6
  119. data/lib/action_dispatch/routing/route_set.rb +52 -22
  120. data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
  121. data/lib/action_dispatch/routing/url_for.rb +26 -22
  122. data/lib/action_dispatch/routing.rb +7 -7
  123. data/lib/action_dispatch/system_test_case.rb +3 -3
  124. data/lib/action_dispatch/system_testing/browser.rb +20 -19
  125. data/lib/action_dispatch/system_testing/driver.rb +14 -22
  126. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
  127. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  128. data/lib/action_dispatch/testing/assertions/response.rb +14 -7
  129. data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
  130. data/lib/action_dispatch/testing/assertions.rb +3 -1
  131. data/lib/action_dispatch/testing/integration.rb +27 -17
  132. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  133. data/lib/action_dispatch/testing/test_process.rb +4 -3
  134. data/lib/action_dispatch/testing/test_request.rb +1 -1
  135. data/lib/action_dispatch/testing/test_response.rb +23 -9
  136. data/lib/action_dispatch.rb +37 -4
  137. data/lib/action_pack/gem_version.rb +4 -4
  138. data/lib/action_pack/version.rb +1 -1
  139. data/lib/action_pack.rb +1 -1
  140. metadata +65 -29
@@ -4,12 +4,14 @@ require "action_dispatch/middleware/session/abstract_store"
4
4
  begin
5
5
  require "rack/session/dalli"
6
6
  rescue LoadError => e
7
- $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
7
+ warn "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
8
8
  raise e
9
9
  end
10
10
 
11
11
  module ActionDispatch
12
12
  module Session
13
+ # = Action Dispatch Session \MemCacheStore
14
+ #
13
15
  # A session store that uses MemCache to implement storage.
14
16
  #
15
17
  # ==== Options
@@ -3,18 +3,24 @@
3
3
  require "action_dispatch/middleware/exception_wrapper"
4
4
 
5
5
  module ActionDispatch
6
+ # = Action Dispatch \ShowExceptions
7
+ #
6
8
  # This middleware rescues any exception returned by the application
7
9
  # and calls an exceptions app that will wrap it in a format for the end user.
8
10
  #
9
- # The exceptions app should be passed as parameter on initialization
10
- # of ShowExceptions. Every time there is an exception, ShowExceptions will
11
- # store the exception in env["action_dispatch.exception"], rewrite the
12
- # PATH_INFO to the exception status code and call the Rack app.
11
+ # The exceptions app should be passed as a parameter on initialization of
12
+ # +ShowExceptions+. Every time there is an exception, +ShowExceptions+ will
13
+ # store the exception in <tt>env["action_dispatch.exception"]</tt>, rewrite
14
+ # the +PATH_INFO+ to the exception status code, and call the Rack app.
15
+ #
16
+ # In \Rails applications, the exceptions app can be configured with
17
+ # +config.exceptions_app+, which defaults to ActionDispatch::PublicExceptions.
13
18
  #
14
- # If the application returns a "X-Cascade" pass response, this middleware
15
- # will send an empty response as result with the correct status code.
16
- # If any exception happens inside the exceptions app, this middleware
17
- # catches the exceptions and returns a failsafe response.
19
+ # If the application returns a response with the <tt>X-Cascade</tt> header
20
+ # set to <tt>"pass"</tt>, this middleware will send an empty response as a
21
+ # result with the correct status code. If any exception happens inside the
22
+ # exceptions app, this middleware catches the exceptions and returns a
23
+ # failsafe response.
18
24
  class ShowExceptions
19
25
  def initialize(app, exceptions_app)
20
26
  @app = app
@@ -22,21 +28,21 @@ module ActionDispatch
22
28
  end
23
29
 
24
30
  def call(env)
25
- request = ActionDispatch::Request.new env
26
31
  @app.call(env)
27
32
  rescue Exception => exception
28
- if request.show_exceptions?
29
- render_exception(request, exception)
33
+ request = ActionDispatch::Request.new env
34
+ backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
35
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
36
+ if wrapper.show?(request)
37
+ render_exception(request, wrapper)
30
38
  else
31
39
  raise exception
32
40
  end
33
41
  end
34
42
 
35
43
  private
36
- def render_exception(request, exception)
37
- backtrace_cleaner = request.get_header "action_dispatch.backtrace_cleaner"
38
- wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
39
- status = wrapper.status_code
44
+ def render_exception(request, wrapper)
45
+ status = wrapper.status_code
40
46
  request.set_header "action_dispatch.exception", wrapper.unwrapped_exception
41
47
  request.set_header "action_dispatch.original_path", request.path_info
42
48
  request.set_header "action_dispatch.original_request_method", request.raw_request_method
@@ -44,11 +50,11 @@ module ActionDispatch
44
50
  request.path_info = "/#{status}"
45
51
  request.request_method = "GET"
46
52
  response = @exceptions_app.call(request.env)
47
- response[1]["X-Cascade"] == "pass" ? pass_response(status) : response
53
+ response[1][Constants::X_CASCADE] == "pass" ? pass_response(status) : response
48
54
  rescue Exception => failsafe_error
49
55
  $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
50
56
 
51
- [500, { "Content-Type" => "text/plain" },
57
+ [500, { Rack::CONTENT_TYPE => "text/plain; charset=utf-8" },
52
58
  ["500 Internal Server Error\n" \
53
59
  "If you are the administrator of this website, then please read this web " \
54
60
  "application's log file and/or the web server's log file to find out what " \
@@ -65,7 +71,8 @@ module ActionDispatch
65
71
  end
66
72
 
67
73
  def pass_response(status)
68
- [status, { "Content-Type" => "text/html; charset=#{Response.default_charset}", "Content-Length" => "0" }, []]
74
+ [status, { Rack::CONTENT_TYPE => "text/html; charset=#{Response.default_charset}",
75
+ Rack::CONTENT_LENGTH => "0" }, []]
69
76
  end
70
77
  end
71
78
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionDispatch
4
+ # = Action Dispatch \SSL
5
+ #
4
6
  # This middleware is added to the stack when <tt>config.force_ssl = true</tt>, and is passed
5
7
  # the options set in +config.ssl_options+. It does three jobs to enforce secure HTTP
6
8
  # requests:
@@ -86,7 +88,7 @@ module ActionDispatch
86
88
 
87
89
  private
88
90
  def set_hsts_header!(headers)
89
- headers["Strict-Transport-Security"] ||= @hsts_header
91
+ headers[Constants::STRICT_TRANSPORT_SECURITY] ||= @hsts_header
90
92
  end
91
93
 
92
94
  def normalize_hsts_options(options)
@@ -112,23 +114,33 @@ module ActionDispatch
112
114
  end
113
115
 
114
116
  def flag_cookies_as_secure!(headers)
115
- if cookies = headers["Set-Cookie"]
116
- cookies = cookies.split("\n")
117
+ cookies = headers[Rack::SET_COOKIE]
118
+ return unless cookies
117
119
 
118
- headers["Set-Cookie"] = cookies.map { |cookie|
120
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
121
+ cookies = cookies.split("\n")
122
+ headers[Rack::SET_COOKIE] = cookies.map { |cookie|
119
123
  if !/;\s*secure\s*(;|$)/i.match?(cookie)
120
124
  "#{cookie}; secure"
121
125
  else
122
126
  cookie
123
127
  end
124
128
  }.join("\n")
129
+ else
130
+ headers[Rack::SET_COOKIE] = Array(cookies).map do |cookie|
131
+ if !/;\s*secure\s*(;|$)/i.match?(cookie)
132
+ "#{cookie}; secure"
133
+ else
134
+ cookie
135
+ end
136
+ end
125
137
  end
126
138
  end
127
139
 
128
140
  def redirect_to_https(request)
129
141
  [ @redirect.fetch(:status, redirection_status(request)),
130
- { "Content-Type" => "text/html",
131
- "Location" => https_location_for(request) },
142
+ { Rack::CONTENT_TYPE => "text/html; charset=utf-8",
143
+ Constants::LOCATION => https_location_for(request) },
132
144
  (@redirect[:body] || []) ]
133
145
  end
134
146
 
@@ -4,6 +4,11 @@ require "active_support/inflector/methods"
4
4
  require "active_support/dependencies"
5
5
 
6
6
  module ActionDispatch
7
+ # = Action Dispatch \MiddlewareStack
8
+ #
9
+ # Read more about {Rails middleware
10
+ # stack}[https://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack]
11
+ # in the guides.
7
12
  class MiddlewareStack
8
13
  class Middleware
9
14
  attr_reader :args, :block, :klass
@@ -20,13 +25,13 @@ module ActionDispatch
20
25
  case middleware
21
26
  when Middleware
22
27
  klass == middleware.klass
23
- when Class
28
+ when Module
24
29
  klass == middleware
25
30
  end
26
31
  end
27
32
 
28
33
  def inspect
29
- if klass.is_a?(Class)
34
+ if klass.is_a?(Module)
30
35
  klass.to_s
31
36
  else
32
37
  klass.class.to_s
@@ -3,10 +3,12 @@
3
3
  require "rack/utils"
4
4
 
5
5
  module ActionDispatch
6
+ # = Action Dispatch \Static
7
+ #
6
8
  # This middleware serves static files from disk, if available.
7
9
  # If no file is found, it hands off to the main app.
8
10
  #
9
- # In Rails apps, this middleware is configured to serve assets from
11
+ # In \Rails apps, this middleware is configured to serve assets from
10
12
  # the +public/+ directory.
11
13
  #
12
14
  # Only GET and HEAD requests are served. POST and other HTTP methods
@@ -24,22 +26,24 @@ module ActionDispatch
24
26
  end
25
27
  end
26
28
 
27
- # This endpoint serves static files from disk using Rack::File.
29
+ # = Action Dispatch \FileHandler
30
+ #
31
+ # This endpoint serves static files from disk using +Rack::Files+.
28
32
  #
29
33
  # URL paths are matched with static files according to expected
30
34
  # conventions: +path+, +path+.html, +path+/index.html.
31
35
  #
32
36
  # Precompressed versions of these files are checked first. Brotli (.br)
33
37
  # and gzip (.gz) files are supported. If +path+.br exists, this
34
- # endpoint returns that file with a <tt>Content-Encoding: br</tt> header.
38
+ # endpoint returns that file with a <tt>content-encoding: br</tt> header.
35
39
  #
36
- # If no matching file is found, this endpoint responds 404 Not Found.
40
+ # If no matching file is found, this endpoint responds <tt>404 Not Found</tt>.
37
41
  #
38
42
  # Pass the +root+ directory to search for matching files, an optional
39
43
  # <tt>index: "index"</tt> to change the default +path+/index.html, and optional
40
44
  # additional response headers.
41
45
  class FileHandler
42
- # Accept-Encoding value -> file extension
46
+ # +Accept-Encoding+ value -> file extension
43
47
  PRECOMPRESSED = {
44
48
  "br" => ".br",
45
49
  "gzip" => ".gz",
@@ -53,7 +57,7 @@ module ActionDispatch
53
57
  @precompressed = Array(precompressed).map(&:to_s) | %w[ identity ]
54
58
  @compressible_content_types = compressible_content_types
55
59
 
56
- @file_server = ::Rack::File.new(@root, headers)
60
+ @file_server = ::Rack::Files.new(@root, headers)
57
61
  end
58
62
 
59
63
  def call(env)
@@ -76,7 +80,7 @@ module ActionDispatch
76
80
  request.path_info, ::Rack::Utils.escape_path(filepath).b
77
81
 
78
82
  @file_server.call(request.env).tap do |status, headers, body|
79
- # Omit Content-Encoding/Type/etc headers for 304 Not Modified
83
+ # Omit content-encoding/type/etc headers for 304 Not Modified
80
84
  if status != 304
81
85
  headers.update(content_headers)
82
86
  end
@@ -104,7 +108,7 @@ module ActionDispatch
104
108
  end
105
109
 
106
110
  def try_files(filepath, content_type, accept_encoding:)
107
- headers = { "Content-Type" => content_type }
111
+ headers = { Rack::CONTENT_TYPE => content_type }
108
112
 
109
113
  if compressible? content_type
110
114
  try_precompressed_files filepath, headers, accept_encoding: accept_encoding
@@ -124,10 +128,10 @@ module ActionDispatch
124
128
  if content_encoding == "identity"
125
129
  return precompressed_filepath, headers
126
130
  else
127
- headers["Vary"] = "Accept-Encoding"
131
+ headers[ActionDispatch::Constants::VARY] = "accept-encoding"
128
132
 
129
133
  if accept_encoding.any? { |enc, _| /\b#{content_encoding}\b/i.match?(enc) }
130
- headers["Content-Encoding"] = content_encoding
134
+ headers[ActionDispatch::Constants::CONTENT_ENCODING] = content_encoding
131
135
  return precompressed_filepath, headers
132
136
  end
133
137
  end
@@ -1,10 +1,10 @@
1
- <% actions = ActiveSupport::ActionableError.actions(exception) %>
1
+ <% actions = exception_wrapper.actions %>
2
2
 
3
3
  <% if actions.any? %>
4
4
  <div class="actions">
5
5
  <% actions.each do |action, _| %>
6
6
  <%= button_to action, ActionDispatch::ActionableExceptions.endpoint, params: {
7
- error: exception.class.name,
7
+ error: exception_wrapper.exception_class_name,
8
8
  action: action,
9
9
  location: request.path
10
10
  } %>
@@ -1,11 +1,11 @@
1
- <% if exception.respond_to?(:original_message) && exception.respond_to?(:corrections) %>
1
+ <% if exception_wrapper.has_corrections? %>
2
2
  <div class="exception-message">
3
- <%= simple_format h(exception.original_message), { class: "message" }, wrapper_tag: "div" %>
3
+ <%= simple_format h(exception_wrapper.original_message), { class: "message" }, wrapper_tag: "div" %>
4
4
  </div>
5
5
  <%
6
6
  # The 'did_you_mean' gem can raise exceptions when calling #corrections on
7
7
  # the exception. If it does there are no corrections to show.
8
- corrections = exception.corrections rescue []
8
+ corrections = exception_wrapper.corrections rescue []
9
9
  %>
10
10
  <% if corrections.any? %>
11
11
  <b>Did you mean?</b>
@@ -17,6 +17,6 @@
17
17
  <% end %>
18
18
  <% else %>
19
19
  <div class="exception-message">
20
- <%= simple_format h(exception.message), { class: "message" }, wrapper_tag: "div" %>
20
+ <%= simple_format h(exception_wrapper.message), { class: "message" }, wrapper_tag: "div" %>
21
21
  </div>
22
22
  <% end %>
@@ -18,12 +18,19 @@
18
18
  </td>
19
19
  <td width="100%">
20
20
  <pre>
21
- <% source_extract[:code].each do |line, source| -%><div class="line<%= " active" if line == source_extract[:line_number] -%>"><%= source -%></div><% end -%>
21
+ <% source_extract[:code].each do |line, source| -%>
22
+ <div class="line<%= " active" if line == source_extract[:line_number] -%>"><% if source.is_a?(Array) -%><%= source[0] -%><span class="error_highlight"><%= source[1] -%></span><%= source[2] -%>
23
+ <% else -%>
24
+ <%= source -%>
25
+ <% end -%></div><% end -%>
22
26
  </pre>
23
27
  </td>
24
28
  </tr>
25
29
  </table>
26
30
  </div>
31
+ <%- unless self.error_highlight_available? -%>
32
+ <p class="error_highlight_tip">Tip: You may want to add <code>gem 'error_highlight', '&gt;= 0.4.0'</code> into your Gemfile, which will display the fine-grained error location.</p>
33
+ <%- end -%>
27
34
  </div>
28
35
  <% end %>
29
36
  <% end %>
@@ -1,8 +1,12 @@
1
1
  <header>
2
- <h1>Blocked host: <%= @host %></h1>
2
+ <h1>Blocked hosts: <%= @hosts.join(", ") %></h1>
3
3
  </header>
4
4
  <main role="main" id="container">
5
- <h2>To allow requests to <%= @host %> make sure it is a valid hostname (containing only numbers, letters, dashes and dots), then add the following to your environment configuration:</h2>
6
- <pre>config.hosts &lt;&lt; "<%= @host %>"</pre>
5
+ <h2>To allow requests to these hosts, make sure they are valid hostnames (containing only numbers, letters, dashes and dots), then add the following to your environment configuration:</h2>
6
+ <pre>
7
+ <% @hosts.each do |host| %>
8
+ config.hosts &lt;&lt; "<%= host %>"
9
+ <% end %>
10
+ </pre>
7
11
  <p>For more details view: <a href="https://guides.rubyonrails.org/configuring.html#actiondispatch-hostauthorization">the Host Authorization guide</a></p>
8
12
  </main>
@@ -1,7 +1,9 @@
1
- Blocked host: <%= @host %>
1
+ Blocked hosts: <%= @hosts.join(", ") %>
2
2
 
3
- To allow requests to <%= @host %> make sure it is a valid hostname (containing only numbers, letters, dashes and dots), then add the following to your environment configuration:
3
+ To allow requests to these hosts, make sure they are valid hostnames (containing only numbers, letters, dashes and dots), then add the following to your environment configuration:
4
4
 
5
- config.hosts << "<%= @host %>"
5
+ <% @hosts.each do |host| %>
6
+ config.hosts << "<%= host %>"
7
+ <% end %>
6
8
 
7
9
  For more details on host authorization view: https://guides.rubyonrails.org/configuring.html#actiondispatch-hostauthorization
@@ -1,6 +1,6 @@
1
1
  <header>
2
2
  <h1>
3
- <%= @exception.class.to_s %>
3
+ <%= @exception_wrapper.exception_class_name %>
4
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 %>
@@ -8,24 +8,24 @@
8
8
  </header>
9
9
 
10
10
  <main role="main" id="container">
11
- <%= render "rescues/message_and_suggestions", exception: @exception %>
12
- <%= render "rescues/actions", exception: @exception, request: @request %>
11
+ <%= render "rescues/message_and_suggestions", exception: @exception, exception_wrapper: @exception_wrapper %>
12
+ <%= render "rescues/actions", exception: @exception, request: @request, exception_wrapper: @exception_wrapper %>
13
13
 
14
14
  <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx, error_index: 0 %>
15
15
  <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show, error_index: 0 %>
16
16
 
17
- <% if @exception.cause %>
17
+ <% if @exception_wrapper.has_cause? %>
18
18
  <h2>Exception Causes</h2>
19
19
  <% end %>
20
20
 
21
21
  <% @exception_wrapper.wrapped_causes.each.with_index(1) do |wrapper, index| %>
22
22
  <div class="details">
23
- <a class="summary" href="#" onclick="return toggle(<%= wrapper.exception.object_id %>)">
24
- <%= wrapper.exception.class.name %>: <%= h wrapper.exception.message %>
23
+ <a class="summary" href="#" onclick="return toggle(<%= wrapper.exception_id %>)">
24
+ <%= wrapper.exception_class_name %>: <%= h wrapper.message %>
25
25
  </a>
26
26
  </div>
27
27
 
28
- <div id="<%= wrapper.exception.object_id %>" class="hidden">
28
+ <div id="<%= wrapper.exception_id %>" class="hidden">
29
29
  <%= render "rescues/source", source_extracts: wrapper.source_extracts, show_source_idx: wrapper.source_to_show_id, error_index: index %>
30
30
  <%= render "rescues/trace", traces: wrapper.traces, trace_to_show: wrapper.trace_to_show, error_index: index %>
31
31
  </div>
@@ -1,9 +1,9 @@
1
- <%= @exception.class.to_s %><%
1
+ <%= @exception_wrapper.exception_class_name %><%
2
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
 
6
- <%= @exception.message %>
6
+ <%= @exception_wrapper.message %>
7
7
  <%= render template: "rescues/_source" %>
8
8
  <%= render template: "rescues/_trace" %>
9
9
  <%= render template: "rescues/_request_and_response" %>
@@ -3,6 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="turbo-visit-control" content="reload">
6
7
  <title>Action Controller: Exception caught</title>
7
8
  <style>
8
9
  body {
@@ -148,6 +149,18 @@
148
149
  background-color: #FCC;
149
150
  }
150
151
 
152
+ .error_highlight {
153
+ display: inline-block;
154
+ background-color: #FF9;
155
+ text-decoration: #F00 wavy underline;
156
+ }
157
+
158
+ .error_highlight_tip {
159
+ color: #666;
160
+ padding: 2px 2px;
161
+ font-size: 10px;
162
+ }
163
+
151
164
  .button_to {
152
165
  display: inline-block;
153
166
  margin-top: 0.75em;
@@ -225,6 +238,10 @@
225
238
  background-color: #900;
226
239
  }
227
240
 
241
+ .error_highlight {
242
+ color: #333;
243
+ }
244
+
228
245
  input[type="submit"] {
229
246
  box-shadow: 0 3px #800;
230
247
  }
@@ -1,19 +1,23 @@
1
1
  <header role="banner">
2
- <h1>No template for interactive request</h1>
2
+ <h1>No view template for interactive request</h1>
3
3
  </header>
4
4
 
5
5
  <main id="container">
6
6
  <h2><%= h @exception.message %></h2>
7
7
 
8
- <p class="summary">
9
- <strong>NOTE!</strong><br>
10
- Unless told otherwise, Rails expects an action to render a template with the same name,<br>
11
- contained in a folder named after its controller.
12
-
13
- If this controller is an API responding with 204 (No Content), <br>
14
- which does not require a template,
15
- then this error will occur when trying to access it via browser,<br>
16
- since we expect an HTML template
17
- to be rendered for such requests. If that's the case, carry on.
18
- </p>
8
+ <div class="summary">
9
+ <p>
10
+ <strong>NOTE:</strong> Rails usually expects a controller action to render a view template with the same name.
11
+ </p>
12
+ <p>
13
+ For example, a <code><%= @exception.controller %>#<%= @exception.action_name %></code> action defined in <code>app/controllers/<%= @exception.controller.controller_path %>_controller.rb</code> should have a corresponding view template
14
+ in a file named <code>app/views/<%= @exception.controller.controller_name %>/<%= @exception.action_name %>.html.erb</code>.
15
+ </p>
16
+ <p>
17
+ However, if this controller is an API endpoint responding with 204 (No Content), which does not require a view template because it doesn't serve an HTML response, then this error will occur when trying to access it with a browser. In this particular scenario, you can ignore this error.
18
+ </p>
19
+ <p>
20
+ You can find more about view template rendering conventions in the <a href="https://guides.rubyonrails.org/layouts_and_rendering.html#rendering-by-default-convention-over-configuration-in-action">Rails Guides on Layouts and Rendering in Rails</a>.
21
+ </p>
22
+ </div>
19
23
  </main>
@@ -3,7 +3,7 @@
3
3
  </header>
4
4
 
5
5
  <main role="main" id="container">
6
- <h2><%= h @exception.message %></h2>
6
+ <h2><%= h @exception_wrapper.message %></h2>
7
7
 
8
8
  <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
9
9
  <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
@@ -2,12 +2,12 @@
2
2
  <h1>Routing Error</h1>
3
3
  </header>
4
4
  <main role="main" id="container">
5
- <h2><%= h @exception.message %></h2>
6
- <% unless @exception.failures.empty? %>
5
+ <h2><%= h @exception_wrapper.message %></h2>
6
+ <% unless @exception_wrapper.failures.empty? %>
7
7
  <p>
8
8
  <h2>Failure reasons:</h2>
9
9
  <ol>
10
- <% @exception.failures.each do |route, reason| %>
10
+ <% @exception_wrapper.failures.each do |route, reason| %>
11
11
  <li><code><%= route.inspect.delete('\\') %></code> failed because <%= reason.downcase %></li>
12
12
  <% end %>
13
13
  </ol>
@@ -1,19 +1,19 @@
1
1
  <header role="banner">
2
2
  <h1>
3
- <%= @exception.cause.class.to_s %> in
3
+ <%= @exception_wrapper.exception_name %> in
4
4
  <%= @request.parameters["controller"].camelize if @request.parameters["controller"] %>#<%= @request.parameters["action"] %>
5
5
  </h1>
6
6
  </header>
7
7
 
8
8
  <main role="main" id="container">
9
9
  <p>
10
- Showing <i><%= @exception.file_name %></i> where line <b>#<%= @exception.line_number %></b> raised:
10
+ Showing <i><%= @exception_wrapper.file_name %></i> where line <b>#<%= @exception_wrapper.line_number %></b> raised:
11
11
  </p>
12
- <pre><code><%= h @exception.message %></code></pre>
12
+ <pre><code><%= h @exception_wrapper.message %></code></pre>
13
13
 
14
14
  <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
15
15
 
16
- <p><%= @exception.sub_template_message %></p>
16
+ <p><%= @exception_wrapper.sub_template_message %></p>
17
17
 
18
18
  <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
19
19
  <%= render template: "rescues/_request_and_response" %>
@@ -2,5 +2,5 @@
2
2
  <h1>Unknown action</h1>
3
3
  </header>
4
4
  <main role="main" id="container">
5
- <%= render "rescues/message_and_suggestions", exception: @exception %>
5
+ <%= render "rescues/message_and_suggestions", exception: @exception, exception_wrapper: @exception_wrapper %>
6
6
  </main>
@@ -1,3 +1,3 @@
1
1
  Unknown action
2
2
 
3
- <%= @exception.message %>
3
+ <%= @exception_wrapper.message %>
@@ -13,4 +13,7 @@
13
13
  <td>
14
14
  <%=simple_format route[:reqs] %>
15
15
  </td>
16
+ <td>
17
+ <%=simple_format route[:source_location] %>
18
+ </td>
16
19
  </tr>