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 135 136 137 138 139 140 141 142 143 144 145
|
require 'journey/core-ext/hash'
require 'journey/router/utils'
require 'journey/router/strexp'
require 'journey/routes'
require 'journey/formatter'
before = $-w
$-w = false
require 'journey/parser'
$-w = before
require 'journey/route'
require 'journey/path/pattern'
module Journey
class Router
class RoutingError < ::StandardError
end
VERSION = '1.0.4'
class NullReq # :nodoc:
attr_reader :env
def initialize env
@env = env
end
def request_method
env['REQUEST_METHOD']
end
def path_info
env['PATH_INFO']
end
def ip
env['REMOTE_ADDR']
end
def [](k); env[k]; end
end
attr_reader :request_class, :formatter
attr_accessor :routes
def initialize routes, options
@options = options
@params_key = options[:parameters_key]
@request_class = options[:request_class] || NullReq
@routes = routes
end
def call env
env['PATH_INFO'] = Utils.normalize_path env['PATH_INFO']
find_routes(env).each do |match, parameters, route|
script_name, path_info, set_params = env.values_at('SCRIPT_NAME',
'PATH_INFO',
@params_key)
unless route.path.anchored
env['SCRIPT_NAME'] = (script_name.to_s + match.to_s).chomp('/')
env['PATH_INFO'] = Utils.normalize_path(match.post_match)
end
env[@params_key] = (set_params || {}).merge parameters
status, headers, body = route.app.call(env)
if 'pass' == headers['X-Cascade']
env['SCRIPT_NAME'] = script_name
env['PATH_INFO'] = path_info
env[@params_key] = set_params
next
end
return [status, headers, body]
end
return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
end
def recognize req
find_routes(req.env).each do |match, parameters, route|
unless route.path.anchored
req.env['SCRIPT_NAME'] = match.to_s
req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
end
yield(route, nil, 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
data = simulator.match(path)
data ? data.memos : []
end
def find_routes env
req = request_class.new env
routes = filter_routes(req.path_info) + custom_routes.find_all { |r|
r.path.match(req.path_info)
}
routes.sort_by(&:precedence).find_all { |r|
r.constraints.all? { |k,v| v === req.send(k) } &&
r.verb === req.request_method
}.reject { |r| req.ip && !(r.ip === req.ip) }.map { |r|
match_data = r.path.match(req.path_info)
match_names = match_data.names.map { |n| n.to_sym }
match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) }
info = Hash[match_names.zip(match_values).find_all { |_,y| y }]
[match_data, r.defaults.merge(info), r]
}
end
end
end
|