http_router 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|