http_router 0.10.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/Rakefile +7 -5
- data/benchmarks/gen2.rb +1 -1
- data/benchmarks/rack_mount.rb +8 -14
- data/examples/rack_mapper.ru +12 -13
- data/examples/variable_with_regex.ru +1 -1
- data/http_router.gemspec +1 -1
- data/lib/http_router.rb +159 -62
- data/lib/http_router/generation_helper.rb +29 -0
- data/lib/http_router/generator.rb +150 -0
- data/lib/http_router/node.rb +27 -17
- data/lib/http_router/node/abstract_request_node.rb +31 -0
- data/lib/http_router/node/host.rb +9 -0
- data/lib/http_router/node/lookup.rb +8 -10
- data/lib/http_router/node/path.rb +23 -38
- data/lib/http_router/node/request_method.rb +16 -0
- data/lib/http_router/node/root.rb +104 -10
- data/lib/http_router/node/scheme.rb +9 -0
- data/lib/http_router/node/user_agent.rb +9 -0
- data/lib/http_router/regex_route_generation.rb +10 -0
- data/lib/http_router/request.rb +7 -17
- data/lib/http_router/response.rb +4 -0
- data/lib/http_router/route.rb +16 -277
- data/lib/http_router/route_helper.rb +126 -0
- data/lib/http_router/util.rb +1 -37
- data/lib/http_router/version.rb +1 -1
- data/test/common/generate.txt +1 -1
- data/test/generation.rb +15 -11
- data/test/generic.rb +2 -3
- data/test/helper.rb +15 -10
- data/test/rack/test_route.rb +0 -5
- data/test/test_misc.rb +50 -40
- data/test/test_mounting.rb +27 -26
- data/test/test_recognition.rb +1 -76
- metadata +104 -161
- data/.rspec +0 -1
- data/lib/http_router/node/arbitrary.rb +0 -30
- data/lib/http_router/node/request.rb +0 -52
- data/lib/http_router/rack.rb +0 -19
- data/lib/http_router/rack/builder.rb +0 -61
- data/lib/http_router/rack/url_map.rb +0 -16
- data/lib/http_router/regex_route.rb +0 -39
@@ -0,0 +1,29 @@
|
|
1
|
+
class HttpRouter
|
2
|
+
module GenerationHelper
|
3
|
+
def max_param_count
|
4
|
+
@generator.max_param_count
|
5
|
+
end
|
6
|
+
|
7
|
+
def url(*args)
|
8
|
+
@generator.url(*args)
|
9
|
+
rescue InvalidRouteException
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def url_ns(*args)
|
14
|
+
@generator.url_ns(*args)
|
15
|
+
rescue InvalidRouteException
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def path(*args)
|
20
|
+
@generator.path(*args)
|
21
|
+
rescue InvalidRouteException
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def param_names
|
26
|
+
@generator.param_names
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
class HttpRouter
|
2
|
+
class Generator
|
3
|
+
SCHEME_PORTS = {'http' => 80, 'https' => 443}
|
4
|
+
|
5
|
+
class PathGenerator
|
6
|
+
attr_reader :path
|
7
|
+
attr_accessor :param_names
|
8
|
+
def initialize(route, path, validation_regex = nil)
|
9
|
+
@route = route
|
10
|
+
@path = path.dup
|
11
|
+
@param_names = []
|
12
|
+
if path.is_a?(String)
|
13
|
+
path[0, 0] = '/' unless path[0] == ?/
|
14
|
+
regex_parts = path.split(/([:\*][a-zA-Z0-9_]+)/)
|
15
|
+
regex, code = '', ''
|
16
|
+
dynamic = false
|
17
|
+
regex_parts.each_with_index do |part, index|
|
18
|
+
case part[0]
|
19
|
+
when ?:, ?*
|
20
|
+
if index != 0 && regex_parts[index - 1][-1] == ?\\
|
21
|
+
regex << Regexp.quote(part) unless validation_regex
|
22
|
+
code << part
|
23
|
+
dynamic = true
|
24
|
+
else
|
25
|
+
regex << (@route.matches_with(part[1, part.size].to_sym) || '.*?').to_s unless validation_regex
|
26
|
+
code << "\#{args.shift || (options && options.delete(:#{part[1, part.size]})) || return}"
|
27
|
+
dynamic = true
|
28
|
+
end
|
29
|
+
else
|
30
|
+
regex << Regexp.quote(part) unless validation_regex
|
31
|
+
code << part
|
32
|
+
end
|
33
|
+
end
|
34
|
+
validation_regex ||= Regexp.new("^#{regex}$") if dynamic
|
35
|
+
if validation_regex
|
36
|
+
instance_eval <<-EOT, __FILE__, __LINE__ + 1
|
37
|
+
def generate(args, options)
|
38
|
+
generated_path = \"#{code}\"
|
39
|
+
#{validation_regex.inspect}.match(generated_path) ? URI.escape(generated_path) : nil
|
40
|
+
end
|
41
|
+
EOT
|
42
|
+
else
|
43
|
+
instance_eval <<-EOT, __FILE__, __LINE__ + 1
|
44
|
+
def generate(args, options)
|
45
|
+
URI.escape(\"#{code}\")
|
46
|
+
end
|
47
|
+
EOT
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(route, paths)
|
54
|
+
@route, @paths = route, paths
|
55
|
+
@router = @route.router
|
56
|
+
@route.generator = self
|
57
|
+
@path_generators = @paths.map do |p|
|
58
|
+
generator = PathGenerator.new(route, p.is_a?(String) ? p : route.path_for_generation, p.is_a?(Regexp) ? p : nil)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def param_names
|
63
|
+
@param_names ||= @path_generators.map{|path| path.param_names}.flatten.uniq
|
64
|
+
end
|
65
|
+
|
66
|
+
def max_param_count
|
67
|
+
@max_param_count ||= @path_generators.map{|p| p.param_names.size}.max
|
68
|
+
end
|
69
|
+
|
70
|
+
def each_path
|
71
|
+
@path_generators.each {|p| yield p }
|
72
|
+
@path_generators.sort! do |p1, p2|
|
73
|
+
p2.param_names.size <=> p1.param_names.size
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def url(*args)
|
78
|
+
"#{scheme_port.first}#{url_ns(*args)}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def url_ns(*args)
|
82
|
+
"://#{@route.host || @router.default_host}#{scheme_port.last}#{path(*args)}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def path(*args)
|
86
|
+
result, extra_params = path_with_params(*args)
|
87
|
+
append_querystring(result, extra_params)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def scheme_port
|
92
|
+
@scheme_port ||= begin
|
93
|
+
scheme = @route.scheme || @router.default_scheme
|
94
|
+
port = @router.default_port
|
95
|
+
port_part = SCHEME_PORTS.key?(scheme) && SCHEME_PORTS[scheme] == port ? '' : ":#{port}"
|
96
|
+
[scheme, port_part]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def path_with_params(*a)
|
101
|
+
path_args_processing(a) do |args, options|
|
102
|
+
path = args.empty? ? matching_path(options) : matching_path(args, options)
|
103
|
+
path &&= path.generate(args, options)
|
104
|
+
raise TooManyParametersException unless args.empty?
|
105
|
+
raise InvalidRouteException.new("Error generating #{@route.path_for_generation}") unless path
|
106
|
+
path ? [path, options] : nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def path_args_processing(args)
|
111
|
+
options = args.last.is_a?(Hash) ? args.pop : nil
|
112
|
+
options = options.nil? ? @route.default_values.dup : @route.default_values.merge(options) if @route.default_values
|
113
|
+
options.delete_if{ |k,v| v.nil? } if options
|
114
|
+
result, params = yield args, options
|
115
|
+
mount_point = @router.url_mount && (options ? @router.url_mount.url(options) : @router.url_mount.url)
|
116
|
+
mount_point ? [File.join(mount_point, result), params] : [result, params]
|
117
|
+
end
|
118
|
+
|
119
|
+
def matching_path(params, other_hash = nil)
|
120
|
+
return @path_generators.first if @path_generators.size == 1
|
121
|
+
case params
|
122
|
+
when Array, nil
|
123
|
+
@path_generators.find do |path|
|
124
|
+
significant_key_count = params ? params.size : 0
|
125
|
+
significant_key_count += (path.param_names & other_hash.keys).size if other_hash
|
126
|
+
significant_key_count >= path.param_names.size
|
127
|
+
end
|
128
|
+
when Hash
|
129
|
+
@path_generators.find { |path| (params && !params.empty? && (path.param_names & params.keys).size == path.param_names.size) || path.param_names.empty? }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def append_querystring_value(uri, key, value)
|
134
|
+
case value
|
135
|
+
when Array then value.each{ |v| append_querystring_value(uri, "#{key}[]", v) }
|
136
|
+
when Hash then value.each{ |k, v| append_querystring_value(uri, "#{key}[#{k}]", v) }
|
137
|
+
else uri << "&#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def append_querystring(uri, params)
|
142
|
+
if params && !params.empty?
|
143
|
+
uri_size = uri.size
|
144
|
+
params.each{ |k,v| append_querystring_value(uri, k, v) }
|
145
|
+
uri[uri_size] = ??
|
146
|
+
end
|
147
|
+
uri
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/http_router/node.rb
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Node
|
3
|
-
autoload :Root,
|
4
|
-
autoload :Glob,
|
5
|
-
autoload :GlobRegex,
|
6
|
-
autoload :Variable,
|
7
|
-
autoload :Regex,
|
8
|
-
autoload :SpanningRegex,
|
9
|
-
autoload :GlobRegex,
|
10
|
-
autoload :FreeRegex,
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
14
|
-
autoload :
|
3
|
+
autoload :Root, 'http_router/node/root'
|
4
|
+
autoload :Glob, 'http_router/node/glob'
|
5
|
+
autoload :GlobRegex, 'http_router/node/glob_regex'
|
6
|
+
autoload :Variable, 'http_router/node/variable'
|
7
|
+
autoload :Regex, 'http_router/node/regex'
|
8
|
+
autoload :SpanningRegex, 'http_router/node/spanning_regex'
|
9
|
+
autoload :GlobRegex, 'http_router/node/glob_regex'
|
10
|
+
autoload :FreeRegex, 'http_router/node/free_regex'
|
11
|
+
autoload :AbstractRequestNode, 'http_router/node/abstract_request_node'
|
12
|
+
autoload :Host, 'http_router/node/host'
|
13
|
+
autoload :UserAgent, 'http_router/node/user_agent'
|
14
|
+
autoload :RequestMethod, 'http_router/node/request_method'
|
15
|
+
autoload :Scheme, 'http_router/node/scheme'
|
16
|
+
autoload :Lookup, 'http_router/node/lookup'
|
17
|
+
autoload :Path, 'http_router/node/path'
|
15
18
|
|
16
19
|
attr_reader :router
|
17
20
|
|
@@ -31,13 +34,20 @@ class HttpRouter
|
|
31
34
|
add(GlobRegex.new(@router, self, matcher))
|
32
35
|
end
|
33
36
|
|
34
|
-
def
|
35
|
-
|
36
|
-
add(Request.new(@router, self, opts))
|
37
|
+
def add_host(hosts)
|
38
|
+
add(Host.new(@router, self, hosts))
|
37
39
|
end
|
38
40
|
|
39
|
-
def
|
40
|
-
add(
|
41
|
+
def add_user_agent(uas)
|
42
|
+
add(UserAgent.new(@router, self, uas))
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_request_method(rm)
|
46
|
+
add(RequestMethod.new(@router, self, rm))
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_scheme(scheme)
|
50
|
+
add(Scheme.new(@router, self, scheme))
|
41
51
|
end
|
42
52
|
|
43
53
|
def add_match(regexp, matching_indicies = [0], splitting_indicies = nil)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class HttpRouter
|
2
|
+
class Node
|
3
|
+
class AbstractRequestNode < Node
|
4
|
+
attr_reader :request_method, :tests
|
5
|
+
|
6
|
+
def initialize(route, parent, tests, request_method)
|
7
|
+
@request_method = request_method
|
8
|
+
@tests = case tests
|
9
|
+
when Array then tests
|
10
|
+
when Set then tests.to_a
|
11
|
+
else [tests]
|
12
|
+
end
|
13
|
+
super(route, parent)
|
14
|
+
end
|
15
|
+
|
16
|
+
def usable?(other)
|
17
|
+
other.class == self.class && other.tests == tests && other.request_method == request_method
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_code
|
21
|
+
"if #{@tests.map { |test| "#{test.inspect} === request.rack_request.#{request_method}" } * ' or '}
|
22
|
+
#{super}
|
23
|
+
end"
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect_label
|
27
|
+
"#{self.class.name.split("::").last} #{tests.inspect} (#{@matchers.size} matchers)"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -25,16 +25,14 @@ class HttpRouter
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def to_code
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end"}.join("\n")
|
37
|
-
"send(\"#{method_prefix}\#{request.path.first}\", request) if !request.path_finished? && #{lookup_ivar}.key?(request.path.first)"
|
28
|
+
part_name = "part#{root.next_counter}"
|
29
|
+
"unless request.path_finished?
|
30
|
+
#{part_name} = request.path.shift
|
31
|
+
case #{part_name}
|
32
|
+
#{@map.map{|k, v| "when #{k.inspect}; #{v.map(&:to_code) * "\n"};"} * "\n"}
|
33
|
+
end
|
34
|
+
request.path.unshift #{part_name}
|
35
|
+
end"
|
38
36
|
end
|
39
37
|
end
|
40
38
|
end
|
@@ -1,52 +1,42 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Node
|
3
3
|
class Path < Node
|
4
|
-
attr_reader :route, :param_names, :dynamic, :
|
4
|
+
attr_reader :route, :param_names, :dynamic, :path
|
5
5
|
alias_method :dynamic?, :dynamic
|
6
6
|
def initialize(router, parent, route, path, param_names = [])
|
7
|
-
@route, @
|
7
|
+
@route, @path, @param_names, @dynamic = route, path, param_names, !param_names.empty?
|
8
|
+
@route.add_path(self)
|
8
9
|
raise AmbiguousVariableException, "You have duplicate variable name present: #{param_names.join(', ')}" if param_names.uniq.size != param_names.size
|
9
|
-
Util.add_path_generation(self, route, @original_path) if @original_path.respond_to?(:split)
|
10
10
|
super router, parent
|
11
|
-
|
11
|
+
router.uncompile
|
12
12
|
end
|
13
13
|
|
14
14
|
def hashify_params(params)
|
15
15
|
@dynamic && params ? Hash[param_names.zip(params)] : {}
|
16
16
|
end
|
17
17
|
|
18
|
-
def url(args, options)
|
19
|
-
if path = raw_url(args, options)
|
20
|
-
raise TooManyParametersException unless args.empty?
|
21
|
-
[URI.escape(path), options]
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
18
|
def to_code
|
26
19
|
path_ivar = inject_root_ivar(self)
|
27
|
-
"#{"if request.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
throw :success, response.finish
|
33
|
-
end" if router.redirect_trailing_slash?}
|
20
|
+
"#{"if !callback && request.path.size == 1 && request.path.first == '' && (request.rack_request.head? || request.rack_request.get?) && request.rack_request.path_info[-1] == ?/
|
21
|
+
response = ::Rack::Response.new
|
22
|
+
response.redirect(request.rack_request.path_info[0, request.rack_request.path_info.size - 1], 302)
|
23
|
+
return response.finish
|
24
|
+
end" if router.redirect_trailing_slash?}
|
34
25
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
#{"end" unless route.match_partially?}
|
26
|
+
#{"if request.#{router.ignore_trailing_slash? ? 'path_finished?' : 'path.empty?'}" unless route.match_partially}
|
27
|
+
if callback
|
28
|
+
request.called = true
|
29
|
+
callback.call(Response.new(request, #{path_ivar}))
|
30
|
+
else
|
31
|
+
env = request.rack_request.dup.env
|
32
|
+
env['router.request'] = request
|
33
|
+
env['router.params'] ||= {}
|
34
|
+
#{"env['router.params'].merge!(Hash[#{param_names.inspect}.zip(request.params)])" if dynamic?}
|
35
|
+
@router.rewrite#{"_partial" if route.match_partially}_path_info(env, request)
|
36
|
+
response = @router.process_destination_path(#{path_ivar}, env)
|
37
|
+
return response unless router.pass_on_response(response)
|
48
38
|
end
|
49
|
-
#{"end" unless route.match_partially
|
39
|
+
#{"end" unless route.match_partially}"
|
50
40
|
end
|
51
41
|
|
52
42
|
def usable?(other)
|
@@ -54,12 +44,7 @@ class HttpRouter
|
|
54
44
|
end
|
55
45
|
|
56
46
|
def inspect_label
|
57
|
-
"Path: #{
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
def raw_url(args, options)
|
62
|
-
raise InvalidRouteException
|
47
|
+
"Path: #{path.inspect} for route #{route.name || 'unnamed route'} to #{route.dest.inspect}"
|
63
48
|
end
|
64
49
|
end
|
65
50
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class HttpRouter
|
2
|
+
class Node
|
3
|
+
class RequestMethod < AbstractRequestNode
|
4
|
+
def initialize(router, parent, request_methods)
|
5
|
+
super(router, parent, request_methods, :request_method)
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_code
|
9
|
+
"if #{@tests.map { |test| "#{test.inspect} === request.rack_request.#{request_method}" } * ' or '}
|
10
|
+
#{super}
|
11
|
+
end
|
12
|
+
request.acceptable_methods.merge(#{@tests.inspect})"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -8,14 +8,8 @@ class HttpRouter
|
|
8
8
|
@counter, @methods_module = 0, Module.new
|
9
9
|
end
|
10
10
|
|
11
|
-
def [](request)
|
12
|
-
compile
|
13
|
-
self[request]
|
14
|
-
end
|
15
|
-
alias_method :compiling_lookup, :[]
|
16
|
-
|
17
11
|
def uncompile
|
18
|
-
instance_eval "undef :
|
12
|
+
instance_eval "undef :call; def call(req); raise 'uncompiled root'; end", __FILE__, __LINE__ if compiled?
|
19
13
|
end
|
20
14
|
|
21
15
|
def next_counter
|
@@ -36,12 +30,112 @@ class HttpRouter
|
|
36
30
|
"Root (#{@matchers.size} matchers)"
|
37
31
|
end
|
38
32
|
|
39
|
-
|
40
|
-
|
33
|
+
def compile(routes)
|
34
|
+
routes.each {|route| add_route(route)}
|
41
35
|
root.extend(root.methods_module)
|
42
|
-
instance_eval "def
|
36
|
+
instance_eval "def call(request, &callback)\n#{to_code}\nnil\nend"
|
43
37
|
@compiled = true
|
44
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def add_route(route)
|
42
|
+
paths = if route.path_for_generation.nil?
|
43
|
+
route.match_partially = true
|
44
|
+
[]
|
45
|
+
elsif route.path_for_generation.is_a?(Regexp)
|
46
|
+
[route.path_for_generation]
|
47
|
+
else
|
48
|
+
path_for_generation = route.path_for_generation.dup
|
49
|
+
start_index, end_index = 0, 1
|
50
|
+
raw_paths, chars = [""], path_for_generation.split('')
|
51
|
+
until chars.empty?
|
52
|
+
case chars.first[0]
|
53
|
+
when ?(
|
54
|
+
chars.shift
|
55
|
+
(start_index...end_index).each { |path_index| raw_paths << raw_paths[path_index].dup }
|
56
|
+
start_index = end_index
|
57
|
+
end_index = raw_paths.size
|
58
|
+
when ?)
|
59
|
+
chars.shift
|
60
|
+
start_index -= end_index - start_index
|
61
|
+
else
|
62
|
+
c = if chars[0][0] == ?\\ && (chars[1][0] == ?( || chars[1][0] == ?)); chars.shift; chars.shift; else; chars.shift; end
|
63
|
+
(start_index...end_index).each { |path_index| raw_paths[path_index] << c }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
raw_paths
|
67
|
+
end
|
68
|
+
paths.reverse!
|
69
|
+
if paths.empty?
|
70
|
+
add_non_path_to_tree(route, @router.root, nil, [])
|
71
|
+
else
|
72
|
+
Generator.new(route, paths).each_path do |path_generator|
|
73
|
+
case path_generator.path
|
74
|
+
when Regexp
|
75
|
+
path_generator.param_names = path_generator.path.names.map(&:to_sym) if path_generator.path.respond_to?(:names)
|
76
|
+
add_non_path_to_tree(route, add_free_match(path_generator.path), path_generator.path, path_generator.param_names)
|
77
|
+
else
|
78
|
+
node = self
|
79
|
+
path_generator.path.split(/\//).each do |part|
|
80
|
+
next if part == ''
|
81
|
+
parts = part.scan(/\\.|[:*][a-z0-9_]+|[^:*\\]+/)
|
82
|
+
node = parts.size == 1 ? add_normal_part(route, node, part, path_generator) : add_complex_part(route, node, parts, path_generator)
|
83
|
+
end
|
84
|
+
add_non_path_to_tree(route, node, path_generator.path, path_generator.param_names)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_normal_part(route, node, part, path_generator)
|
91
|
+
name = part[1, part.size]
|
92
|
+
node = case part[0]
|
93
|
+
when ?\\
|
94
|
+
node.add_lookup(part[1].chr)
|
95
|
+
when ?:
|
96
|
+
path_generator.param_names << name.to_sym
|
97
|
+
route.matches_with(name) ? node.add_spanning_match(route.matches_with(name)) : node.add_variable
|
98
|
+
when ?*
|
99
|
+
path_generator.param_names << name.to_sym
|
100
|
+
route.matches_with(name) ? node.add_glob_regexp(route.matches_with(name)) : node.add_glob
|
101
|
+
else
|
102
|
+
node.add_lookup(part)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_complex_part(route, node, parts, path_generator)
|
107
|
+
capturing_indicies, splitting_indicies, captures, spans = [], [], 0, false
|
108
|
+
regex = parts.inject('') do |reg, part|
|
109
|
+
reg << case part[0]
|
110
|
+
when ?\\ then Regexp.quote(part[1].chr)
|
111
|
+
when ?:, ?*
|
112
|
+
spans = true if part[0] == ?*
|
113
|
+
captures += 1
|
114
|
+
(part[0] == ?* ? splitting_indicies : capturing_indicies) << captures
|
115
|
+
name = part[1, part.size].to_sym
|
116
|
+
path_generator.param_names << name.to_sym
|
117
|
+
if spans
|
118
|
+
route.matches_with(name) ? "((?:#{route.matches_with(name)}\\/?)+)" : '(.*?)'
|
119
|
+
else
|
120
|
+
"(#{(route.matches_with(name) || '[^/]*?')})"
|
121
|
+
end
|
122
|
+
else
|
123
|
+
Regexp.quote(part)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
spans ? node.add_spanning_match(Regexp.new("#{regex}$"), capturing_indicies, splitting_indicies) :
|
127
|
+
node.add_match(Regexp.new("#{regex}$"), capturing_indicies, splitting_indicies)
|
128
|
+
end
|
129
|
+
|
130
|
+
def add_non_path_to_tree(route, node, path, param_names)
|
131
|
+
node = node.add_host([route.host, route.other_hosts].flatten.compact) if route.host or route.other_hosts
|
132
|
+
node = node.add_user_agent(route.user_agent) if route.user_agent
|
133
|
+
node = node.add_scheme(route.scheme) if route.scheme
|
134
|
+
node = node.add_request_method(route.request_methods) if route.request_methods
|
135
|
+
path_obj = node.add_destination(route, path, param_names)
|
136
|
+
path_obj
|
137
|
+
end
|
138
|
+
|
45
139
|
end
|
46
140
|
end
|
47
141
|
end
|