http_router 0.8.1 → 0.8.2
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/lib/http_router.rb +6 -11
- data/lib/http_router/node.rb +21 -16
- data/lib/http_router/node/arbitrary.rb +3 -2
- data/lib/http_router/node/destination.rb +3 -2
- data/lib/http_router/node/free_regex.rb +4 -4
- data/lib/http_router/node/glob.rb +3 -3
- data/lib/http_router/node/glob_regex.rb +19 -12
- data/lib/http_router/node/lookup.rb +12 -8
- data/lib/http_router/node/regex.rb +2 -2
- data/lib/http_router/node/request.rb +11 -3
- data/lib/http_router/node/root.rb +22 -0
- data/lib/http_router/node/spanning_regex.rb +12 -12
- data/lib/http_router/node/variable.rb +2 -2
- data/lib/http_router/request.rb +3 -2
- data/lib/http_router/route.rb +5 -4
- data/lib/http_router/version.rb +1 -1
- data/test/test_recognize.rb +10 -0
- data/test/test_variable.rb +20 -0
- metadata +3 -2
data/lib/http_router.rb
CHANGED
@@ -39,7 +39,7 @@ class HttpRouter
|
|
39
39
|
@ignore_trailing_slash = options && options.key?(:ignore_trailing_slash) ? options[:ignore_trailing_slash] : true
|
40
40
|
@redirect_trailing_slash = options && options.key?(:redirect_trailing_slash) ? options[:redirect_trailing_slash] : false
|
41
41
|
@known_methods = Set.new(options && options[:known_methods] || [])
|
42
|
-
@
|
42
|
+
@counter = 0
|
43
43
|
reset!
|
44
44
|
instance_eval(&blk) if blk
|
45
45
|
end
|
@@ -138,7 +138,7 @@ class HttpRouter
|
|
138
138
|
|
139
139
|
# Resets the router to a clean state.
|
140
140
|
def reset!
|
141
|
-
@routes, @named_routes, @root = [], {}, Node.new(self
|
141
|
+
@routes, @named_routes, @root = [], {}, Node::Root.new(self)
|
142
142
|
@default_app = Proc.new{ |env| ::Rack::Response.new("Your request couldn't be found", 404).finish }
|
143
143
|
end
|
144
144
|
|
@@ -147,15 +147,6 @@ class HttpRouter
|
|
147
147
|
@default_app = app
|
148
148
|
end
|
149
149
|
|
150
|
-
def register_node(n)
|
151
|
-
@nodes << n
|
152
|
-
@nodes.size - 1
|
153
|
-
end
|
154
|
-
|
155
|
-
def [](pos)
|
156
|
-
@nodes.at(pos)
|
157
|
-
end
|
158
|
-
|
159
150
|
# Generate a URL for a specified route. This will accept a list of variable values plus any other variable names named as a hash.
|
160
151
|
# This first value must be either the Route object or the name of the route.
|
161
152
|
#
|
@@ -208,6 +199,10 @@ class HttpRouter
|
|
208
199
|
@root.compile
|
209
200
|
end
|
210
201
|
|
202
|
+
def next_counter
|
203
|
+
@counter += 1
|
204
|
+
end
|
205
|
+
|
211
206
|
private
|
212
207
|
def no_response(env, perform_call = true)
|
213
208
|
supported_methods = (@known_methods - [env['REQUEST_METHOD']]).select do |m|
|
data/lib/http_router/node.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Node
|
3
|
+
autoload :Root, 'http_router/node/root'
|
3
4
|
autoload :Glob, 'http_router/node/glob'
|
5
|
+
autoload :GlobRegex, 'http_router/node/glob_regex'
|
4
6
|
autoload :Variable, 'http_router/node/variable'
|
5
7
|
autoload :Regex, 'http_router/node/regex'
|
6
8
|
autoload :SpanningRegex, 'http_router/node/spanning_regex'
|
@@ -25,6 +27,10 @@ class HttpRouter
|
|
25
27
|
add(Glob.new(@router, self))
|
26
28
|
end
|
27
29
|
|
30
|
+
def add_glob_regexp(matcher)
|
31
|
+
add(GlobRegex.new(@router, self, matcher))
|
32
|
+
end
|
33
|
+
|
28
34
|
def add_request(opts)
|
29
35
|
add(Request.new(@router, self, opts))
|
30
36
|
end
|
@@ -57,20 +63,15 @@ class HttpRouter
|
|
57
63
|
false
|
58
64
|
end
|
59
65
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
66
|
+
private
|
67
|
+
def inject_root_methods(code = nil, &blk)
|
68
|
+
if code
|
69
|
+
root.methods_module.module_eval(code, __FILE__, __LINE__)
|
64
70
|
else
|
65
|
-
|
71
|
+
root.methods_module.module_eval(&blk)
|
66
72
|
end
|
67
73
|
end
|
68
74
|
|
69
|
-
def compile
|
70
|
-
instance_eval "def [](request)\n#{to_code}\nnil\nend", __FILE__, __LINE__
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
75
|
def add(matcher)
|
75
76
|
@matchers << matcher unless matcher.usable?(@matchers.last)
|
76
77
|
@matchers.last
|
@@ -80,14 +81,18 @@ class HttpRouter
|
|
80
81
|
@matchers.map{ |m| "# #{m.class}\n" << m.to_code }.join("\n") << "\n"
|
81
82
|
end
|
82
83
|
|
84
|
+
def root
|
85
|
+
@router.root
|
86
|
+
end
|
87
|
+
|
83
88
|
def depth
|
84
|
-
p = @parent
|
85
|
-
d =
|
86
|
-
until p.nil?
|
87
|
-
d += 1
|
88
|
-
p = p.parent
|
89
|
-
end
|
89
|
+
d, p = 0, @parent
|
90
|
+
d, p = d + 1, p.parent until p.nil?
|
90
91
|
d
|
91
92
|
end
|
93
|
+
|
94
|
+
def use_named_captures?
|
95
|
+
//.respond_to?(:names)
|
96
|
+
end
|
92
97
|
end
|
93
98
|
end
|
@@ -5,7 +5,6 @@ class HttpRouter
|
|
5
5
|
|
6
6
|
def initialize(router, parent, sallow_partial, blk, param_names)
|
7
7
|
@allow_partial, @blk, @param_names = allow_partial, blk, param_names
|
8
|
-
@node_position = router.register_node(blk)
|
9
8
|
super(router, parent)
|
10
9
|
end
|
11
10
|
|
@@ -14,13 +13,15 @@ class HttpRouter
|
|
14
13
|
end
|
15
14
|
|
16
15
|
def to_code
|
16
|
+
b, method_name = @blk, :"blk_#{router.next_counter}"
|
17
|
+
inject_root_methods { define_method(method_name) { b } }
|
17
18
|
"#{"if request.path_finished?" unless @allow_partial}
|
18
19
|
request.continue = proc { |state|
|
19
20
|
if state
|
20
21
|
#{super}
|
21
22
|
end
|
22
23
|
}
|
23
|
-
|
24
|
+
#{method_name}[request, #{@param_names.nil? || @param_names.empty? ? '{}' : "Hash[#{@param_names.inspect}.zip(request.params)]"}]
|
24
25
|
request.continue = nil
|
25
26
|
#{"end" unless @allow_partial}"
|
26
27
|
end
|
@@ -5,7 +5,6 @@ class HttpRouter
|
|
5
5
|
|
6
6
|
def initialize(router, parent, blk, allow_partial)
|
7
7
|
@blk, @allow_partial = blk, allow_partial
|
8
|
-
@node_position = router.register_node(blk)
|
9
8
|
super(router, parent)
|
10
9
|
end
|
11
10
|
|
@@ -14,9 +13,11 @@ class HttpRouter
|
|
14
13
|
end
|
15
14
|
|
16
15
|
def to_code
|
16
|
+
b, method_name = @blk, :"blk_#{router.next_counter}"
|
17
|
+
inject_root_methods { define_method(method_name) { b } }
|
17
18
|
"#{"if request.path_finished?" unless @allow_partial}
|
18
19
|
request.passed_with = catch(:pass) do
|
19
|
-
|
20
|
+
#{method_name}[request, #{@param_names.nil? || @param_names.empty? ? 'nil' : "Hash[#{@param_names.inspect}.zip(request.params)]"}]
|
20
21
|
end
|
21
22
|
#{"end" unless @allow_partial}"
|
22
23
|
end
|
@@ -8,17 +8,17 @@ class HttpRouter
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def to_code
|
11
|
-
"whole_path = \"/\#{request.joined_path}\"
|
12
|
-
if match = #{matcher.inspect}.match(whole_path) and match[0].size == whole_path.size
|
11
|
+
"whole_path#{depth} = \"/\#{request.joined_path}\"
|
12
|
+
if match = #{matcher.inspect}.match(whole_path#{depth}) and match[0].size == whole_path#{depth}.size
|
13
13
|
request.extra_env['router.regex_match'] = match
|
14
14
|
old_path = request.path
|
15
15
|
request.path = ['']
|
16
|
-
" << (
|
16
|
+
" << (use_named_captures? ?
|
17
17
|
"match.names.size.times{|i| request.params << match[i + 1]} if match.respond_to?(:names) && match.names" : "") << "
|
18
18
|
#{super}
|
19
19
|
request.path = old_path
|
20
20
|
request.extra_env.delete('router.regex_match')
|
21
|
-
" << (
|
21
|
+
" << (use_named_captures? ?
|
22
22
|
"params.slice!(-match.names.size, match.names.size)" : ""
|
23
23
|
) << "
|
24
24
|
end"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Node
|
3
3
|
class Glob < Node
|
4
|
+
alias_method :node_to_code, :to_code
|
4
5
|
def usable?(other)
|
5
6
|
other.class == self.class
|
6
7
|
end
|
@@ -9,12 +10,11 @@ class HttpRouter
|
|
9
10
|
"request.params << (globbed_params#{depth} = [])
|
10
11
|
remaining_parts = request.path.dup
|
11
12
|
until remaining_parts.empty?
|
12
|
-
globbed_params#{depth} <<
|
13
|
+
globbed_params#{depth} << remaining_parts.shift
|
13
14
|
request.path = remaining_parts
|
14
15
|
#{super}
|
15
16
|
end
|
16
|
-
request.path[0,0] = request.params.pop
|
17
|
-
"
|
17
|
+
request.path[0,0] = request.params.pop"
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -1,18 +1,25 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Node
|
3
|
-
class GlobRegex <
|
3
|
+
class GlobRegex < Glob
|
4
|
+
attr_reader :matcher
|
5
|
+
def initialize(router, parent, matcher)
|
6
|
+
@matcher = matcher
|
7
|
+
super router, parent
|
8
|
+
end
|
9
|
+
|
10
|
+
def usable?(other)
|
11
|
+
other.class == self.class && other.matcher == matcher
|
12
|
+
end
|
13
|
+
|
4
14
|
def to_code
|
5
|
-
"
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
request.path =
|
13
|
-
#{@capturing_indicies.size == 1 ? "request.params.pop" : "request.params.slice!(#{-@capturing_indicies.size}, #{@capturing_indicies.size})"}
|
14
|
-
end
|
15
|
-
"
|
15
|
+
"request.params << (globbed_params#{depth} = [])
|
16
|
+
remaining_parts = request.path.dup
|
17
|
+
while !remaining_parts.empty? and match = remaining_parts.first.match(#{@matcher.inspect}) and match[0] == remaining_parts.first
|
18
|
+
globbed_params#{depth} << remaining_parts.shift
|
19
|
+
request.path = remaining_parts
|
20
|
+
#{node_to_code}
|
21
|
+
end
|
22
|
+
request.path[0,0] = request.params.pop"
|
16
23
|
end
|
17
24
|
end
|
18
25
|
end
|
@@ -15,15 +15,19 @@ class HttpRouter
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def to_code
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
inject_root_methods @map.keys.map {|k|
|
19
|
+
method = :"lookup_#{object_id}_#{k.hash}"
|
20
|
+
"define_method(#{method.inspect}) do |request|
|
21
|
+
part = request.path.shift
|
22
|
+
#{@map[k].map{|n| n.to_code} * "\n"}
|
23
|
+
request.path.unshift part
|
24
|
+
end"}.join("\n")
|
25
|
+
code = "
|
26
|
+
unless request.path_finished?
|
27
|
+
m = \"lookup_#{object_id}_\#{request.path.first.hash}\"
|
28
|
+
send(m, request) if respond_to?(m)
|
25
29
|
end
|
26
|
-
|
30
|
+
"
|
27
31
|
end
|
28
32
|
end
|
29
33
|
end
|
@@ -17,8 +17,8 @@ class HttpRouter
|
|
17
17
|
params_size = @splitting_indicies.size + @capturing_indicies.size
|
18
18
|
"if match = #{@matcher.inspect}.match(request.path.first) and match.begin(0).zero?
|
19
19
|
part = request.path.shift\n" <<
|
20
|
-
@splitting_indicies.map { |s| "request.params <<
|
21
|
-
@capturing_indicies.map { |c| "request.params <<
|
20
|
+
@splitting_indicies.map { |s| "request.params << match[#{s}].split(/\\//)\n" }.join <<
|
21
|
+
@capturing_indicies.map { |c| "request.params << match[#{c}]\n" }.join << "
|
22
22
|
#{super}
|
23
23
|
request.path.unshift part
|
24
24
|
#{params_size == 1 ? "request.params.pop" : "request.params.slice!(#{-params_size}, #{params_size})"}
|
@@ -16,13 +16,21 @@ class HttpRouter
|
|
16
16
|
def to_code
|
17
17
|
code = "if "
|
18
18
|
code << @opts.map do |k,v|
|
19
|
-
case v
|
20
|
-
when
|
21
|
-
else
|
19
|
+
case v.size
|
20
|
+
when 1 then to_code_condition(k, v.first)
|
21
|
+
else "(#{v.map{|k, vv| to_code_condition(k, vv)}.join(' or ')})"
|
22
22
|
end
|
23
23
|
end * ' and '
|
24
24
|
code << "\n #{super}\nend"
|
25
25
|
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def to_code_condition(k, v)
|
29
|
+
case v
|
30
|
+
when String then "#{v.inspect} == request.rack_request.#{k}"
|
31
|
+
else "#{v.inspect} === request.rack_request.#{k}"
|
32
|
+
end
|
33
|
+
end
|
26
34
|
end
|
27
35
|
end
|
28
36
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class HttpRouter
|
2
|
+
class Node
|
3
|
+
class Root < Node
|
4
|
+
attr_reader :methods_module
|
5
|
+
def initialize(router)
|
6
|
+
super(router, nil)
|
7
|
+
@methods_module = Module.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](request)
|
11
|
+
compile
|
12
|
+
self[request]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def compile
|
17
|
+
root.extend(root.methods_module)
|
18
|
+
instance_eval "def [](request)\n#{to_code}\nnil\nend", __FILE__, __LINE__
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -3,18 +3,18 @@ class HttpRouter
|
|
3
3
|
class SpanningRegex < Regex
|
4
4
|
def to_code
|
5
5
|
params_count = (@splitting_indicies || []).size + @capturing_indicies.size
|
6
|
-
"whole_path = request.joined_path
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
6
|
+
"whole_path#{depth} = request.joined_path
|
7
|
+
if match = #{@matcher.inspect}.match(whole_path#{depth}) and match.begin(0).zero?
|
8
|
+
original_path#{depth} = request.path.dup
|
9
|
+
" <<
|
10
|
+
(@splitting_indicies || []).map { |s| "request.params << match[#{s}].split(/\\//)\n" }.join <<
|
11
|
+
@capturing_indicies.map { |c| "request.params << match[#{c}]\n" }.join << "
|
12
|
+
remaining_path = whole_path#{depth}[match[0].size + (whole_path#{depth}[match[0].size] == ?/ ? 1 : 0), whole_path#{depth}.size]
|
13
|
+
request.path = remaining_path.split('/')
|
14
|
+
#{node_to_code}
|
15
|
+
request.path = original_path#{depth}
|
16
|
+
request.params.slice!(#{-params_count.size}, #{params_count})
|
17
|
+
end
|
18
18
|
"
|
19
19
|
end
|
20
20
|
end
|
@@ -7,9 +7,9 @@ class HttpRouter
|
|
7
7
|
|
8
8
|
def to_code
|
9
9
|
"unless request.path_finished?
|
10
|
-
request.params <<
|
10
|
+
request.params << request.path.shift
|
11
11
|
#{super}
|
12
|
-
request.path.unshift
|
12
|
+
request.path.unshift request.params.pop
|
13
13
|
end"
|
14
14
|
end
|
15
15
|
end
|
data/lib/http_router/request.rb
CHANGED
@@ -5,8 +5,9 @@ class HttpRouter
|
|
5
5
|
alias_method :rack, :rack_request
|
6
6
|
def initialize(path, rack_request, perform_call, &acceptance_test)
|
7
7
|
@rack_request, @perform_call, @acceptance_test = rack_request, perform_call, acceptance_test
|
8
|
-
@path = (path
|
9
|
-
@path
|
8
|
+
@path = URI.unescape(path).split(/\//)
|
9
|
+
@path.shift if @path.first == ''
|
10
|
+
@path.push('') if path[-1] == ?/
|
10
11
|
@extra_env = {}
|
11
12
|
@params = []
|
12
13
|
end
|
data/lib/http_router/route.rb
CHANGED
@@ -210,9 +210,9 @@ class HttpRouter
|
|
210
210
|
when ?*
|
211
211
|
param_names << name.to_sym
|
212
212
|
matches_with[name.to_sym] = @opts[name.to_sym]
|
213
|
-
@opts[name.to_sym] ? node.
|
213
|
+
@opts[name.to_sym] ? node.add_glob_regexp(@opts.delete(name.to_sym)) : node.add_glob
|
214
214
|
else
|
215
|
-
node.add_lookup(
|
215
|
+
node.add_lookup(parts[0])
|
216
216
|
end
|
217
217
|
else
|
218
218
|
capturing_indicies = []
|
@@ -237,9 +237,10 @@ class HttpRouter
|
|
237
237
|
name = part[1, part.size].to_sym
|
238
238
|
param_names << name
|
239
239
|
matches_with[name] = @opts[name]
|
240
|
-
|
240
|
+
@opts[name] ?
|
241
|
+
"((?:#{@opts[name]}\\/?)+)" : '(.*?)'
|
241
242
|
else
|
242
|
-
Regexp.quote(
|
243
|
+
Regexp.quote(part)
|
243
244
|
end
|
244
245
|
end
|
245
246
|
node = spans ? node.add_spanning_match(Regexp.new("#{regex}$"), capturing_indicies, splitting_indicies) :
|
data/lib/http_router/version.rb
CHANGED
data/test/test_recognize.rb
CHANGED
@@ -92,6 +92,16 @@ class TestRecognition < MiniTest::Unit::TestCase
|
|
92
92
|
assert_body '/', router.call(Rack::MockRequest.env_for('/'))
|
93
93
|
end
|
94
94
|
|
95
|
+
def test_request_mutation
|
96
|
+
got_this_far = false
|
97
|
+
non_matching, matching = router {
|
98
|
+
add("/test/:var/:var2/*glob").matching(:var2 => /123/, :glob => /[a-z]+/).get.arbitrary{|env, params| got_this_far = true; false}
|
99
|
+
add("/test/:var/:var2/*glob").matching(:var2 => /123/, :glob => /[a-z]+/).get
|
100
|
+
}
|
101
|
+
assert_route matching, '/test/123/123/asd/aasd/zxcqwe/asdzxc', {:var => '123', :var2 => '123', :glob => %w{asd aasd zxcqwe asdzxc}}
|
102
|
+
assert got_this_far, "matching should have gotten this far"
|
103
|
+
end
|
104
|
+
|
95
105
|
def test_multiple_partial
|
96
106
|
test, root = router {
|
97
107
|
add("/test").partial.to{|env| [200, {}, ['/test',env['PATH_INFO']]]}
|
data/test/test_variable.rb
CHANGED
@@ -90,6 +90,19 @@ class TestVariable < MiniTest::Unit::TestCase
|
|
90
90
|
assert_route '/test/*variable/test', '/test/one/two/three/test', {:variable => ['one', 'two', 'three']}
|
91
91
|
end
|
92
92
|
|
93
|
+
def test_glob_with_regexp
|
94
|
+
r = router { add('/test/*variable', :variable => /[a-z]+/) }
|
95
|
+
assert_route nil, '/test/asd/123'
|
96
|
+
assert_route nil, '/test/asd/asd123'
|
97
|
+
assert_route r, '/test/asd/qwe', :variable => ['asd', 'qwe']
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_glob_with_regexp_and_static
|
101
|
+
r = router { add('/test/*variable/test', :variable => /[a-z]+/) }
|
102
|
+
assert_route nil, '/test/asd/123/test'
|
103
|
+
assert_route r, '/test/asd/qwe/test', :variable => ['asd', 'qwe']
|
104
|
+
end
|
105
|
+
|
93
106
|
def test_glob_with_variable
|
94
107
|
assert_route '/test/*variable/:test', '/test/one/two/three', {:variable => ['one', 'two'], :test => 'three'}
|
95
108
|
end
|
@@ -98,6 +111,13 @@ class TestVariable < MiniTest::Unit::TestCase
|
|
98
111
|
assert_route '/test/*variable.:format', 'test/one/two/three.html', {:variable => ['one', 'two', 'three'], :format => 'html'}
|
99
112
|
end
|
100
113
|
|
114
|
+
def test_glob_regexp_with_format
|
115
|
+
r = router { add('/test/*variable.:format', :variable => /[a-z]+/, :format => 'html') }
|
116
|
+
assert_route nil, 'test/one/2/three.html'
|
117
|
+
assert_route nil, 'test/one/two/three.xml'
|
118
|
+
assert_route r, 'test/one/two/three.html', {:variable => ['one', 'two', 'three'], :format => 'html'}
|
119
|
+
end
|
120
|
+
|
101
121
|
def test_glob_with_optional_format
|
102
122
|
assert_route '/test/*variable(.:format)', 'test/one/two/three.html', {:variable => ['one', 'two', 'three'], :format => 'html'}
|
103
123
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: http_router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.8.
|
5
|
+
version: 0.8.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Joshua Hull
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-01 00:00:00 -
|
13
|
+
date: 2011-06-01 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -155,6 +155,7 @@ files:
|
|
155
155
|
- lib/http_router/node/lookup.rb
|
156
156
|
- lib/http_router/node/regex.rb
|
157
157
|
- lib/http_router/node/request.rb
|
158
|
+
- lib/http_router/node/root.rb
|
158
159
|
- lib/http_router/node/spanning_regex.rb
|
159
160
|
- lib/http_router/node/variable.rb
|
160
161
|
- lib/http_router/optional_compiler.rb
|