1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

move route_inspector to actionpack

this is so we can show route output in the development when we get a routing error. Railties can use features of ActionDispatch, but ActionDispatch should not depend on Railties.
This commit is contained in:
@schneems and @mattt 2012-07-01 20:00:10 -07:00 committed by schneems
parent fa714ec7df
commit ef91cddb48
6 changed files with 304 additions and 10 deletions

View file

@ -1,6 +1,6 @@
require 'action_dispatch/http/request'
require 'action_dispatch/middleware/exception_wrapper'
require 'rails/application/route_inspector'
require 'action_dispatch/routing/inspector'
module ActionDispatch
@ -9,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)
@ -84,9 +85,10 @@ module ActionDispatch
private
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 = Rails::Application::RouteInspector.new
inspector.format(Rails.application.routes.routes).join("\n")
inspector = ActionDispatch::Routing::RouteInspector.new
inspector.format(@routes_app.routes.routes).join("\n")
end
end
end

View file

@ -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 RouteInspector # :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

View file

@ -0,0 +1,170 @@
require 'minitest/autorun'
require 'action_controller'
require 'rails/engine'
require 'action_dispatch/routing/inspector'
module ActionDispatch
module Routing
class RouteInspectTest < ActiveSupport::TestCase
def setup
@set = ActionDispatch::Routing::RouteSet.new
@inspector = ActionDispatch::Routing::RouteInspector.new
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)
Rails.stubs(:env).returns("development")
end
def draw(&block)
@set.draw(&block)
@inspector.format(@set.routes)
end
def test_displaying_routes_for_engines
engine = Class.new(Rails::Engine) do
def self.to_s
"Blog::Engine"
end
end
engine.routes.draw do
get '/cart', :to => 'cart#show'
end
output = draw do
get '/custom/assets', :to => 'custom_assets#show'
mount engine => "/blog", :as => "blog"
end
expected = [
"custom_assets GET /custom/assets(.:format) custom_assets#show",
" blog /blog Blog::Engine",
"\nRoutes for Blog::Engine:",
"cart GET /cart(.:format) cart#show"
]
assert_equal expected, output
end
def test_cart_inspect
output = draw do
get '/cart', :to => 'cart#show'
end
assert_equal ["cart GET /cart(.:format) cart#show"], output
end
def test_inspect_shows_custom_assets
output = draw do
get '/custom/assets', :to => 'custom_assets#show'
end
assert_equal ["custom_assets GET /custom/assets(.:format) custom_assets#show"], output
end
def test_inspect_routes_shows_resources_route
output = draw do
resources :articles
end
expected = [
" articles GET /articles(.:format) articles#index",
" POST /articles(.:format) articles#create",
" new_article GET /articles/new(.:format) articles#new",
"edit_article GET /articles/:id/edit(.:format) articles#edit",
" article GET /articles/:id(.:format) articles#show",
" PATCH /articles/:id(.:format) articles#update",
" PUT /articles/:id(.:format) articles#update",
" DELETE /articles/:id(.:format) articles#destroy" ]
assert_equal expected, output
end
def test_inspect_routes_shows_root_route
output = draw do
root :to => 'pages#main'
end
assert_equal ["root GET / pages#main"], output
end
def test_inspect_routes_shows_dynamic_action_route
output = draw do
get 'api/:action' => 'api'
end
assert_equal [" GET /api/:action(.:format) api#:action"], output
end
def test_inspect_routes_shows_controller_and_action_only_route
output = draw do
get ':controller/:action'
end
assert_equal [" GET /:controller/:action(.:format) :controller#:action"], output
end
def test_inspect_routes_shows_controller_and_action_route_with_constraints
output = draw do
get ':controller(/:action(/:id))', :id => /\d+/
end
assert_equal [" GET /:controller(/:action(/:id))(.:format) :controller#:action {:id=>/\\d+/}"], output
end
def test_rake_routes_shows_route_with_defaults
output = draw do
get 'photos/:id' => 'photos#show', :defaults => {:format => 'jpg'}
end
assert_equal [%Q[ GET /photos/:id(.:format) photos#show {:format=>"jpg"}]], output
end
def test_rake_routes_shows_route_with_constraints
output = draw do
get 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
end
assert_equal [" GET /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}"], output
end
class RackApp
def self.call(env)
end
end
def test_rake_routes_shows_route_with_rack_app
output = draw do
get 'foo/:id' => RackApp, :id => /[A-Z]\d{5}/
end
assert_equal [" GET /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}"], output
end
def test_rake_routes_shows_route_with_rack_app_nested_with_dynamic_constraints
constraint = Class.new do
def to_s
"( my custom constraint )"
end
end
output = draw do
scope :constraint => constraint.new do
mount RackApp => '/foo'
end
end
assert_equal [" /foo #{RackApp.name} {:constraint=>( my custom constraint )}"], output
end
def test_rake_routes_dont_show_app_mounted_in_assets_prefix
output = draw do
get '/sprockets' => RackApp
end
assert_no_match(/RackApp/, output.first)
assert_no_match(/\/sprockets/, output.first)
end
def test_redirect
output = draw do
get "/foo" => redirect("/foo/bar"), :constraints => { :subdomain => "admin" }
get "/bar" => redirect(path: "/foo/bar", status: 307)
get "/foobar" => redirect{ "/foo/bar" }
end
assert_equal " foo GET /foo(.:format) redirect(301, /foo/bar) {:subdomain=>\"admin\"}", output[0]
assert_equal " bar GET /bar(.:format) redirect(307, path: /foo/bar)", output[1]
assert_equal "foobar GET /foobar(.:format) redirect(301)", output[2]
end
end
end
end

View file

@ -267,6 +267,7 @@ module Rails
def default_middleware_stack #:nodoc:
ActionDispatch::MiddlewareStack.new.tap do |middleware|
app = self
if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
require "action_dispatch/http/rack_cache"
middleware.use ::Rack::Cache, rack_cache
@ -290,11 +291,10 @@ module Rails
middleware.use ::ActionDispatch::RequestId
middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods
middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
middleware.use ::ActionDispatch::DebugExceptions
middleware.use ::ActionDispatch::DebugExceptions, app
middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
unless config.cache_classes
app = self
middleware.use ::ActionDispatch::Reloader, lambda { app.reload_dependencies? }
end

View file

@ -1,4 +1,4 @@
require 'rails/application/routes_inspector'
require 'action_dispatch/routing/inspector'
class Rails::InfoController < ActionController::Base
self.view_paths = File.join(File.dirname(__FILE__), 'templates')
@ -16,6 +16,7 @@ class Rails::InfoController < ActionController::Base
def routes
inspector = Rails::Application::RoutesInspector.new
inspector = ActionDispatch::Routing::RouteInspector.new
@info = inspector.format(_routes.routes).join("\n")
end

View file

@ -1,7 +1,7 @@
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
task :routes => :environment do
all_routes = Rails.application.routes.routes
require 'rails/application/routes_inspector'
inspector = Rails::Application::RoutesInspector.new
require 'action_dispatch/routing/inspector'
inspector = ActionDispatch::Routing::RouteInspector.new
puts inspector.format(all_routes, ENV['CONTROLLER']).join "\n"
end