actionpack 6.1.7.5 → 7.1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -435
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +33 -37
  7. data/lib/abstract_controller/caching/fragments.rb +4 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +50 -11
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/deprecator.rb +7 -0
  12. data/lib/abstract_controller/error.rb +1 -1
  13. data/lib/abstract_controller/helpers.rb +78 -30
  14. data/lib/abstract_controller/logger.rb +1 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +12 -14
  17. data/lib/abstract_controller/translation.rb +26 -7
  18. data/lib/abstract_controller/url_for.rb +6 -6
  19. data/lib/abstract_controller.rb +6 -0
  20. data/lib/action_controller/api.rb +12 -10
  21. data/lib/action_controller/base.rb +8 -21
  22. data/lib/action_controller/caching.rb +2 -0
  23. data/lib/action_controller/deprecator.rb +7 -0
  24. data/lib/action_controller/form_builder.rb +4 -2
  25. data/lib/action_controller/log_subscriber.rb +20 -7
  26. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  27. data/lib/action_controller/metal/conditional_get.rb +137 -102
  28. data/lib/action_controller/metal/content_security_policy.rb +37 -3
  29. data/lib/action_controller/metal/cookies.rb +1 -1
  30. data/lib/action_controller/metal/data_streaming.rb +25 -31
  31. data/lib/action_controller/metal/default_headers.rb +2 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  34. data/lib/action_controller/metal/exceptions.rb +27 -30
  35. data/lib/action_controller/metal/flash.rb +6 -2
  36. data/lib/action_controller/metal/head.rb +9 -7
  37. data/lib/action_controller/metal/helpers.rb +5 -16
  38. data/lib/action_controller/metal/http_authentication.rb +78 -42
  39. data/lib/action_controller/metal/implicit_render.rb +5 -3
  40. data/lib/action_controller/metal/instrumentation.rb +62 -50
  41. data/lib/action_controller/metal/live.rb +67 -2
  42. data/lib/action_controller/metal/mime_responds.rb +5 -5
  43. data/lib/action_controller/metal/params_wrapper.rb +24 -13
  44. data/lib/action_controller/metal/permissions_policy.rb +20 -29
  45. data/lib/action_controller/metal/redirecting.rb +96 -23
  46. data/lib/action_controller/metal/renderers.rb +14 -15
  47. data/lib/action_controller/metal/rendering.rb +121 -16
  48. data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
  49. data/lib/action_controller/metal/rescue.rb +7 -4
  50. data/lib/action_controller/metal/streaming.rb +74 -36
  51. data/lib/action_controller/metal/strong_parameters.rb +254 -151
  52. data/lib/action_controller/metal/testing.rb +9 -2
  53. data/lib/action_controller/metal/url_for.rb +10 -5
  54. data/lib/action_controller/metal.rb +89 -34
  55. data/lib/action_controller/railtie.rb +66 -9
  56. data/lib/action_controller/renderer.rb +99 -85
  57. data/lib/action_controller/test_case.rb +42 -11
  58. data/lib/action_controller.rb +10 -6
  59. data/lib/action_dispatch/constants.rb +32 -0
  60. data/lib/action_dispatch/deprecator.rb +7 -0
  61. data/lib/action_dispatch/http/cache.rb +21 -16
  62. data/lib/action_dispatch/http/content_security_policy.rb +122 -44
  63. data/lib/action_dispatch/http/filter_parameters.rb +14 -23
  64. data/lib/action_dispatch/http/headers.rb +3 -1
  65. data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
  66. data/lib/action_dispatch/http/mime_type.rb +43 -22
  67. data/lib/action_dispatch/http/mime_types.rb +3 -1
  68. data/lib/action_dispatch/http/parameters.rb +6 -6
  69. data/lib/action_dispatch/http/permissions_policy.rb +57 -19
  70. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  71. data/lib/action_dispatch/http/request.rb +75 -51
  72. data/lib/action_dispatch/http/response.rb +81 -77
  73. data/lib/action_dispatch/http/upload.rb +15 -2
  74. data/lib/action_dispatch/http/url.rb +11 -19
  75. data/lib/action_dispatch/journey/formatter.rb +8 -2
  76. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  79. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  80. data/lib/action_dispatch/journey/path/pattern.rb +36 -27
  81. data/lib/action_dispatch/journey/route.rb +8 -14
  82. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  83. data/lib/action_dispatch/journey/router.rb +10 -9
  84. data/lib/action_dispatch/journey/routes.rb +5 -5
  85. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  86. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  87. data/lib/action_dispatch/log_subscriber.rb +23 -0
  88. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
  89. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  90. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  91. data/lib/action_dispatch/middleware/cookies.rb +97 -107
  92. data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
  93. data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
  94. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  95. data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
  96. data/lib/action_dispatch/middleware/executor.rb +3 -0
  97. data/lib/action_dispatch/middleware/flash.rb +24 -18
  98. data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
  99. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  100. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  101. data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
  102. data/lib/action_dispatch/middleware/request_id.rb +5 -3
  103. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
  105. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  106. data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
  107. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  108. data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
  109. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  110. data/lib/action_dispatch/middleware/stack.rb +34 -11
  111. data/lib/action_dispatch/middleware/static.rb +16 -16
  112. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  113. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
  114. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  115. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  116. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  119. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
  120. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  122. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
  123. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
  125. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
  126. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
  127. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
  131. data/lib/action_dispatch/railtie.rb +20 -4
  132. data/lib/action_dispatch/request/session.rb +59 -19
  133. data/lib/action_dispatch/request/utils.rb +8 -3
  134. data/lib/action_dispatch/routing/inspector.rb +55 -7
  135. data/lib/action_dispatch/routing/mapper.rb +117 -107
  136. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  137. data/lib/action_dispatch/routing/redirection.rb +20 -8
  138. data/lib/action_dispatch/routing/route_set.rb +67 -27
  139. data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
  140. data/lib/action_dispatch/routing/url_for.rb +29 -26
  141. data/lib/action_dispatch/routing.rb +12 -13
  142. data/lib/action_dispatch/system_test_case.rb +8 -8
  143. data/lib/action_dispatch/system_testing/browser.rb +20 -29
  144. data/lib/action_dispatch/system_testing/driver.rb +34 -18
  145. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
  146. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  147. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  148. data/lib/action_dispatch/testing/assertions/response.rb +14 -7
  149. data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
  150. data/lib/action_dispatch/testing/assertions.rb +3 -4
  151. data/lib/action_dispatch/testing/integration.rb +33 -25
  152. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  153. data/lib/action_dispatch/testing/test_process.rb +5 -30
  154. data/lib/action_dispatch/testing/test_request.rb +1 -1
  155. data/lib/action_dispatch/testing/test_response.rb +34 -2
  156. data/lib/action_dispatch.rb +38 -4
  157. data/lib/action_pack/gem_version.rb +4 -4
  158. data/lib/action_pack/version.rb +1 -1
  159. data/lib/action_pack.rb +1 -1
  160. metadata +67 -30
@@ -1,11 +1,11 @@
1
- <header>
1
+ <header role="banner">
2
2
  <h1>Template is missing</h1>
3
3
  </header>
4
4
 
5
- <div id="container">
6
- <h2><%= h @exception.message %></h2>
5
+ <main role="main" id="container">
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 %>
10
10
  <%= render template: "rescues/_request_and_response" %>
11
- </div>
11
+ </main>
@@ -1,13 +1,13 @@
1
- <header>
1
+ <header role="banner">
2
2
  <h1>Routing Error</h1>
3
3
  </header>
4
- <div id="container">
5
- <h2><%= h @exception.message %></h2>
6
- <% unless @exception.failures.empty? %>
4
+ <main role="main" id="container">
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>
@@ -29,4 +29,4 @@
29
29
  <% end %>
30
30
 
31
31
  <%= render template: "rescues/_request_and_response" %>
32
- </div>
32
+ </main>
@@ -1,20 +1,20 @@
1
- <header>
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
- <div id="container">
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" %>
20
- </div>
20
+ </main>
@@ -1,6 +1,6 @@
1
- <header>
1
+ <header role="banner">
2
2
  <h1>Unknown action</h1>
3
3
  </header>
4
- <div id="container">
5
- <%= render "rescues/message_and_suggestions", exception: @exception %>
6
- </div>
4
+ <main role="main" id="container">
5
+ <%= render "rescues/message_and_suggestions", exception: @exception, exception_wrapper: @exception_wrapper %>
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,54 +66,34 @@
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
- body {
55
- background-color: #222;
56
- color: #ECECEC;
57
- }
58
-
59
70
  #route_table tbody tr:nth-child(odd) {
60
- background: #333;
61
- }
62
-
63
- #route_table tbody tr:nth-child(even) {
64
- background: #444;
71
+ background: #282828;
65
72
  }
66
73
 
67
- #route_table tbody.exact_matches,
68
- #route_table tbody.fuzzy_matches {
69
- color: #333;
74
+ #route_table tbody.exact_matches tr,
75
+ #route_table tbody.fuzzy_matches tr {
76
+ background: DarkSlateGrey;
70
77
  }
71
78
  }
72
79
  <% end %>
73
80
 
74
- <table id='route_table' class='route_table'>
81
+ <table id='route_table'>
75
82
  <thead>
76
83
  <tr>
77
- <th>Helper</th>
78
- <th>HTTP Verb</th>
79
- <th>Path</th>
80
- <th>Controller#Action</th>
81
- </tr>
82
- <tr class='bottom'>
83
- <th><%# Helper %>
84
- <%= link_to "Path", "#", 'data-route-helper' => '_path',
84
+ <th>Helper
85
+ (<%= link_to "Path", "#", 'data-route-helper' => '_path',
85
86
  title: "Returns a relative path (without the http or domain)" %> /
86
87
  <%= link_to "Url", "#", 'data-route-helper' => '_url',
87
- title: "Returns an absolute URL (with the http and domain)" %>
88
- </th>
89
- <th><%# HTTP Verb %>
90
- </th>
91
- <th><%# Path %>
92
- <%= search_field(:path, nil, id: 'search', placeholder: "Path Match") %>
93
- </th>
94
- <th><%# Controller#action %>
88
+ title: "Returns an absolute URL (with the http and domain)" %>)
95
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>
96
97
  </tr>
97
98
  </thead>
98
99
  <tbody class='exact_matches' id='exact_matches'>
@@ -104,16 +105,16 @@
104
105
  </tbody>
105
106
  </table>
106
107
 
107
- <script type='text/javascript'>
108
+ <script>
108
109
  // support forEach iterator on NodeList
109
110
  NodeList.prototype.forEach = Array.prototype.forEach;
110
111
 
111
- // Enables path search functionality
112
- function setupMatchPaths() {
112
+ // Enables query search functionality
113
+ function setupMatchingRoutes() {
113
114
  // Check if there are any matched results in a section
114
- function checkNoMatch(section, noMatchText) {
115
+ function checkNoMatch(section, trElement) {
115
116
  if (section.children.length <= 1) {
116
- section.innerHTML += noMatchText;
117
+ section.appendChild(trElement);
117
118
  }
118
119
  }
119
120
 
@@ -137,8 +138,8 @@
137
138
  }
138
139
 
139
140
  // remove params or fragments
140
- function sanitizePath(path) {
141
- return path.replace(/[#?].*/, '');
141
+ function sanitizeQuery(query) {
142
+ return query.replace(/[#?].*/, '');
142
143
  }
143
144
 
144
145
  var pathElements = document.querySelectorAll('#route_table [data-route-path]'),
@@ -154,26 +155,34 @@
154
155
  }
155
156
  }
156
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
+
157
167
  // On key press perform a search for matching paths
158
168
  delayedKeyup(searchElem, function() {
159
- var path = sanitizePath(searchElem.value),
160
- defaultExactMatch = '<tr><th colspan="4">Paths Matching (' + path +'):</th></tr>',
161
- defaultFuzzyMatch = '<tr><th colspan="4">Paths Containing (' + path +'):</th></tr>',
162
- noExactMatch = '<tr><th colspan="4">No Exact Matches Found</th></tr>',
163
- 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');
164
174
 
165
- if (!path)
175
+ if (!query)
166
176
  return searchElem.onblur();
167
177
 
168
- getJSON('/rails/info/routes?path=' + path, function(matches){
178
+ getJSON('/rails/info/routes?query=' + query, function(matches){
169
179
  // Clear out results section
170
- exactSection.innerHTML = defaultExactMatch;
171
- fuzzySection.innerHTML = defaultFuzzyMatch;
180
+ exactSection.replaceChildren(defaultExactMatch);
181
+ fuzzySection.replaceChildren(defaultFuzzyMatch);
172
182
 
173
183
  // Display exact matches and fuzzy matches
174
184
  pathElements.forEach(function(elem) {
175
185
  var elemPath = elem.getAttribute('data-route-path');
176
-
177
186
  if (matches['exact'].indexOf(elemPath) != -1)
178
187
  exactSection.appendChild(elem.parentNode.cloneNode(true));
179
188
 
@@ -215,7 +224,7 @@
215
224
  });
216
225
  }
217
226
 
218
- setupMatchPaths();
227
+ setupMatchingRoutes();
219
228
  setupRouteToggleHelperLinks();
220
229
 
221
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,7 +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.request_id_header = ActionDispatch::Constants::X_REQUEST_ID
28
+ config.action_dispatch.log_rescued_responses = true
29
+ config.action_dispatch.debug_exception_log_level = :fatal
27
30
 
28
31
  config.action_dispatch.default_headers = {
29
32
  "X-Frame-Options" => "SAMEORIGIN",
@@ -38,11 +41,21 @@ module ActionDispatch
38
41
 
39
42
  config.eager_load_namespaces << ActionDispatch
40
43
 
44
+ initializer "action_dispatch.deprecator", before: :load_environment_config do |app|
45
+ app.deprecators[:action_dispatch] = ActionDispatch.deprecator
46
+ end
47
+
41
48
  initializer "action_dispatch.configure" do |app|
42
49
  ActionDispatch::Http::URL.secure_protocol = app.config.force_ssl
43
50
  ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
44
- ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
45
- ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge
51
+
52
+ ActiveSupport.on_load(:action_dispatch_request) do
53
+ self.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
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
57
+ ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge
58
+ end
46
59
 
47
60
  ActiveSupport.on_load(:action_dispatch_response) do
48
61
  self.default_charset = app.config.action_dispatch.default_charset || app.config.encoding
@@ -55,6 +68,9 @@ module ActionDispatch
55
68
  config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
56
69
  ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
57
70
 
71
+ ActionDispatch::Routing::Mapper.route_source_locations = Rails.env.development?
72
+ ActionDispatch::Routing::Mapper.backtrace_cleaner = Rails.backtrace_cleaner
73
+
58
74
  ActionDispatch.test_app = app
59
75
  end
60
76
  end
@@ -6,6 +6,7 @@ module ActionDispatch
6
6
  class Request
7
7
  # Session is responsible for lazily loading the session from store.
8
8
  class Session # :nodoc:
9
+ DisabledSessionError = Class.new(StandardError)
9
10
  ENV_SESSION_KEY = Rack::RACK_SESSION # :nodoc:
10
11
  ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS # :nodoc:
11
12
 
@@ -23,6 +24,12 @@ module ActionDispatch
23
24
  session
24
25
  end
25
26
 
27
+ def self.disabled(req)
28
+ new(nil, req, enabled: false).tap do
29
+ Session::Options.set(req, Session::Options.new(nil, { id: nil }))
30
+ end
31
+ end
32
+
26
33
  def self.find(req)
27
34
  req.get_header ENV_SESSION_KEY
28
35
  end
@@ -31,7 +38,11 @@ module ActionDispatch
31
38
  req.set_header ENV_SESSION_KEY, session
32
39
  end
33
40
 
34
- class Options #:nodoc:
41
+ def self.delete(req)
42
+ req.delete_header ENV_SESSION_KEY
43
+ end
44
+
45
+ class Options # :nodoc:
35
46
  def self.set(req, options)
36
47
  req.set_header ENV_SESSION_OPTIONS_KEY, options
37
48
  end
@@ -60,30 +71,40 @@ module ActionDispatch
60
71
  def values_at(*args); @delegate.values_at(*args); end
61
72
  end
62
73
 
63
- def initialize(by, req)
74
+ def initialize(by, req, enabled: true)
64
75
  @by = by
65
76
  @req = req
66
77
  @delegate = {}
67
78
  @loaded = false
68
79
  @exists = nil # We haven't checked yet.
80
+ @enabled = enabled
81
+ @id_was = nil
82
+ @id_was_initialized = false
69
83
  end
70
84
 
71
85
  def id
72
86
  options.id(@req)
73
87
  end
74
88
 
89
+ def enabled?
90
+ @enabled
91
+ end
92
+
75
93
  def options
76
94
  Options.find @req
77
95
  end
78
96
 
79
97
  def destroy
80
98
  clear
81
- options = self.options || {}
82
- @by.send(:delete_session, @req, options.id(@req), options)
83
99
 
84
- # Load the new sid to be written with the response.
85
- @loaded = false
86
- load_for_write!
100
+ if enabled?
101
+ options = self.options || {}
102
+ @by.send(:delete_session, @req, options.id(@req), options)
103
+
104
+ # Load the new sid to be written with the response.
105
+ @loaded = false
106
+ load_for_write!
107
+ end
87
108
  end
88
109
 
89
110
  # Returns value of the key stored in the session or
@@ -135,7 +156,7 @@ module ActionDispatch
135
156
 
136
157
  # Clears the session.
137
158
  def clear
138
- load_for_write!
159
+ load_for_delete!
139
160
  @delegate.clear
140
161
  end
141
162
 
@@ -157,13 +178,18 @@ module ActionDispatch
157
178
  # session.to_hash
158
179
  # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
159
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
+
160
185
  load_for_write!
161
- @delegate.update hash.stringify_keys
186
+ @delegate.update hash.to_hash.stringify_keys
162
187
  end
188
+ alias :merge! :update
163
189
 
164
190
  # Deletes given key from the session.
165
191
  def delete(key)
166
- load_for_write!
192
+ load_for_delete!
167
193
  @delegate.delete key.to_s
168
194
  end
169
195
 
@@ -199,6 +225,7 @@ module ActionDispatch
199
225
  end
200
226
 
201
227
  def exists?
228
+ return false unless enabled?
202
229
  return @exists unless @exists.nil?
203
230
  @exists = @by.send(:session_exists?, @req)
204
231
  end
@@ -212,28 +239,41 @@ module ActionDispatch
212
239
  @delegate.empty?
213
240
  end
214
241
 
215
- def merge!(other)
216
- load_for_write!
217
- @delegate.merge!(other)
218
- end
219
-
220
242
  def each(&block)
221
243
  to_hash.each(&block)
222
244
  end
223
245
 
246
+ def id_was
247
+ load_for_read!
248
+ @id_was
249
+ end
250
+
224
251
  private
225
252
  def load_for_read!
226
253
  load! if !loaded? && exists?
227
254
  end
228
255
 
229
256
  def load_for_write!
230
- load! unless loaded?
257
+ if enabled?
258
+ load! unless loaded?
259
+ else
260
+ raise DisabledSessionError, "Your application has sessions disabled. To write to the session you must first configure a session store"
261
+ end
262
+ end
263
+
264
+ def load_for_delete!
265
+ load! if enabled? && !loaded?
231
266
  end
232
267
 
233
268
  def load!
234
- id, session = @by.load_session @req
235
- options[:id] = id
236
- @delegate.replace(session.stringify_keys)
269
+ if enabled?
270
+ @id_was_initialized = true unless exists?
271
+ id, session = @by.load_session @req
272
+ options[:id] = id
273
+ @delegate.replace(session.stringify_keys)
274
+ @id_was = id unless @id_was_initialized
275
+ end
276
+ @id_was_initialized = true
237
277
  @loaded = true
238
278
  end
239
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
@@ -5,9 +5,22 @@ require "io/console/size"
5
5
 
6
6
  module ActionDispatch
7
7
  module Routing
8
- class RouteWrapper < SimpleDelegator
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 = URI::DEFAULT_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