2012-12-19 15:54:47 -05:00
|
|
|
require 'action_dispatch/journey/router/utils'
|
|
|
|
require 'action_dispatch/journey/router/strexp'
|
|
|
|
require 'action_dispatch/journey/routes'
|
|
|
|
require 'action_dispatch/journey/formatter'
|
|
|
|
|
|
|
|
before = $-w
|
|
|
|
$-w = false
|
|
|
|
require 'action_dispatch/journey/parser'
|
|
|
|
$-w = before
|
|
|
|
|
|
|
|
require 'action_dispatch/journey/route'
|
|
|
|
require 'action_dispatch/journey/path/pattern'
|
|
|
|
|
|
|
|
module ActionDispatch
|
2012-12-19 19:24:25 -05:00
|
|
|
module Journey # :nodoc:
|
|
|
|
class Router # :nodoc:
|
|
|
|
class RoutingError < ::StandardError # :nodoc:
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
2012-12-19 19:24:25 -05:00
|
|
|
# :nodoc:
|
2012-12-19 15:54:47 -05:00
|
|
|
VERSION = '2.0.0'
|
|
|
|
|
|
|
|
attr_reader :request_class, :formatter
|
|
|
|
attr_accessor :routes
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def initialize(routes, options)
|
2012-12-19 15:54:47 -05:00
|
|
|
@options = options
|
2014-05-23 13:09:24 -04:00
|
|
|
@request_class = options[:request_class]
|
2012-12-19 15:54:47 -05:00
|
|
|
@routes = routes
|
|
|
|
end
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def call(env)
|
2014-05-22 18:46:38 -04:00
|
|
|
req = request_class.new(env)
|
|
|
|
|
2014-05-23 12:56:33 -04:00
|
|
|
req.path_info = Utils.normalize_path(req.path_info)
|
|
|
|
|
2014-05-23 13:16:03 -04:00
|
|
|
find_routes(req).each do |match, parameters, route|
|
2014-05-23 12:56:33 -04:00
|
|
|
set_params = req.path_parameters
|
|
|
|
path_info = req.path_info
|
|
|
|
script_name = req.script_name
|
2012-12-19 15:54:47 -05:00
|
|
|
|
|
|
|
unless route.path.anchored
|
2014-05-23 12:56:33 -04:00
|
|
|
req.script_name = (script_name.to_s + match.to_s).chomp('/')
|
|
|
|
req.path_info = match.post_match
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
2014-05-22 18:57:24 -04:00
|
|
|
req.path_parameters = set_params.merge parameters
|
2012-12-19 15:54:47 -05:00
|
|
|
|
|
|
|
status, headers, body = route.app.call(env)
|
|
|
|
|
|
|
|
if 'pass' == headers['X-Cascade']
|
2014-05-23 12:56:33 -04:00
|
|
|
req.script_name = script_name
|
|
|
|
req.path_info = path_info
|
2014-05-22 18:57:24 -04:00
|
|
|
req.path_parameters = set_params
|
2012-12-19 15:54:47 -05:00
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
return [status, headers, body]
|
|
|
|
end
|
|
|
|
|
|
|
|
return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
|
|
|
|
end
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def recognize(req)
|
2014-05-22 18:46:38 -04:00
|
|
|
rails_req = request_class.new(req.env)
|
|
|
|
|
2014-05-23 13:16:03 -04:00
|
|
|
find_routes(rails_req).each do |match, parameters, route|
|
2012-12-19 15:54:47 -05:00
|
|
|
unless route.path.anchored
|
|
|
|
req.env['SCRIPT_NAME'] = match.to_s
|
|
|
|
req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
|
|
|
|
end
|
|
|
|
|
2014-05-21 19:12:49 -04:00
|
|
|
yield(route, parameters)
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def visualizer
|
|
|
|
tt = GTG::Builder.new(ast).transition_table
|
|
|
|
groups = partitioned_routes.first.map(&:ast).group_by { |a| a.to_s }
|
|
|
|
asts = groups.values.map { |v| v.first }
|
2012-12-20 15:42:39 -05:00
|
|
|
tt.visualizer(asts)
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def partitioned_routes
|
|
|
|
routes.partitioned_routes
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def ast
|
|
|
|
routes.ast
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def simulator
|
|
|
|
routes.simulator
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def custom_routes
|
|
|
|
partitioned_routes.last
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def filter_routes(path)
|
|
|
|
return [] unless ast
|
2014-04-01 18:55:48 -04:00
|
|
|
simulator.memos(path) { [] }
|
2012-12-20 15:42:39 -05:00
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2014-05-23 13:16:03 -04:00
|
|
|
def find_routes req
|
2012-12-20 15:42:39 -05:00
|
|
|
routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
|
|
|
|
r.path.match(req.path_info)
|
|
|
|
}
|
|
|
|
routes.concat get_routes_as_head(routes)
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2013-01-15 08:38:10 -05:00
|
|
|
routes.sort_by!(&:precedence).select! { |r| r.matches?(req) }
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
routes.map! { |r|
|
|
|
|
match_data = r.path.match(req.path_info)
|
2014-05-21 19:33:06 -04:00
|
|
|
path_parameters = r.defaults.dup
|
2014-05-21 19:21:33 -04:00
|
|
|
match_data.names.zip(match_data.captures) { |name,val|
|
|
|
|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
|
|
|
|
}
|
2014-05-21 19:33:06 -04:00
|
|
|
[match_data, path_parameters, r]
|
2012-12-20 15:42:39 -05:00
|
|
|
}
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def get_routes_as_head(routes)
|
|
|
|
precedence = (routes.map(&:precedence).max || 0) + 1
|
|
|
|
routes = routes.select { |r|
|
|
|
|
r.verb === "GET" && !(r.verb === "HEAD")
|
|
|
|
}.map! { |r|
|
|
|
|
Route.new(r.name,
|
|
|
|
r.app,
|
|
|
|
r.path,
|
|
|
|
r.conditions.merge(request_method: "HEAD"),
|
|
|
|
r.defaults).tap do |route|
|
|
|
|
route.precedence = r.precedence + precedence
|
|
|
|
end
|
|
|
|
}
|
|
|
|
routes.flatten!
|
|
|
|
routes
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|