actionpack 5.2.3.rc1 → 6.0.0.beta1

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +124 -337
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/abstract_controller/base.rb +4 -2
  6. data/lib/abstract_controller/caching/fragments.rb +6 -21
  7. data/lib/abstract_controller/callbacks.rb +12 -0
  8. data/lib/abstract_controller/collector.rb +1 -1
  9. data/lib/abstract_controller/helpers.rb +2 -2
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  11. data/lib/action_controller.rb +1 -0
  12. data/lib/action_controller/api.rb +2 -1
  13. data/lib/action_controller/base.rb +2 -7
  14. data/lib/action_controller/caching.rb +1 -1
  15. data/lib/action_controller/log_subscriber.rb +8 -5
  16. data/lib/action_controller/metal/conditional_get.rb +9 -3
  17. data/lib/action_controller/metal/data_streaming.rb +5 -6
  18. data/lib/action_controller/metal/default_headers.rb +17 -0
  19. data/lib/action_controller/metal/exceptions.rb +22 -1
  20. data/lib/action_controller/metal/flash.rb +5 -5
  21. data/lib/action_controller/metal/force_ssl.rb +17 -57
  22. data/lib/action_controller/metal/head.rb +1 -1
  23. data/lib/action_controller/metal/helpers.rb +1 -2
  24. data/lib/action_controller/metal/http_authentication.rb +20 -21
  25. data/lib/action_controller/metal/implicit_render.rb +2 -12
  26. data/lib/action_controller/metal/instrumentation.rb +3 -5
  27. data/lib/action_controller/metal/live.rb +28 -26
  28. data/lib/action_controller/metal/mime_responds.rb +13 -2
  29. data/lib/action_controller/metal/params_wrapper.rb +16 -12
  30. data/lib/action_controller/metal/redirecting.rb +32 -11
  31. data/lib/action_controller/metal/rendering.rb +1 -1
  32. data/lib/action_controller/metal/request_forgery_protection.rb +22 -11
  33. data/lib/action_controller/metal/strong_parameters.rb +57 -32
  34. data/lib/action_controller/metal/url_for.rb +1 -1
  35. data/lib/action_controller/railties/helpers.rb +1 -1
  36. data/lib/action_controller/renderer.rb +15 -2
  37. data/lib/action_controller/test_case.rb +1 -4
  38. data/lib/action_dispatch.rb +3 -1
  39. data/lib/action_dispatch/http/cache.rb +14 -10
  40. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  41. data/lib/action_dispatch/http/content_security_policy.rb +9 -8
  42. data/lib/action_dispatch/http/filter_parameters.rb +8 -6
  43. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  44. data/lib/action_dispatch/http/headers.rb +1 -1
  45. data/lib/action_dispatch/http/mime_negotiation.rb +7 -10
  46. data/lib/action_dispatch/http/mime_type.rb +1 -5
  47. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  48. data/lib/action_dispatch/http/parameters.rb +13 -3
  49. data/lib/action_dispatch/http/request.rb +10 -13
  50. data/lib/action_dispatch/http/response.rb +18 -17
  51. data/lib/action_dispatch/http/upload.rb +5 -0
  52. data/lib/action_dispatch/http/url.rb +81 -81
  53. data/lib/action_dispatch/journey/formatter.rb +1 -1
  54. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  55. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  56. data/lib/action_dispatch/journey/path/pattern.rb +3 -3
  57. data/lib/action_dispatch/journey/router.rb +0 -3
  58. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  59. data/lib/action_dispatch/journey/scanner.rb +11 -4
  60. data/lib/action_dispatch/journey/visitors.rb +1 -1
  61. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  62. data/lib/action_dispatch/middleware/cookies.rb +49 -70
  63. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -58
  64. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  65. data/lib/action_dispatch/middleware/debug_view.rb +50 -0
  66. data/lib/action_dispatch/middleware/exception_wrapper.rb +36 -7
  67. data/lib/action_dispatch/middleware/flash.rb +1 -1
  68. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  69. data/lib/action_dispatch/middleware/remote_ip.rb +6 -8
  70. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  71. data/lib/action_dispatch/middleware/session/cookie_store.rb +4 -10
  72. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  73. data/lib/action_dispatch/middleware/static.rb +5 -6
  74. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  75. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  76. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  77. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  78. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +20 -2
  79. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -4
  80. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
  81. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  82. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  83. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  84. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  85. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  86. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  87. data/lib/action_dispatch/railtie.rb +1 -0
  88. data/lib/action_dispatch/request/session.rb +8 -0
  89. data/lib/action_dispatch/routing.rb +3 -2
  90. data/lib/action_dispatch/routing/inspector.rb +99 -50
  91. data/lib/action_dispatch/routing/mapper.rb +36 -29
  92. data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -4
  93. data/lib/action_dispatch/routing/route_set.rb +11 -12
  94. data/lib/action_dispatch/routing/url_for.rb +1 -0
  95. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +3 -3
  96. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  97. data/lib/action_dispatch/testing/assertions/routing.rb +7 -2
  98. data/lib/action_dispatch/testing/integration.rb +11 -4
  99. data/lib/action_dispatch/testing/test_process.rb +2 -2
  100. data/lib/action_dispatch/testing/test_response.rb +4 -32
  101. data/lib/action_pack.rb +1 -1
  102. data/lib/action_pack/gem_version.rb +4 -4
  103. metadata +19 -11
@@ -1,52 +1,62 @@
1
- <% names = @traces.keys %>
1
+ <% names = traces.keys %>
2
+ <% error_index = local_assigns[:error_index] || 0 %>
2
3
 
3
4
  <p><code>Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %></code></p>
4
5
 
5
- <div id="traces">
6
+ <div id="traces-<%= error_index %>">
6
7
  <% names.each do |name| %>
7
8
  <%
8
- show = "show('#{name.gsub(/\s/, '-')}');"
9
- hide = (names - [name]).collect {|hide_name| "hide('#{hide_name.gsub(/\s/, '-')}');"}
9
+ show = "show('#{name.gsub(/\s/, '-')}-#{error_index}');"
10
+ hide = (names - [name]).collect {|hide_name| "hide('#{hide_name.gsub(/\s/, '-')}-#{error_index}');"}
10
11
  %>
11
12
  <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
12
13
  <% end %>
13
14
 
14
- <% @traces.each do |name, trace| %>
15
- <div id="<%= name.gsub(/\s/, '-') %>" style="display: <%= (name == @trace_to_show) ? 'block' : 'none' %>;">
16
- <pre><code><% trace.each do |frame| %><a class="trace-frames" data-frame-id="<%= frame[:id] %>" href="#"><%= frame[:trace] %></a><br><% end %></code></pre>
15
+ <% traces.each do |name, trace| %>
16
+ <div id="<%= "#{name.gsub(/\s/, '-')}-#{error_index}" %>" style="display: <%= (name == trace_to_show) ? 'block' : 'none' %>;">
17
+ <code style="font-size: 11px;">
18
+ <% trace.each do |frame| %>
19
+ <a class="trace-frames trace-frames-<%= error_index %>" data-exception-object-id="<%= frame[:exception_object_id] %>" data-frame-id="<%= frame[:id] %>" href="#">
20
+ <%= frame[:trace] %>
21
+ </a>
22
+ <br>
23
+ <% end %>
24
+ </code>
17
25
  </div>
18
26
  <% end %>
19
27
 
20
28
  <script type="text/javascript">
21
- var traceFrames = document.getElementsByClassName('trace-frames');
22
- var selectedFrame, currentSource = document.getElementById('frame-source-0');
23
-
24
- // Add click listeners for all stack frames
25
- for (var i = 0; i < traceFrames.length; i++) {
26
- traceFrames[i].addEventListener('click', function(e) {
27
- e.preventDefault();
28
- var target = e.target;
29
- var frame_id = target.dataset.frameId;
30
-
31
- if (selectedFrame) {
32
- selectedFrame.className = selectedFrame.className.replace("selected", "");
33
- }
34
-
35
- target.className += " selected";
36
- selectedFrame = target;
37
-
38
- // Change the extracted source code
39
- changeSourceExtract(frame_id);
40
- });
41
-
42
- function changeSourceExtract(frame_id) {
43
- var el = document.getElementById('frame-source-' + frame_id);
44
- if (currentSource && el) {
45
- currentSource.className += " hidden";
46
- el.className = el.className.replace(" hidden", "");
47
- currentSource = el;
29
+ (function() {
30
+ var traceFrames = document.getElementsByClassName('trace-frames-<%= error_index %>');
31
+ var selectedFrame, currentSource = document.getElementById('frame-source-<%= error_index %>-0');
32
+
33
+ // Add click listeners for all stack frames
34
+ for (var i = 0; i < traceFrames.length; i++) {
35
+ traceFrames[i].addEventListener('click', function(e) {
36
+ e.preventDefault();
37
+ var target = e.target;
38
+ var frame_id = target.dataset.frameId;
39
+
40
+ if (selectedFrame) {
41
+ selectedFrame.className = selectedFrame.className.replace("selected", "");
42
+ }
43
+
44
+ target.className += " selected";
45
+ selectedFrame = target;
46
+
47
+ // Change the extracted source code
48
+ changeSourceExtract(frame_id);
49
+ });
50
+
51
+ function changeSourceExtract(frame_id) {
52
+ var el = document.getElementById('frame-source-<%= error_index %>-' + frame_id);
53
+ if (currentSource && el) {
54
+ currentSource.className += " hidden";
55
+ el.className = el.className.replace(" hidden", "");
56
+ currentSource = el;
57
+ }
48
58
  }
49
59
  }
50
- }
60
+ })();
51
61
  </script>
52
62
  </div>
@@ -0,0 +1,7 @@
1
+ <header>
2
+ <h1>Blocked host: <%= @host %></h1>
3
+ </header>
4
+ <div id="container">
5
+ <h2>To allow requests to <%= @host %>, add the following configuration:</h2>
6
+ <pre>Rails.application.config.hosts &lt;&lt; "<%= @host %>"</pre>
7
+ </div>
@@ -0,0 +1,5 @@
1
+ Blocked host: <%= @host %>
2
+
3
+ To allow requests to <%= @host %>, add the following configuration:
4
+
5
+ Rails.application.config.hosts << "<%= @host %>"
@@ -10,7 +10,25 @@
10
10
  <div id="container">
11
11
  <h2><%= h @exception.message %></h2>
12
12
 
13
- <%= render template: "rescues/_source" %>
14
- <%= render template: "rescues/_trace" %>
13
+ <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx, error_index: 0 %>
14
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show, error_index: 0 %>
15
+
16
+ <% if @exception.cause %>
17
+ <h2>Exception Causes</h2>
18
+ <% end %>
19
+
20
+ <% @exception_wrapper.wrapped_causes.each.with_index(1) do |wrapper, index| %>
21
+ <div class="details">
22
+ <a class="summary" href="#" style="color: #F0F0F0; text-decoration: none; background: #C52F24; border-bottom: none;" onclick="return toggle(<%= wrapper.exception.object_id %>)">
23
+ <%= wrapper.exception.class.name %>: <%= h wrapper.exception.message %>
24
+ </a>
25
+ </div>
26
+
27
+ <div id="<%= wrapper.exception.object_id %>" style="display: none;">
28
+ <%= render "rescues/source", source_extracts: wrapper.source_extracts, show_source_idx: wrapper.source_to_show_id, error_index: index %>
29
+ <%= render "rescues/trace", traces: wrapper.traces, trace_to_show: wrapper.trace_to_show, error_index: index %>
30
+ </div>
31
+ <% end %>
32
+
15
33
  <%= render template: "rescues/_request_and_response" %>
16
34
  </div>
@@ -10,12 +10,12 @@
10
10
  <div id="container">
11
11
  <h2>
12
12
  <%= h @exception.message %>
13
- <% if %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}.match?(@exception.message) %>
14
- <br />To resolve this issue run: bin/rails active_storage:install
13
+ <% if @exception.message.match? %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}} %>
14
+ <br />To resolve this issue run: rails active_storage:install
15
15
  <% end %>
16
16
  </h2>
17
17
 
18
- <%= render template: "rescues/_source" %>
19
- <%= render template: "rescues/_trace" %>
18
+ <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
19
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
20
20
  <%= render template: "rescues/_request_and_response" %>
21
21
  </div>
@@ -4,8 +4,8 @@
4
4
  <% end %>
5
5
 
6
6
  <%= @exception.message %>
7
- <% if %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}.match?(@exception.message) %>
8
- To resolve this issue run: bin/rails active_storage:install
7
+ <% if @exception.message.match? %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}} %>
8
+ To resolve this issue run: rails active_storage:install
9
9
  <% end %>
10
10
 
11
11
  <%= render template: "rescues/_source" %>
@@ -0,0 +1,19 @@
1
+ <header>
2
+ <h1>No template for interactive request</h1>
3
+ </header>
4
+
5
+ <div id="container">
6
+ <h2><%= h @exception.message %></h2>
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>
19
+ </div>
@@ -0,0 +1,3 @@
1
+ Missing exact template
2
+
3
+ <%= @exception.message %>
@@ -5,7 +5,7 @@
5
5
  <div id="container">
6
6
  <h2><%= h @exception.message %></h2>
7
7
 
8
- <%= render template: "rescues/_source" %>
9
- <%= render template: "rescues/_trace" %>
8
+ <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
9
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
10
10
  <%= render template: "rescues/_request_and_response" %>
11
11
  </div>
@@ -14,7 +14,7 @@
14
14
  </p>
15
15
  <% end %>
16
16
 
17
- <%= render template: "rescues/_trace" %>
17
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
18
18
 
19
19
  <% if @routes_inspector %>
20
20
  <h2>
@@ -11,10 +11,10 @@
11
11
  </p>
12
12
  <pre><code><%= h @exception.message %></code></pre>
13
13
 
14
- <%= render template: "rescues/_source" %>
14
+ <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
15
15
 
16
16
  <p><%= @exception.sub_template_message %></p>
17
17
 
18
- <%= render template: "rescues/_trace" %>
18
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
19
19
  <%= render template: "rescues/_request_and_response" %>
20
20
  </div>
@@ -197,4 +197,7 @@
197
197
 
198
198
  setupMatchPaths();
199
199
  setupRouteToggleHelperLinks();
200
+
201
+ // Focus the search input after page has loaded
202
+ document.getElementById('search').focus();
200
203
  </script>
@@ -21,6 +21,7 @@ module ActionDispatch
21
21
  config.action_dispatch.encrypted_signed_cookie_salt = "signed encrypted cookie"
22
22
  config.action_dispatch.authenticated_encrypted_cookie_salt = "authenticated encrypted cookie"
23
23
  config.action_dispatch.use_authenticated_cookie_encryption = false
24
+ config.action_dispatch.use_cookies_with_metadata = false
24
25
  config.action_dispatch.perform_deep_munge = true
25
26
 
26
27
  config.action_dispatch.default_headers = {
@@ -93,6 +93,14 @@ module ActionDispatch
93
93
  @delegate[key.to_s]
94
94
  end
95
95
 
96
+ # Returns the nested value specified by the sequence of keys, returning
97
+ # +nil+ if any intermediate step is +nil+.
98
+ def dig(*keys)
99
+ load_for_read!
100
+ keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
101
+ @delegate.dig(*keys)
102
+ end
103
+
96
104
  # Returns true if the session has the given key or false.
97
105
  def has_key?(key)
98
106
  load_for_read!
@@ -243,8 +243,9 @@ module ActionDispatch
243
243
  #
244
244
  # rails routes
245
245
  #
246
- # Target specific controllers by prefixing the command with <tt>-c</tt> option.
247
- #
246
+ # Target a specific controller with <tt>-c</tt>, or grep routes
247
+ # using <tt>-g</tt>. Useful in conjunction with <tt>--expanded</tt>
248
+ # which displays routes vertically.
248
249
  module Routing
249
250
  extend ActiveSupport::Autoload
250
251
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "delegate"
4
- require "active_support/core_ext/string/strip"
4
+ require "io/console/size"
5
5
 
6
6
  module ActionDispatch
7
7
  module Routing
@@ -61,11 +61,11 @@ module ActionDispatch
61
61
  @routes = routes
62
62
  end
63
63
 
64
- def format(formatter, filter = nil)
64
+ def format(formatter, filter = {})
65
65
  routes_to_display = filter_routes(normalize_filter(filter))
66
66
  routes = collect_routes(routes_to_display)
67
67
  if routes.none?
68
- formatter.no_routes(collect_routes(@routes))
68
+ formatter.no_routes(collect_routes(@routes), filter)
69
69
  return formatter.result
70
70
  end
71
71
 
@@ -81,12 +81,12 @@ module ActionDispatch
81
81
  end
82
82
 
83
83
  private
84
-
85
84
  def normalize_filter(filter)
86
- if filter.is_a?(Hash) && filter[:controller]
85
+ if filter[:controller]
87
86
  { controller: /#{filter[:controller].underscore.sub(/_?controller\z/, "")}/ }
88
- elsif filter
89
- { controller: /#{filter}/, action: /#{filter}/, verb: /#{filter}/, name: /#{filter}/, path: /#{filter}/ }
87
+ elsif filter[:grep]
88
+ { controller: /#{filter[:grep]}/, action: /#{filter[:grep]}/,
89
+ verb: /#{filter[:grep]}/, name: /#{filter[:grep]}/, path: /#{filter[:grep]}/ }
90
90
  end
91
91
  end
92
92
 
@@ -126,62 +126,111 @@ module ActionDispatch
126
126
  end
127
127
  end
128
128
 
129
- class ConsoleFormatter
130
- def initialize
131
- @buffer = []
132
- end
129
+ module ConsoleFormatter
130
+ class Base
131
+ def initialize
132
+ @buffer = []
133
+ end
133
134
 
134
- def result
135
- @buffer.join("\n")
136
- end
135
+ def result
136
+ @buffer.join("\n")
137
+ end
137
138
 
138
- def section_title(title)
139
- @buffer << "\n#{title}:"
140
- end
139
+ def section_title(title)
140
+ end
141
141
 
142
- def section(routes)
143
- @buffer << draw_section(routes)
144
- end
142
+ def section(routes)
143
+ end
145
144
 
146
- def header(routes)
147
- @buffer << draw_header(routes)
148
- end
145
+ def header(routes)
146
+ end
149
147
 
150
- def no_routes(routes)
151
- @buffer <<
152
- if routes.none?
153
- <<-MESSAGE.strip_heredoc
154
- You don't have any routes defined!
148
+ def no_routes(routes, filter)
149
+ @buffer <<
150
+ if routes.none?
151
+ <<~MESSAGE
152
+ You don't have any routes defined!
153
+
154
+ Please add some routes in config/routes.rb.
155
+ MESSAGE
156
+ elsif filter.key?(:controller)
157
+ "No routes were found for this controller."
158
+ elsif filter.key?(:grep)
159
+ "No routes were found for this grep pattern."
160
+ end
155
161
 
156
- Please add some routes in config/routes.rb.
157
- MESSAGE
158
- else
159
- "No routes were found for this controller"
162
+ @buffer << "For more information about routes, see the Rails guide: https://guides.rubyonrails.org/routing.html."
160
163
  end
161
- @buffer << "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
162
164
  end
163
165
 
164
- private
165
- def draw_section(routes)
166
- header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length)
167
- name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
166
+ class Sheet < Base
167
+ def section_title(title)
168
+ @buffer << "\n#{title}:"
169
+ end
168
170
 
169
- routes.map do |r|
170
- "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
171
- end
171
+ def section(routes)
172
+ @buffer << draw_section(routes)
173
+ end
174
+
175
+ def header(routes)
176
+ @buffer << draw_header(routes)
172
177
  end
173
178
 
174
- def draw_header(routes)
175
- name_width, verb_width, path_width = widths(routes)
179
+ private
180
+
181
+ def draw_section(routes)
182
+ header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length)
183
+ name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
184
+
185
+ routes.map do |r|
186
+ "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
187
+ end
188
+ end
189
+
190
+ def draw_header(routes)
191
+ name_width, verb_width, path_width = widths(routes)
192
+
193
+ "#{"Prefix".rjust(name_width)} #{"Verb".ljust(verb_width)} #{"URI Pattern".ljust(path_width)} Controller#Action"
194
+ end
195
+
196
+ def widths(routes)
197
+ [routes.map { |r| r[:name].length }.max || 0,
198
+ routes.map { |r| r[:verb].length }.max || 0,
199
+ routes.map { |r| r[:path].length }.max || 0]
200
+ end
201
+ end
176
202
 
177
- "#{"Prefix".rjust(name_width)} #{"Verb".ljust(verb_width)} #{"URI Pattern".ljust(path_width)} Controller#Action"
203
+ class Expanded < Base
204
+ def section_title(title)
205
+ @buffer << "\n#{"[ #{title} ]"}"
178
206
  end
179
207
 
180
- def widths(routes)
181
- [routes.map { |r| r[:name].length }.max || 0,
182
- routes.map { |r| r[:verb].length }.max || 0,
183
- routes.map { |r| r[:path].length }.max || 0]
208
+ def section(routes)
209
+ @buffer << draw_expanded_section(routes)
184
210
  end
211
+
212
+ private
213
+
214
+ def draw_expanded_section(routes)
215
+ routes.map.each_with_index do |r, i|
216
+ <<~MESSAGE.chomp
217
+ #{route_header(index: i + 1)}
218
+ Prefix | #{r[:name]}
219
+ Verb | #{r[:verb]}
220
+ URI | #{r[:path]}
221
+ Controller#Action | #{r[:reqs]}
222
+ MESSAGE
223
+ end
224
+ end
225
+
226
+ def route_header(index:)
227
+ console_width = IO.console_size.second
228
+ header_prefix = "--[ Route #{index} ]"
229
+ dash_remainder = [console_width - header_prefix.size, 0].max
230
+
231
+ "#{header_prefix}#{'-' * dash_remainder}"
232
+ end
233
+ end
185
234
  end
186
235
 
187
236
  class HtmlTableFormatter
@@ -203,16 +252,16 @@ module ActionDispatch
203
252
  end
204
253
 
205
254
  def no_routes(*)
206
- @buffer << <<-MESSAGE.strip_heredoc
255
+ @buffer << <<~MESSAGE
207
256
  <p>You don't have any routes defined!</p>
208
257
  <ul>
209
258
  <li>Please add some routes in <tt>config/routes.rb</tt>.</li>
210
259
  <li>
211
260
  For more information about routes, please see the Rails guide
212
- <a href="http://guides.rubyonrails.org/routing.html">Rails Routing from the Outside In</a>.
261
+ <a href="https://guides.rubyonrails.org/routing.html">Rails Routing from the Outside In</a>.
213
262
  </li>
214
263
  </ul>
215
- MESSAGE
264
+ MESSAGE
216
265
  end
217
266
 
218
267
  def result