aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch')
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb18
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb10
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb121
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb4
4 files changed, 148 insertions, 5 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index b903f98761..0f0589a844 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -1,5 +1,7 @@
require 'action_dispatch/http/request'
require 'action_dispatch/middleware/exception_wrapper'
+require 'action_dispatch/routing/inspector'
+
module ActionDispatch
# This middleware is responsible for logging exceptions and
@@ -7,8 +9,9 @@ module ActionDispatch
class DebugExceptions
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
- def initialize(app)
- @app = app
+ def initialize(app, routes_app = nil)
+ @app = app
+ @routes_app = routes_app
end
def call(env)
@@ -39,7 +42,8 @@ module ActionDispatch
:exception => wrapper.exception,
:application_trace => wrapper.application_trace,
:framework_trace => wrapper.framework_trace,
- :full_trace => wrapper.full_trace
+ :full_trace => wrapper.full_trace,
+ :routes => formatted_routes(exception)
)
file = "rescues/#{wrapper.rescue_template}"
@@ -78,5 +82,13 @@ module ActionDispatch
def stderr_logger
@stderr_logger ||= ActiveSupport::Logger.new($stderr)
end
+
+ def formatted_routes(exception)
+ return false unless @routes_app.respond_to?(:routes)
+ if exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error)
+ inspector = ActionDispatch::Routing::RoutesInspector.new
+ inspector.format(@routes_app.routes.routes).join("\n")
+ end
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
index 177d383e94..8c594c1523 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
@@ -10,8 +10,14 @@
</ol>
</p>
<% end %>
+<%= render :template => "rescues/_trace" %>
+
+<h2>
+ Routes
+</h2>
+
<p>
- Try running <code>rake routes</code> for more information on available routes.
+ Routes match in priority from top to bottom
</p>
-<%= render :template => "rescues/_trace" %>
+<p><pre><%= @routes %></pre></p>
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
new file mode 100644
index 0000000000..bc7229b6a1
--- /dev/null
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -0,0 +1,121 @@
+require 'delegate'
+
+module ActionDispatch
+ module Routing
+ class RouteWrapper < SimpleDelegator
+ def endpoint
+ rack_app ? rack_app.inspect : "#{controller}##{action}"
+ end
+
+ def constraints
+ requirements.except(:controller, :action)
+ end
+
+ def rack_app(app = self.app)
+ @rack_app ||= begin
+ class_name = app.class.name.to_s
+ if class_name == "ActionDispatch::Routing::Mapper::Constraints"
+ rack_app(app.app)
+ elsif ActionDispatch::Routing::Redirect === app || class_name !~ /^ActionDispatch::Routing/
+ app
+ end
+ end
+ end
+
+ def verb
+ super.source.gsub(/[$^]/, '')
+ end
+
+ def path
+ super.spec.to_s
+ end
+
+ def name
+ super.to_s
+ end
+
+ def reqs
+ @reqs ||= begin
+ reqs = endpoint
+ reqs += " #{constraints.inspect}" unless constraints.empty?
+ reqs
+ end
+ end
+
+ def controller
+ requirements[:controller] || ':controller'
+ end
+
+ def action
+ requirements[:action] || ':action'
+ end
+
+ def internal?
+ path =~ %r{/rails/info.*|^#{Rails.application.config.assets.prefix}}
+ end
+
+ def engine?
+ rack_app && rack_app.respond_to?(:routes)
+ end
+ end
+
+ ##
+ # This class is just used for displaying route information when someone
+ # executes `rake routes`. People should not use this class.
+ class RoutesInspector # :nodoc:
+ def initialize
+ @engines = Hash.new
+ end
+
+ def format(all_routes, filter = nil)
+ if filter
+ all_routes = all_routes.select{ |route| route.defaults[:controller] == filter }
+ end
+
+ routes = collect_routes(all_routes)
+
+ formatted_routes(routes) +
+ formatted_routes_for_engines
+ end
+
+ def collect_routes(routes)
+ routes = routes.collect do |route|
+ RouteWrapper.new(route)
+ end.reject do |route|
+ route.internal?
+ end.collect do |route|
+ collect_engine_routes(route)
+
+ {:name => route.name, :verb => route.verb, :path => route.path, :reqs => route.reqs }
+ end
+ end
+
+ def collect_engine_routes(route)
+ name = route.endpoint
+ return unless route.engine?
+ return if @engines[name]
+
+ routes = route.rack_app.routes
+ if routes.is_a?(ActionDispatch::Routing::RouteSet)
+ @engines[name] = collect_routes(routes.routes)
+ end
+ end
+
+ def formatted_routes_for_engines
+ @engines.map do |name, routes|
+ ["\nRoutes for #{name}:"] + formatted_routes(routes)
+ end.flatten
+ end
+
+ def formatted_routes(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
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 53a4afecb3..a49b12621e 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -430,6 +430,10 @@ module ActionDispatch
if options
path = options.delete(:at)
else
+ unless Hash === app
+ raise ArgumentError, "must be called with mount point"
+ end
+
options = app
app, path = options.find { |k, v| k.respond_to?(:call) }
options.delete(app) if app