actionpack 7.0.4 → 7.1.5.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.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +495 -257
  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 +11 -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 +15 -9
  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 +29 -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 +200 -103
  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 +18 -8
  50. data/lib/action_controller.rb +13 -3
  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 +9 -11
  54. data/lib/action_dispatch/http/content_security_policy.rb +35 -13
  55. data/lib/action_dispatch/http/filter_parameters.rb +23 -32
  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 +37 -11
  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 +85 -32
  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 +108 -117
  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 +7 -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 +39 -22
  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 +13 -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 +97 -26
  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 +53 -23
  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 +25 -19
  125. data/lib/action_dispatch/system_testing/driver.rb +15 -23
  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 +41 -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 +68 -32
@@ -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_path %>/<%= @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>
@@ -1,24 +1,45 @@
1
1
  <% content_for :style do %>
2
+ h2, p {
3
+ padding-left: 30px;
4
+ }
5
+
2
6
  #route_table {
3
7
  margin: 0;
4
8
  border-collapse: collapse;
9
+ word-wrap:break-word;
10
+ table-layout: fixed;
11
+ width:100%;
5
12
  }
6
13
 
7
14
  #route_table thead tr {
8
15
  border-bottom: 2px solid #ddd;
9
16
  }
10
17
 
18
+ #route_table th {
19
+ padding-left: 30px;
20
+ text-align: left;
21
+ }
22
+
11
23
  #route_table thead tr.bottom {
12
24
  border-bottom: none;
13
25
  }
14
26
 
15
27
  #route_table thead tr.bottom th {
16
- padding: 10px 0;
28
+ padding: 10px 30px;
17
29
  line-height: 15px;
18
30
  }
19
31
 
20
- #route_table thead tr.bottom th input#search {
32
+ #route_table #search_container {
33
+ padding: 7px 30px;
34
+ }
35
+
36
+ #route_table thead tr th input#search {
21
37
  -webkit-appearance: textfield;
38
+ width:100%;
39
+ }
40
+
41
+ #route_table thead th.http-verb {
42
+ width: 10%;
22
43
  }
23
44
 
24
45
  #route_table tbody tr {
@@ -45,11 +66,6 @@
45
66
  padding: 4px 30px;
46
67
  }
47
68
 
48
- #path_search {
49
- width: 80%;
50
- font-size: inherit;
51
- }
52
-
53
69
  @media (prefers-color-scheme: dark) {
54
70
  #route_table tbody tr:nth-child(odd) {
55
71
  background: #282828;
@@ -62,28 +78,22 @@
62
78
  }
63
79
  <% end %>
64
80
 
65
- <table id='route_table' class='route_table'>
81
+ <table id='route_table'>
66
82
  <thead>
67
83
  <tr>
68
- <th>Helper</th>
69
- <th>HTTP Verb</th>
70
- <th>Path</th>
71
- <th>Controller#Action</th>
72
- </tr>
73
- <tr class='bottom'>
74
- <th><%# Helper %>
75
- <%= link_to "Path", "#", 'data-route-helper' => '_path',
84
+ <th>Helper
85
+ (<%= link_to "Path", "#", 'data-route-helper' => '_path',
76
86
  title: "Returns a relative path (without the http or domain)" %> /
77
87
  <%= link_to "Url", "#", 'data-route-helper' => '_url',
78
- title: "Returns an absolute URL (with the http and domain)" %>
79
- </th>
80
- <th><%# HTTP Verb %>
81
- </th>
82
- <th><%# Path %>
83
- <%= search_field(:path, nil, id: 'search', placeholder: "Path Match") %>
84
- </th>
85
- <th><%# Controller#action %>
88
+ title: "Returns an absolute URL (with the http and domain)" %>)
86
89
  </th>
90
+ <th class="http-verb">HTTP Verb</th>
91
+ <th>Path</th>
92
+ <th>Controller#Action</th>
93
+ <th>Source Location</th>
94
+ </tr>
95
+ <tr>
96
+ <th colspan="5" id="search_container"><%= search_field(:query, nil, id: 'search', placeholder: "Search") %></th>
87
97
  </tr>
88
98
  </thead>
89
99
  <tbody class='exact_matches' id='exact_matches'>
@@ -99,12 +109,12 @@
99
109
  // support forEach iterator on NodeList
100
110
  NodeList.prototype.forEach = Array.prototype.forEach;
101
111
 
102
- // Enables path search functionality
103
- function setupMatchPaths() {
112
+ // Enables query search functionality
113
+ function setupMatchingRoutes() {
104
114
  // Check if there are any matched results in a section
105
- function checkNoMatch(section, noMatchText) {
115
+ function checkNoMatch(section, trElement) {
106
116
  if (section.children.length <= 1) {
107
- section.innerHTML += noMatchText;
117
+ section.appendChild(trElement);
108
118
  }
109
119
  }
110
120
 
@@ -128,8 +138,8 @@
128
138
  }
129
139
 
130
140
  // remove params or fragments
131
- function sanitizePath(path) {
132
- return path.replace(/[#?].*/, '');
141
+ function sanitizeQuery(query) {
142
+ return query.replace(/[#?].*/, '');
133
143
  }
134
144
 
135
145
  var pathElements = document.querySelectorAll('#route_table [data-route-path]'),
@@ -145,26 +155,34 @@
145
155
  }
146
156
  }
147
157
 
158
+ function buildTr(string) {
159
+ var tr = document.createElement('tr');
160
+ var th = document.createElement('th');
161
+ th.setAttribute('colspan', 4);
162
+ tr.appendChild(th);
163
+ th.innerText = string;
164
+ return tr;
165
+ }
166
+
148
167
  // On key press perform a search for matching paths
149
168
  delayedKeyup(searchElem, function() {
150
- var path = sanitizePath(searchElem.value),
151
- defaultExactMatch = '<tr><th colspan="4">Paths Matching (' + path +'):</th></tr>',
152
- defaultFuzzyMatch = '<tr><th colspan="4">Paths Containing (' + path +'):</th></tr>',
153
- noExactMatch = '<tr><th colspan="4">No Exact Matches Found</th></tr>',
154
- noFuzzyMatch = '<tr><th colspan="4">No Fuzzy Matches Found</th></tr>';
169
+ var query = sanitizeQuery(searchElem.value),
170
+ defaultExactMatch = buildTr("Routes matching '" + query + "':"),
171
+ defaultFuzzyMatch = buildTr("Routes containing '" + query + "':"),
172
+ noExactMatch = buildTr('No exact matches found'),
173
+ noFuzzyMatch = buildTr('No fuzzy matches found');
155
174
 
156
- if (!path)
175
+ if (!query)
157
176
  return searchElem.onblur();
158
177
 
159
- getJSON('/rails/info/routes?path=' + path, function(matches){
178
+ getJSON('/rails/info/routes?query=' + query, function(matches){
160
179
  // Clear out results section
161
- exactSection.innerHTML = defaultExactMatch;
162
- fuzzySection.innerHTML = defaultFuzzyMatch;
180
+ exactSection.replaceChildren(defaultExactMatch);
181
+ fuzzySection.replaceChildren(defaultFuzzyMatch);
163
182
 
164
183
  // Display exact matches and fuzzy matches
165
184
  pathElements.forEach(function(elem) {
166
185
  var elemPath = elem.getAttribute('data-route-path');
167
-
168
186
  if (matches['exact'].indexOf(elemPath) != -1)
169
187
  exactSection.appendChild(elem.parentNode.cloneNode(true));
170
188
 
@@ -206,7 +224,7 @@
206
224
  });
207
225
  }
208
226
 
209
- setupMatchPaths();
227
+ setupMatchingRoutes();
210
228
  setupRouteToggleHelperLinks();
211
229
 
212
230
  // Focus the search input after page has loaded
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "action_dispatch"
4
+ require "action_dispatch/log_subscriber"
4
5
  require "active_support/messages/rotation_configuration"
5
6
 
6
7
  module ActionDispatch
@@ -8,7 +9,7 @@ module ActionDispatch
8
9
  config.action_dispatch = ActiveSupport::OrderedOptions.new
9
10
  config.action_dispatch.x_sendfile_header = nil
10
11
  config.action_dispatch.ip_spoofing_check = true
11
- config.action_dispatch.show_exceptions = true
12
+ config.action_dispatch.show_exceptions = :all
12
13
  config.action_dispatch.tld_length = 1
13
14
  config.action_dispatch.ignore_accept_header = false
14
15
  config.action_dispatch.rescue_templates = {}
@@ -23,9 +24,9 @@ module ActionDispatch
23
24
  config.action_dispatch.use_authenticated_cookie_encryption = false
24
25
  config.action_dispatch.use_cookies_with_metadata = false
25
26
  config.action_dispatch.perform_deep_munge = true
26
- config.action_dispatch.request_id_header = "X-Request-Id"
27
- config.action_dispatch.return_only_request_media_type_on_content_type = true
27
+ config.action_dispatch.request_id_header = ActionDispatch::Constants::X_REQUEST_ID
28
28
  config.action_dispatch.log_rescued_responses = true
29
+ config.action_dispatch.debug_exception_log_level = :fatal
29
30
 
30
31
  config.action_dispatch.default_headers = {
31
32
  "X-Frame-Options" => "SAMEORIGIN",
@@ -40,13 +41,19 @@ module ActionDispatch
40
41
 
41
42
  config.eager_load_namespaces << ActionDispatch
42
43
 
44
+ initializer "action_dispatch.deprecator", before: :load_environment_config do |app|
45
+ app.deprecators[:action_dispatch] = ActionDispatch.deprecator
46
+ end
47
+
43
48
  initializer "action_dispatch.configure" do |app|
44
49
  ActionDispatch::Http::URL.secure_protocol = app.config.force_ssl
45
50
  ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
46
51
 
47
52
  ActiveSupport.on_load(:action_dispatch_request) do
48
53
  self.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
49
- self.return_only_media_type_on_content_type = app.config.action_dispatch.return_only_request_media_type_on_content_type
54
+ unless app.config.action_dispatch.respond_to?(:return_only_request_media_type_on_content_type)
55
+ self.return_only_media_type_on_content_type = app.config.action_dispatch.return_only_request_media_type_on_content_type
56
+ end
50
57
  ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge
51
58
  end
52
59
 
@@ -61,6 +68,8 @@ module ActionDispatch
61
68
  config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
62
69
  ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
63
70
 
71
+ ActionDispatch::Routing::Mapper.route_source_locations = Rails.env.development?
72
+
64
73
  ActionDispatch.test_app = app
65
74
  end
66
75
  end
@@ -78,6 +78,8 @@ module ActionDispatch
78
78
  @loaded = false
79
79
  @exists = nil # We haven't checked yet.
80
80
  @enabled = enabled
81
+ @id_was = nil
82
+ @id_was_initialized = false
81
83
  end
82
84
 
83
85
  def id
@@ -176,9 +178,14 @@ module ActionDispatch
176
178
  # session.to_hash
177
179
  # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
178
180
  def update(hash)
181
+ unless hash.respond_to?(:to_hash)
182
+ raise TypeError, "no implicit conversion of #{hash.class.name} into Hash"
183
+ end
184
+
179
185
  load_for_write!
180
- @delegate.update hash.stringify_keys
186
+ @delegate.update hash.to_hash.stringify_keys
181
187
  end
188
+ alias :merge! :update
182
189
 
183
190
  # Deletes given key from the session.
184
191
  def delete(key)
@@ -232,15 +239,15 @@ module ActionDispatch
232
239
  @delegate.empty?
233
240
  end
234
241
 
235
- def merge!(other)
236
- load_for_write!
237
- @delegate.merge!(other)
238
- end
239
-
240
242
  def each(&block)
241
243
  to_hash.each(&block)
242
244
  end
243
245
 
246
+ def id_was
247
+ load_for_read!
248
+ @id_was
249
+ end
250
+
244
251
  private
245
252
  def load_for_read!
246
253
  load! if !loaded? && exists?
@@ -260,10 +267,13 @@ module ActionDispatch
260
267
 
261
268
  def load!
262
269
  if enabled?
270
+ @id_was_initialized = true unless exists?
263
271
  id, session = @by.load_session @req
264
272
  options[:id] = id
265
273
  @delegate.replace(session.stringify_keys)
274
+ @id_was = id unless @id_was_initialized
266
275
  end
276
+ @id_was_initialized = true
267
277
  @loaded = true
268
278
  end
269
279
  end
@@ -55,9 +55,11 @@ module ActionDispatch
55
55
  if params.has_key?(:tempfile)
56
56
  ActionDispatch::Http::UploadedFile.new(params)
57
57
  else
58
- params.transform_values do |val|
59
- normalize_encode_params(val)
60
- end.with_indifferent_access
58
+ hwia = ActiveSupport::HashWithIndifferentAccess.new
59
+ params.each_pair do |key, val|
60
+ hwia[key] = normalize_encode_params(val)
61
+ end
62
+ hwia
61
63
  end
62
64
  else
63
65
  params
@@ -83,6 +85,9 @@ module ActionDispatch
83
85
  return params unless controller && controller.valid_encoding? && encoding_template = action_encoding_template(request, controller, action)
84
86
  params.except(:controller, :action).each do |key, value|
85
87
  ActionDispatch::Request::Utils.each_param_value(value) do |param|
88
+ # If `param` is frozen, it comes from the router defaults
89
+ next if param.frozen?
90
+
86
91
  if encoding_template[key.to_s]
87
92
  param.force_encoding(encoding_template[key.to_s])
88
93
  end
@@ -6,8 +6,21 @@ require "io/console/size"
6
6
  module ActionDispatch
7
7
  module Routing
8
8
  class RouteWrapper < SimpleDelegator # :nodoc:
9
+ def matches_filter?(filter, value)
10
+ return __getobj__.path.match(value) if filter == :exact_path_match
11
+
12
+ value.match?(public_send(filter))
13
+ end
14
+
9
15
  def endpoint
10
- app.dispatcher? ? "#{controller}##{action}" : rack_app.inspect
16
+ case
17
+ when app.dispatcher?
18
+ "#{controller}##{action}"
19
+ when rack_app.is_a?(Proc)
20
+ "Inline handler (Proc/Lambda)"
21
+ else
22
+ rack_app.inspect
23
+ end
11
24
  end
12
25
 
13
26
  def constraints
@@ -85,8 +98,18 @@ module ActionDispatch
85
98
  if filter[:controller]
86
99
  { controller: /#{filter[:controller].underscore.sub(/_?controller\z/, "")}/ }
87
100
  elsif filter[:grep]
88
- { controller: /#{filter[:grep]}/, action: /#{filter[:grep]}/,
89
- verb: /#{filter[:grep]}/, name: /#{filter[:grep]}/, path: /#{filter[:grep]}/ }
101
+ grep_pattern = Regexp.new(filter[:grep])
102
+ path = RFC2396_PARSER.escape(filter[:grep])
103
+ normalized_path = ("/" + path).squeeze("/")
104
+
105
+ {
106
+ controller: grep_pattern,
107
+ action: grep_pattern,
108
+ verb: grep_pattern,
109
+ name: grep_pattern,
110
+ path: grep_pattern,
111
+ exact_path_match: normalized_path,
112
+ }
90
113
  end
91
114
  end
92
115
 
@@ -94,7 +117,7 @@ module ActionDispatch
94
117
  if filter
95
118
  @routes.select do |route|
96
119
  route_wrapper = RouteWrapper.new(route)
97
- filter.any? { |default, value| value.match?(route_wrapper.send(default)) }
120
+ filter.any? { |filter_type, value| route_wrapper.matches_filter?(filter_type, value) }
98
121
  end
99
122
  else
100
123
  @routes
@@ -110,7 +133,8 @@ module ActionDispatch
110
133
  { name: route.name,
111
134
  verb: route.verb,
112
135
  path: route.path,
113
- reqs: route.reqs }
136
+ reqs: route.reqs,
137
+ source_location: route.source_location }
114
138
  end
115
139
  end
116
140
 
@@ -216,13 +240,16 @@ module ActionDispatch
216
240
  private
217
241
  def draw_expanded_section(routes)
218
242
  routes.map.each_with_index do |r, i|
219
- <<~MESSAGE.chomp
243
+ route_rows = <<~MESSAGE.chomp
220
244
  #{route_header(index: i + 1)}
221
245
  Prefix | #{r[:name]}
222
246
  Verb | #{r[:verb]}
223
247
  URI | #{r[:path]}
224
248
  Controller#Action | #{r[:reqs]}
225
249
  MESSAGE
250
+ source_location = "\nSource Location | #{r[:source_location]}"
251
+ route_rows += source_location if r[:source_location].present?
252
+ route_rows
226
253
  end
227
254
  end
228
255
 
@@ -230,6 +257,27 @@ module ActionDispatch
230
257
  "--[ Route #{index} ]".ljust(@width, "-")
231
258
  end
232
259
  end
260
+
261
+ class Unused < Sheet
262
+ def header(routes)
263
+ @buffer << <<~MSG
264
+ Found #{routes.count} unused #{"route".pluralize(routes.count)}:
265
+ MSG
266
+
267
+ super
268
+ end
269
+
270
+ def no_routes(routes, filter)
271
+ @buffer <<
272
+ if filter.none?
273
+ "No unused routes found."
274
+ elsif filter.key?(:controller)
275
+ "No unused routes found for this controller."
276
+ elsif filter.key?(:grep)
277
+ "No unused routes found for this grep pattern."
278
+ end
279
+ end
280
+ end
233
281
  end
234
282
 
235
283
  class HtmlTableFormatter