提交 a6cec6e8 编写于 作者: C Carlos Antonio da Silva

Merge pull request #8658 from senny/8649_unify_routes_output_for_console_and_web

Unify routes output for console and web, ensure engine routes are shown in html routes table.

Closes #8649.
...@@ -560,7 +560,9 @@ ...@@ -560,7 +560,9 @@
*Carlos Galdino + Rafael Mendonça França* *Carlos Galdino + Rafael Mendonça França*
* Show routes in exception page while debugging a `RoutingError` in development. *Richard Schneeman and Mattt Thompson* * Show routes in exception page while debugging a `RoutingError` in development.
*Richard Schneeman + Mattt Thompson + Yves Senn*
* Add `ActionController::Flash.add_flash_types` method to allow people to register their own flash types. e.g.: * Add `ActionController::Flash.add_flash_types` method to allow people to register their own flash types. e.g.:
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
require 'action_dispatch/middleware/exception_wrapper' require 'action_dispatch/middleware/exception_wrapper'
require 'action_dispatch/routing/inspector' require 'action_dispatch/routing/inspector'
module ActionDispatch module ActionDispatch
# This middleware is responsible for logging exceptions and # This middleware is responsible for logging exceptions and
# showing a debugging page in case the request is local. # showing a debugging page in case the request is local.
...@@ -42,12 +41,11 @@ def render_exception(env, exception) ...@@ -42,12 +41,11 @@ def render_exception(env, exception)
:application_trace => wrapper.application_trace, :application_trace => wrapper.application_trace,
:framework_trace => wrapper.framework_trace, :framework_trace => wrapper.framework_trace,
:full_trace => wrapper.full_trace, :full_trace => wrapper.full_trace,
:routes => formatted_routes(exception), :routes_inspector => routes_inspector(exception),
:source_extract => wrapper.source_extract, :source_extract => wrapper.source_extract,
:line_number => wrapper.line_number, :line_number => wrapper.line_number,
:file => wrapper.file :file => wrapper.file
) )
file = "rescues/#{wrapper.rescue_template}" file = "rescues/#{wrapper.rescue_template}"
body = template.render(:template => file, :layout => 'rescues/layout') body = template.render(:template => file, :layout => 'rescues/layout')
render(wrapper.status_code, body) render(wrapper.status_code, body)
...@@ -85,11 +83,28 @@ def stderr_logger ...@@ -85,11 +83,28 @@ def stderr_logger
@stderr_logger ||= ActiveSupport::Logger.new($stderr) @stderr_logger ||= ActiveSupport::Logger.new($stderr)
end end
def formatted_routes(exception) def routes_inspector(exception)
return false unless @routes_app.respond_to?(:routes) return false unless @routes_app.respond_to?(:routes)
if exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error) if exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error)
inspector = ActionDispatch::Routing::RoutesInspector.new ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
inspector.collect_routes(@routes_app.routes.routes) end
end
class TableRoutesFormatter
def initialize(view)
@view = view
@buffer = []
end
def section(type, title, routes)
@buffer << %(<tr><th colspan="4">#{title}</th></tr>)
@buffer << @view.render(partial: "routes/route", collection: routes)
end
def result
@view.raw @view.render(layout: "routes/route_wrapper") {
@view.raw @buffer.join("\n")
}
end end
end end
end end
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
<% end %> <% end %>
<%= render template: "rescues/_trace" %> <%= render template: "rescues/_trace" %>
<% if @routes_inspector %>
<h2> <h2>
Routes Routes
</h2> </h2>
...@@ -23,6 +24,5 @@ ...@@ -23,6 +24,5 @@
Routes match in priority from top to bottom Routes match in priority from top to bottom
</p> </p>
<%= render layout: "routes/route_wrapper" do %> <%= @routes_inspector.format(ActionDispatch::DebugExceptions::TableRoutesFormatter.new(self)) %>
<%= render partial: "routes/route", collection: @routes %>
<% end %> <% end %>
...@@ -61,21 +61,33 @@ def engine? ...@@ -61,21 +61,33 @@ def engine?
## ##
# This class is just used for displaying route information when someone # This class is just used for displaying route information when someone
# executes `rake routes`. People should not use this class. # executes `rake routes` or looks at the RoutingError page.
# People should not use this class.
class RoutesInspector # :nodoc: class RoutesInspector # :nodoc:
def initialize def initialize(routes)
@engines = Hash.new @engines = {}
@routes = routes
end end
def format(all_routes, filter = nil) def format(formatter, filter = nil)
if filter routes_to_display = filter_routes(filter)
all_routes = all_routes.select{ |route| route.defaults[:controller] == filter }
routes = collect_routes(routes_to_display)
formatter.section :application, 'Application routes', routes
@engines.each do |name, routes|
formatter.section :engine, "Routes for #{name}", routes
end end
routes = collect_routes(all_routes) formatter.result
end
formatted_routes(routes) + def filter_routes(filter)
formatted_routes_for_engines if filter
@routes.select { |route| route.defaults[:controller] == filter }
else
@routes
end
end end
def collect_routes(routes) def collect_routes(routes)
...@@ -100,22 +112,32 @@ def collect_engine_routes(route) ...@@ -100,22 +112,32 @@ def collect_engine_routes(route)
@engines[name] = collect_routes(routes.routes) @engines[name] = collect_routes(routes.routes)
end end
end end
end
def formatted_routes_for_engines class ConsoleFormatter
@engines.map do |name, routes| def initialize
["\nRoutes for #{name}:"] + formatted_routes(routes) @buffer = []
end.flatten
end end
def formatted_routes(routes) def result
name_width = routes.map{ |r| r[:name].length }.max @buffer.join("\n")
verb_width = routes.map{ |r| r[:verb].length }.max end
path_width = routes.map{ |r| r[:path].length }.max
routes.map do |r| def section(type, title, routes)
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}" @buffer << "\n#{title}:" unless type == :application
end @buffer << draw_section(routes)
end end
private
def draw_section(routes)
name_width = routes.map { |r| r[:name].length }.max
verb_width = routes.map { |r| r[:verb].length }.max
path_width = routes.map { |r| r[:path].length }.max
routes.map do |r|
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
end
end
end end
end end
end end
...@@ -45,8 +45,17 @@ def call(env) ...@@ -45,8 +45,17 @@ def call(env)
end end
end end
ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false)) def setup
DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true)) app = ActiveSupport::OrderedOptions.new
app.config = ActiveSupport::OrderedOptions.new
app.config.assets = ActiveSupport::OrderedOptions.new
app.config.assets.prefix = '/sprockets'
Rails.stubs(:application).returns(app)
end
RoutesApp = Struct.new(:routes).new(SharedTestRoutes)
ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false), RoutesApp)
DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp)
test 'skip diagnosis if not showing detailed exceptions' do test 'skip diagnosis if not showing detailed exceptions' do
@app = ProductionApp @app = ProductionApp
...@@ -78,6 +87,15 @@ def call(env) ...@@ -78,6 +87,15 @@ def call(env)
assert boomer.closed, "Expected to close the response body" assert boomer.closed, "Expected to close the response body"
end end
test 'displays routes in a table when a RoutingError occurs' do
@app = DevelopmentApp
get "/pass", {}, {'action_dispatch.show_exceptions' => true}
routing_table = body[/route_table.*<.table>/m]
assert_match '/:controller(/:action)(.:format)', routing_table
assert_match ':controller#:action', routing_table
assert_no_match '&lt;|&gt;', routing_table, "there should not be escaped html in the output"
end
test "rescue with diagnostics message" do test "rescue with diagnostics message" do
@app = DevelopmentApp @app = DevelopmentApp
......
...@@ -8,7 +8,6 @@ module Routing ...@@ -8,7 +8,6 @@ module Routing
class RoutesInspectorTest < ActiveSupport::TestCase class RoutesInspectorTest < ActiveSupport::TestCase
def setup def setup
@set = ActionDispatch::Routing::RouteSet.new @set = ActionDispatch::Routing::RouteSet.new
@inspector = ActionDispatch::Routing::RoutesInspector.new
app = ActiveSupport::OrderedOptions.new app = ActiveSupport::OrderedOptions.new
app.config = ActiveSupport::OrderedOptions.new app.config = ActiveSupport::OrderedOptions.new
app.config.assets = ActiveSupport::OrderedOptions.new app.config.assets = ActiveSupport::OrderedOptions.new
...@@ -17,9 +16,10 @@ def setup ...@@ -17,9 +16,10 @@ def setup
Rails.stubs(:env).returns("development") Rails.stubs(:env).returns("development")
end end
def draw(&block) def draw(options = {}, &block)
@set.draw(&block) @set.draw(&block)
@inspector.format(@set.routes) inspector = ActionDispatch::Routing::RoutesInspector.new(@set.routes)
inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, options[:filter]).split("\n")
end end
def test_displaying_routes_for_engines def test_displaying_routes_for_engines
...@@ -40,7 +40,8 @@ def self.inspect ...@@ -40,7 +40,8 @@ def self.inspect
expected = [ expected = [
"custom_assets GET /custom/assets(.:format) custom_assets#show", "custom_assets GET /custom/assets(.:format) custom_assets#show",
" blog /blog Blog::Engine", " blog /blog Blog::Engine",
"\nRoutes for Blog::Engine:", "",
"Routes for Blog::Engine:",
"cart GET /cart(.:format) cart#show" "cart GET /cart(.:format) cart#show"
] ]
assert_equal expected, output assert_equal expected, output
...@@ -165,6 +166,22 @@ def test_redirect ...@@ -165,6 +166,22 @@ def test_redirect
assert_equal " bar GET /bar(.:format) redirect(307, path: /foo/bar)", output[1] assert_equal " bar GET /bar(.:format) redirect(307, path: /foo/bar)", output[1]
assert_equal "foobar GET /foobar(.:format) redirect(301)", output[2] assert_equal "foobar GET /foobar(.:format) redirect(301)", output[2]
end end
def test_routes_can_be_filtered
output = draw(filter: 'posts') do
resources :articles
resources :posts
end
assert_equal [" posts GET /posts(.:format) posts#index",
" POST /posts(.:format) posts#create",
" new_post GET /posts/new(.:format) posts#new",
"edit_post GET /posts/:id/edit(.:format) posts#edit",
" post GET /posts/:id(.:format) posts#show",
" PATCH /posts/:id(.:format) posts#update",
" PUT /posts/:id(.:format) posts#update",
" DELETE /posts/:id(.:format) posts#destroy"], output
end
end end
end end
end end
...@@ -2,6 +2,6 @@ desc 'Print out all defined routes in match order, with names. Target specific c ...@@ -2,6 +2,6 @@ desc 'Print out all defined routes in match order, with names. Target specific c
task routes: :environment do task routes: :environment do
all_routes = Rails.application.routes.routes all_routes = Rails.application.routes.routes
require 'action_dispatch/routing/inspector' require 'action_dispatch/routing/inspector'
inspector = ActionDispatch::Routing::RoutesInspector.new inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
puts inspector.format(all_routes, ENV['CONTROLLER']).join "\n" puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, ENV['CONTROLLER'])
end end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册