1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
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
module Journey # :nodoc:
class Router # :nodoc:
class RoutingError < ::StandardError # :nodoc:
end
# :nodoc:
VERSION = '2.0.0'
attr_accessor :routes
def initialize(routes)
@routes = routes
end
def serve(req)
find_routes(req).each do |match, parameters, route|
set_params = req.path_parameters
path_info = req.path_info
script_name = req.script_name
unless route.path.anchored
req.script_name = (script_name.to_s + match.to_s).chomp('/')
req.path_info = match.post_match
end
req.path_parameters = set_params.merge parameters
status, headers, body = route.app.call(req.env)
if 'pass' == headers['X-Cascade']
req.script_name = script_name
req.path_info = path_info
req.path_parameters = set_params
next
end
return [status, headers, body]
end
return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
end
def recognize(rails_req)
find_routes(rails_req).each do |match, parameters, route|
unless route.path.anchored
rails_req.script_name = match.to_s
rails_req.path_info = match.post_match.sub(/^([^\/])/, '/\1')
end
yield(route, parameters)
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 }
tt.visualizer(asts)
end
private
def partitioned_routes
routes.partitioned_routes
end
def ast
routes.ast
end
def simulator
routes.simulator
end
def custom_routes
partitioned_routes.last
end
def filter_routes(path)
return [] unless ast
simulator.memos(path) { [] }
end
def find_routes req
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)
routes.sort_by!(&:precedence).select! { |r| r.matches?(req) }
routes.map! { |r|
match_data = r.path.match(req.path_info)
path_parameters = r.defaults.dup
match_data.names.zip(match_data.captures) { |name,val|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
}
[match_data, path_parameters, r]
}
end
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
end
end
end
|