actionpack 7.0.8.4 → 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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +358 -362
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  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 +61 -18
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
  11. data/lib/abstract_controller/rendering.rb +3 -3
  12. data/lib/abstract_controller/translation.rb +1 -20
  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 +5 -3
  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/content_security_policy.rb +1 -1
  22. data/lib/action_controller/metal/data_streaming.rb +2 -0
  23. data/lib/action_controller/metal/default_headers.rb +2 -0
  24. data/lib/action_controller/metal/etag_with_flash.rb +2 -0
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  26. data/lib/action_controller/metal/exceptions.rb +8 -0
  27. data/lib/action_controller/metal/head.rb +8 -6
  28. data/lib/action_controller/metal/helpers.rb +3 -14
  29. data/lib/action_controller/metal/http_authentication.rb +17 -8
  30. data/lib/action_controller/metal/implicit_render.rb +5 -3
  31. data/lib/action_controller/metal/instrumentation.rb +8 -1
  32. data/lib/action_controller/metal/live.rb +24 -0
  33. data/lib/action_controller/metal/mime_responds.rb +2 -2
  34. data/lib/action_controller/metal/params_wrapper.rb +4 -2
  35. data/lib/action_controller/metal/permissions_policy.rb +1 -1
  36. data/lib/action_controller/metal/redirecting.rb +7 -7
  37. data/lib/action_controller/metal/renderers.rb +2 -2
  38. data/lib/action_controller/metal/rendering.rb +0 -7
  39. data/lib/action_controller/metal/request_forgery_protection.rb +139 -50
  40. data/lib/action_controller/metal/rescue.rb +2 -0
  41. data/lib/action_controller/metal/streaming.rb +70 -30
  42. data/lib/action_controller/metal/strong_parameters.rb +132 -52
  43. data/lib/action_controller/metal/url_for.rb +7 -0
  44. data/lib/action_controller/metal.rb +79 -21
  45. data/lib/action_controller/railtie.rb +22 -9
  46. data/lib/action_controller/renderer.rb +98 -65
  47. data/lib/action_controller/test_case.rb +15 -5
  48. data/lib/action_controller.rb +8 -1
  49. data/lib/action_dispatch/constants.rb +32 -0
  50. data/lib/action_dispatch/deprecator.rb +7 -0
  51. data/lib/action_dispatch/http/cache.rb +1 -3
  52. data/lib/action_dispatch/http/content_security_policy.rb +9 -8
  53. data/lib/action_dispatch/http/filter_parameters.rb +11 -5
  54. data/lib/action_dispatch/http/headers.rb +2 -0
  55. data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
  56. data/lib/action_dispatch/http/mime_type.rb +35 -12
  57. data/lib/action_dispatch/http/mime_types.rb +3 -1
  58. data/lib/action_dispatch/http/parameters.rb +1 -1
  59. data/lib/action_dispatch/http/permissions_policy.rb +38 -16
  60. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  61. data/lib/action_dispatch/http/request.rb +48 -14
  62. data/lib/action_dispatch/http/response.rb +80 -59
  63. data/lib/action_dispatch/http/upload.rb +2 -0
  64. data/lib/action_dispatch/journey/formatter.rb +8 -2
  65. data/lib/action_dispatch/journey/path/pattern.rb +14 -14
  66. data/lib/action_dispatch/journey/route.rb +3 -2
  67. data/lib/action_dispatch/journey/router.rb +9 -8
  68. data/lib/action_dispatch/journey/routes.rb +2 -2
  69. data/lib/action_dispatch/log_subscriber.rb +23 -0
  70. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
  71. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  72. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  73. data/lib/action_dispatch/middleware/cookies.rb +81 -98
  74. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
  75. data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
  76. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  77. data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
  78. data/lib/action_dispatch/middleware/executor.rb +1 -1
  79. data/lib/action_dispatch/middleware/flash.rb +7 -0
  80. data/lib/action_dispatch/middleware/host_authorization.rb +6 -3
  81. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  82. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  83. data/lib/action_dispatch/middleware/remote_ip.rb +17 -16
  84. data/lib/action_dispatch/middleware/request_id.rb +2 -0
  85. data/lib/action_dispatch/middleware/server_timing.rb +4 -4
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
  89. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  90. data/lib/action_dispatch/middleware/show_exceptions.rb +19 -15
  91. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  92. data/lib/action_dispatch/middleware/stack.rb +7 -2
  93. data/lib/action_dispatch/middleware/static.rb +12 -8
  94. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  95. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
  96. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  97. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  103. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
  104. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  105. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  106. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  107. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +46 -37
  108. data/lib/action_dispatch/railtie.rb +14 -4
  109. data/lib/action_dispatch/request/session.rb +16 -6
  110. data/lib/action_dispatch/request/utils.rb +8 -3
  111. data/lib/action_dispatch/routing/inspector.rb +54 -6
  112. data/lib/action_dispatch/routing/mapper.rb +35 -24
  113. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  114. data/lib/action_dispatch/routing/redirection.rb +15 -6
  115. data/lib/action_dispatch/routing/route_set.rb +52 -22
  116. data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
  117. data/lib/action_dispatch/routing/url_for.rb +5 -1
  118. data/lib/action_dispatch/routing.rb +7 -7
  119. data/lib/action_dispatch/system_test_case.rb +3 -3
  120. data/lib/action_dispatch/system_testing/browser.rb +20 -19
  121. data/lib/action_dispatch/system_testing/driver.rb +13 -21
  122. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
  123. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  124. data/lib/action_dispatch/testing/assertions/response.rb +13 -6
  125. data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
  126. data/lib/action_dispatch/testing/assertions.rb +3 -1
  127. data/lib/action_dispatch/testing/integration.rb +27 -17
  128. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  129. data/lib/action_dispatch/testing/test_process.rb +4 -3
  130. data/lib/action_dispatch/testing/test_request.rb +1 -1
  131. data/lib/action_dispatch/testing/test_response.rb +23 -9
  132. data/lib/action_dispatch.rb +37 -4
  133. data/lib/action_pack/gem_version.rb +3 -3
  134. data/lib/action_pack/version.rb +1 -1
  135. data/lib/action_pack.rb +1 -1
  136. metadata +62 -26
@@ -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,8 +109,8 @@
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
115
  function checkNoMatch(section, trElement) {
106
116
  if (section.children.length <= 1) {
@@ -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]'),
@@ -156,16 +166,16 @@
156
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 = buildTr('Paths Matching (' + path + '):'),
161
- defaultFuzzyMatch = buildTr('Paths Containing (' + path +'):'),
162
- noExactMatch = buildTr('No Exact Matches Found'),
163
- noFuzzyMatch = buildTr('No Fuzzy Matches Found');
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
180
  exactSection.replaceChildren(defaultExactMatch);
171
181
  fuzzySection.replaceChildren(defaultFuzzyMatch);
@@ -173,7 +183,6 @@
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,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,9 @@ 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
+ ActionDispatch::Routing::Mapper.backtrace_cleaner = Rails.backtrace_cleaner
73
+
64
74
  ActionDispatch.test_app = app
65
75
  end
66
76
  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 = 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
@@ -12,6 +12,9 @@ module ActionDispatch
12
12
  class Mapper
13
13
  URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
14
14
 
15
+ cattr_accessor :route_source_locations, instance_accessor: false, default: false
16
+ cattr_accessor :backtrace_cleaner, instance_accessor: false, default: ActiveSupport::BacktraceCleaner.new
17
+
15
18
  class Constraints < Routing::Endpoint # :nodoc:
16
19
  attr_reader :app, :constraints
17
20
 
@@ -43,7 +46,7 @@ module ActionDispatch
43
46
  end
44
47
 
45
48
  def serve(req)
46
- return [ 404, { "X-Cascade" => "pass" }, [] ] unless matches?(req)
49
+ return [ 404, { Constants::X_CASCADE => "pass" }, [] ] unless matches?(req)
47
50
 
48
51
  @strategy.call @app, req
49
52
  end
@@ -170,7 +173,7 @@ module ActionDispatch
170
173
  Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
171
174
  required_defaults: required_defaults, defaults: defaults,
172
175
  request_method_match: request_method, precedence: precedence,
173
- scope_options: scope_options, internal: @internal)
176
+ scope_options: scope_options, internal: @internal, source_location: route_source_location)
174
177
  end
175
178
 
176
179
  def application
@@ -214,9 +217,16 @@ module ActionDispatch
214
217
  if to.respond_to?(:action) || to.respond_to?(:call)
215
218
  options
216
219
  else
217
- to_endpoint = split_to to
218
- controller = to_endpoint[0] || default_controller
219
- action = to_endpoint[1] || default_action
220
+ if to.nil?
221
+ controller = default_controller
222
+ action = default_action
223
+ elsif to.is_a?(String) && to.include?("#")
224
+ to_endpoint = to.split("#").map!(&:-@)
225
+ controller = to_endpoint[0]
226
+ action = to_endpoint[1]
227
+ else
228
+ raise ArgumentError, ":to must respond to `action` or `call`, or it must be a String that includes '#'"
229
+ end
220
230
 
221
231
  controller = add_controller_module(controller, modyoule)
222
232
 
@@ -305,14 +315,6 @@ module ActionDispatch
305
315
  hash
306
316
  end
307
317
 
308
- def split_to(to)
309
- if /#/.match?(to)
310
- to.split("#").map!(&:-@)
311
- else
312
- []
313
- end
314
- end
315
-
316
318
  def add_controller_module(controller, modyoule)
317
319
  if modyoule && !controller.is_a?(Regexp)
318
320
  if controller&.start_with?("/")
@@ -356,6 +358,15 @@ module ActionDispatch
356
358
  def dispatcher(raise_on_name_error)
357
359
  Routing::RouteSet::Dispatcher.new raise_on_name_error
358
360
  end
361
+
362
+ def route_source_location
363
+ if Mapper.route_source_locations
364
+ action_dispatch_dir = File.expand_path("..", __dir__)
365
+ caller_location = caller_locations.find { |location| !location.path.include?(action_dispatch_dir) }
366
+ cleaned_path = Mapper.backtrace_cleaner.clean([caller_location.path]).first
367
+ "#{cleaned_path}:#{caller_location.lineno}" if cleaned_path
368
+ end
369
+ end
359
370
  end
360
371
 
361
372
  # Invokes Journey::Router::Utils.normalize_path, then ensures that
@@ -652,7 +663,7 @@ module ActionDispatch
652
663
 
653
664
  script_namer = ->(options) do
654
665
  prefix_options = options.slice(*_route.segment_keys)
655
- prefix_options[:relative_url_root] = ""
666
+ prefix_options[:script_name] = "" if options[:original_script_name]
656
667
 
657
668
  if options[:_recall]
658
669
  prefix_options.reverse_merge!(options[:_recall].slice(*_route.segment_keys))
@@ -748,7 +759,7 @@ module ActionDispatch
748
759
  # end
749
760
  #
750
761
  # This will create a number of routes for each of the posts and comments
751
- # controller. For <tt>Admin::PostsController</tt>, Rails will create:
762
+ # controller. For +Admin::PostsController+, \Rails will create:
752
763
  #
753
764
  # GET /admin/posts
754
765
  # GET /admin/posts/new
@@ -759,7 +770,7 @@ module ActionDispatch
759
770
  # DELETE /admin/posts/1
760
771
  #
761
772
  # If you want to route /posts (without the prefix /admin) to
762
- # <tt>Admin::PostsController</tt>, you could use
773
+ # +Admin::PostsController+, you could use
763
774
  #
764
775
  # scope module: "admin" do
765
776
  # resources :posts
@@ -808,7 +819,7 @@ module ActionDispatch
808
819
  #
809
820
  # Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
810
821
  #
811
- # # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt>
822
+ # # route /posts (without the prefix /admin) to +Admin::PostsController+
812
823
  # scope module: "admin" do
813
824
  # resources :posts
814
825
  # end
@@ -917,7 +928,7 @@ module ActionDispatch
917
928
  # resources :posts
918
929
  # end
919
930
  #
920
- # # maps to <tt>Sekret::PostsController</tt> rather than <tt>Admin::PostsController</tt>
931
+ # # maps to +Sekret::PostsController+ rather than +Admin::PostsController+
921
932
  # namespace :admin, module: "sekret" do
922
933
  # resources :posts
923
934
  # end
@@ -1318,7 +1329,7 @@ module ActionDispatch
1318
1329
  self
1319
1330
  end
1320
1331
 
1321
- # In Rails, a resourceful route provides a mapping between HTTP verbs
1332
+ # In \Rails, a resourceful route provides a mapping between HTTP verbs
1322
1333
  # and URLs and controller actions. By convention, each action also maps
1323
1334
  # to particular CRUD operations in a database. A single entry in the
1324
1335
  # routing file, such as
@@ -1450,7 +1461,7 @@ module ActionDispatch
1450
1461
  #
1451
1462
  # === Examples
1452
1463
  #
1453
- # # routes call <tt>Admin::PostsController</tt>
1464
+ # # routes call +Admin::PostsController+
1454
1465
  # resources :posts, module: "admin"
1455
1466
  #
1456
1467
  # # resource actions are at /admin/posts.
@@ -1493,7 +1504,7 @@ module ActionDispatch
1493
1504
  # end
1494
1505
  # end
1495
1506
  #
1496
- # This will enable Rails to recognize paths such as <tt>/photos/search</tt>
1507
+ # This will enable \Rails to recognize paths such as <tt>/photos/search</tt>
1497
1508
  # with GET, and route to the search action of +PhotosController+. It will also
1498
1509
  # create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
1499
1510
  # route helpers.
@@ -1640,7 +1651,7 @@ module ActionDispatch
1640
1651
  when Symbol
1641
1652
  options[:action] = to
1642
1653
  when String
1643
- if /#/.match?(to)
1654
+ if to.include?("#")
1644
1655
  options[:to] = to
1645
1656
  else
1646
1657
  options[:controller] = to
@@ -1663,7 +1674,7 @@ module ActionDispatch
1663
1674
  end
1664
1675
  end
1665
1676
 
1666
- # You can specify what Rails should route "/" to with the root method:
1677
+ # You can specify what \Rails should route "/" to with the root method:
1667
1678
  #
1668
1679
  # root to: 'pages#main'
1669
1680
  #
@@ -1675,7 +1686,7 @@ module ActionDispatch
1675
1686
  #
1676
1687
  # You should put the root route at the top of <tt>config/routes.rb</tt>,
1677
1688
  # because this means it will be matched first. As this is the most popular route
1678
- # of most Rails applications, this is beneficial.
1689
+ # of most \Rails applications, this is beneficial.
1679
1690
  def root(path, options = {})
1680
1691
  if path.is_a?(String)
1681
1692
  options[:to] = path
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActionDispatch
4
4
  module Routing
5
+ # = Action Dispatch Routing \PolymorphicRoutes
6
+ #
5
7
  # Polymorphic URL helpers are methods for smart resolution to a named route call when
6
8
  # given an Active Record model instance. They are to be used in combination with
7
9
  # ActionController::Resources.
@@ -18,10 +18,19 @@ module ActionDispatch
18
18
  def redirect?; true; end
19
19
 
20
20
  def call(env)
21
- serve Request.new env
21
+ ActiveSupport::Notifications.instrument("redirect.action_dispatch") do |payload|
22
+ request = Request.new(env)
23
+ response = build_response(request)
24
+
25
+ payload[:status] = @status
26
+ payload[:location] = response.headers["Location"]
27
+ payload[:request] = request
28
+
29
+ response.to_a
30
+ end
22
31
  end
23
32
 
24
- def serve(req)
33
+ def build_response(req)
25
34
  uri = URI.parse(path(req.path_parameters, req))
26
35
 
27
36
  unless uri.host
@@ -38,15 +47,15 @@ module ActionDispatch
38
47
 
39
48
  req.commit_flash
40
49
 
41
- body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>)
50
+ body = ""
42
51
 
43
52
  headers = {
44
53
  "Location" => uri.to_s,
45
- "Content-Type" => "text/html",
54
+ "Content-Type" => "text/html; charset=#{ActionDispatch::Response.default_charset}",
46
55
  "Content-Length" => body.length.to_s
47
56
  }
48
57
 
49
- [ status, headers, [body] ]
58
+ ActionDispatch::Response.new(status, headers, body)
50
59
  end
51
60
 
52
61
  def path(params, request)
@@ -59,7 +68,7 @@ module ActionDispatch
59
68
 
60
69
  private
61
70
  def relative_path?(path)
62
- path && !path.empty? && path[0] != "/"
71
+ path && !path.empty? && !path.start_with?("/")
63
72
  end
64
73
 
65
74
  def escape(params)