http_router 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +6 -5
- data/benchmarks/rack_mount.rb +16 -45
- data/benchmarks/rec2.rb +8 -8
- data/http_router.gemspec +5 -4
- data/lib/http_router/interface/sinatra.rb +7 -7
- data/lib/http_router/node.rb +106 -105
- data/lib/http_router/optional_compiler.rb +14 -5
- data/lib/http_router/path.rb +18 -28
- data/lib/http_router/root.rb +17 -29
- data/lib/http_router/route.rb +47 -16
- data/lib/http_router/static.rb +5 -0
- data/lib/http_router/variable.rb +2 -5
- data/lib/http_router/version.rb +1 -1
- data/lib/http_router.rb +38 -65
- data/test/helper.rb +74 -0
- data/test/rack/test_dispatch.rb +120 -0
- data/test/rack/test_route.rb +44 -0
- data/test/rack/test_urlmap.rb +12 -0
- data/{spec → test}/sinatra/recognize_spec.rb +0 -0
- data/test/sinatra/test_recognize.rb +150 -0
- data/test/test_arbitrary.rb +50 -0
- data/test/test_generate.rb +93 -0
- data/test/test_greedy.rb +24 -0
- data/test/test_interstitial.rb +47 -0
- data/test/test_misc.rb +30 -0
- data/test/test_mounting.rb +89 -0
- data/test/test_recognize.rb +56 -0
- data/test/test_request.rb +85 -0
- data/test/test_trailing_slash.rb +28 -0
- data/test/test_variable.rb +108 -0
- metadata +41 -32
- data/lib/http_router/response.rb +0 -46
- data/spec/generate_spec.rb +0 -234
- data/spec/misc_spec.rb +0 -65
- data/spec/mounting_spec.rb +0 -5
- data/spec/rack/dispatch_spec.rb +0 -119
- data/spec/rack/generate_spec.rb +0 -29
- data/spec/rack/middleware_spec.rb +0 -22
- data/spec/rack/route_spec.rb +0 -72
- data/spec/rack/urlmap_spec.rb +0 -13
- data/spec/recognize_spec.rb +0 -497
- data/spec/spec_helper.rb +0 -25
data/Rakefile
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler'
|
3
3
|
require 'code_stats'
|
4
|
-
require 'rspec/core/rake_task'
|
5
4
|
|
6
|
-
desc "Run
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
desc "Run tests"
|
6
|
+
task :test do
|
7
|
+
$: << 'lib'
|
8
|
+
require 'http_router'
|
9
|
+
require 'test/helper'
|
10
|
+
Dir['test/**/test_*.rb'].each { |test| require test }
|
10
11
|
end
|
11
12
|
|
12
13
|
require 'rake/rdoctask'
|
data/benchmarks/rack_mount.rb
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rbench'
|
3
3
|
require 'rack'
|
4
4
|
require 'rack/mount'
|
5
|
-
require '../usher/lib/usher'
|
5
|
+
#require '../usher/lib/usher'
|
6
6
|
require 'lib/http_router'
|
7
7
|
|
8
8
|
set = Rack::Mount::RouteSet.new do |set|
|
@@ -11,10 +11,10 @@ set = Rack::Mount::RouteSet.new do |set|
|
|
11
11
|
set.add_route(proc{|env| [200, {'Content-type'=>'text/html'}, []]}, {:path => '/dynamic/:variable'}, {}, :variable)
|
12
12
|
end
|
13
13
|
|
14
|
-
u = Usher::Interface.for(:rack)
|
15
|
-
u.add('/simple').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
|
16
|
-
u.add('/simple/again').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
|
17
|
-
u.add('/dynamic/anything').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
|
14
|
+
#u = Usher::Interface.for(:rack)
|
15
|
+
#u.add('/simple').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
|
16
|
+
#u.add('/simple/again').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
|
17
|
+
#u.add('/dynamic/anything').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
|
18
18
|
|
19
19
|
hr = HttpRouter.new
|
20
20
|
hr.add('/simple').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
|
@@ -27,50 +27,21 @@ simple_env = Rack::MockRequest.env_for('/simple')
|
|
27
27
|
simple2_env = Rack::MockRequest.env_for('/simple/again')
|
28
28
|
simple_and_dynamic_env = Rack::MockRequest.env_for('/dynamic/anything')
|
29
29
|
|
30
|
-
|
30
|
+
3.times do
|
31
31
|
|
32
|
-
|
33
|
-
set.url(simple_env, :simple)
|
34
|
-
end
|
35
|
-
|
36
|
-
report "4 levels, static" do
|
37
|
-
set.url(simple_env, :again)
|
38
|
-
end
|
39
|
-
|
40
|
-
report "4 levels, 1 dynamic" do
|
41
|
-
set.url(simple_env, :variable, {:variable => 'onemore'})
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
32
|
+
RBench.run(TIMES) do
|
45
33
|
|
46
|
-
|
34
|
+
report "2 levels, static" do
|
35
|
+
set.url(simple_env, :simple)
|
36
|
+
end
|
47
37
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
report "4 levels, static" do
|
53
|
-
set.url(simple_env, :again)
|
54
|
-
end
|
38
|
+
report "4 levels, static" do
|
39
|
+
set.url(simple_env, :again)
|
40
|
+
end
|
55
41
|
|
56
|
-
|
57
|
-
|
58
|
-
|
42
|
+
report "4 levels, 1 dynamic" do
|
43
|
+
set.url(simple_env, :variable, {:variable => 'onemore'})
|
44
|
+
end
|
59
45
|
|
60
|
-
end
|
61
|
-
|
62
|
-
RBench.run(TIMES) do
|
63
|
-
|
64
|
-
report "2 levels, static" do
|
65
|
-
set.url(simple_env, :simple)
|
66
46
|
end
|
67
|
-
|
68
|
-
report "4 levels, static" do
|
69
|
-
set.url(simple_env, :again)
|
70
|
-
end
|
71
|
-
|
72
|
-
report "4 levels, 1 dynamic" do
|
73
|
-
set.url(simple_env, :variable, {:variable => 'onemore'})
|
74
|
-
end
|
75
|
-
|
76
47
|
end
|
data/benchmarks/rec2.rb
CHANGED
@@ -8,9 +8,9 @@ require 'http_router'
|
|
8
8
|
|
9
9
|
u = HttpRouter.new
|
10
10
|
u.add('/simple').to {|env| [200, {'Content-type'=>'text/html'}, []]}
|
11
|
-
u.add('/simple/again').
|
11
|
+
u.add('/simple/again').to {|env| [200, {'Content-type'=>'text/html'}, []]}
|
12
12
|
#u.add('/simple/again/and/again').compile.to {|env| [200, {'Content-type'=>'text/html'}, []]}
|
13
|
-
u.add('/dynamic/:variable').
|
13
|
+
u.add('/dynamic/:variable').to {|env| [200, {'Content-type'=>'text/html'}, []]}
|
14
14
|
#u.add('/rails/:controller/:action/:id').compile.to {|env| [200, {'Content-type'=>'text/html'}, []]}
|
15
15
|
#u.add('/greedy/:greed').matching(:greed => /.*/).compile.to {|env| [200, {'Content-type'=>'text/html'}, []]}
|
16
16
|
#u.add('/greedy/hey.:greed.html').to {|env| [200, {'Content-type'=>'text/html'}, []]}
|
@@ -27,10 +27,10 @@ puts Benchmark.measure {
|
|
27
27
|
#
|
28
28
|
TIMES = 50_000
|
29
29
|
|
30
|
-
simple_env =
|
31
|
-
simple2_env =
|
30
|
+
#simple_env =
|
31
|
+
#simple2_env =
|
32
32
|
#simple3_env = Rack::MockRequest.env_for('/simple/again/and/again')
|
33
|
-
simple_and_dynamic_env =
|
33
|
+
#simple_and_dynamic_env =
|
34
34
|
#simple_and_dynamic_env1 = Rack::MockRequest.env_for('/rails/controller/action/id')
|
35
35
|
#simple_and_dynamic_env2 = Rack::MockRequest.env_for('/greedy/controller/action/id')
|
36
36
|
#simple_and_dynamic_env3 = Rack::MockRequest.env_for('/greedy/hey.hello.html')
|
@@ -38,11 +38,11 @@ simple_and_dynamic_env = Rack::MockRequest.env_for('/dynamic/anything')
|
|
38
38
|
RBench.run(TIMES) do
|
39
39
|
|
40
40
|
report "2 levels, static" do
|
41
|
-
u.call(
|
41
|
+
u.call(Rack::MockRequest.env_for('/simple')).first == 200 or raise
|
42
42
|
end
|
43
43
|
|
44
44
|
report "4 levels, static" do
|
45
|
-
u.call(
|
45
|
+
u.call(Rack::MockRequest.env_for('/simple/again')).first == 200 or raise
|
46
46
|
end
|
47
47
|
|
48
48
|
#report "8 levels, static" do
|
@@ -50,7 +50,7 @@ simple_and_dynamic_env = Rack::MockRequest.env_for('/dynamic/anything')
|
|
50
50
|
#end
|
51
51
|
|
52
52
|
report "4 levels, 1 dynamic" do
|
53
|
-
u.call(
|
53
|
+
u.call(Rack::MockRequest.env_for('/dynamic/anything')).first == 200 or raise
|
54
54
|
end
|
55
55
|
|
56
56
|
#report "8 levels, 3 dynamic" do
|
data/http_router.gemspec
CHANGED
@@ -20,14 +20,15 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.rubyforge_project = 'http_router'
|
21
21
|
|
22
22
|
# dependencies
|
23
|
-
s.add_runtime_dependency 'rack',
|
24
|
-
s.add_runtime_dependency 'url_mount',
|
25
|
-
s.add_development_dependency '
|
23
|
+
s.add_runtime_dependency 'rack', '>= 1.0.0'
|
24
|
+
s.add_runtime_dependency 'url_mount', '~> 0.2.1'
|
25
|
+
s.add_development_dependency 'minitest', '~> 2.0.0'
|
26
26
|
s.add_development_dependency 'code_stats'
|
27
27
|
s.add_development_dependency 'rake'
|
28
28
|
s.add_development_dependency 'sinatra'
|
29
29
|
s.add_development_dependency 'rbench'
|
30
|
-
s.add_development_dependency '
|
30
|
+
s.add_development_dependency 'phocus'
|
31
|
+
s.add_development_dependency 'bundler', '~> 1.0.0'
|
31
32
|
|
32
33
|
if s.respond_to? :specification_version then
|
33
34
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
@@ -25,16 +25,16 @@ class HttpRouter
|
|
25
25
|
private
|
26
26
|
def route!(base=self.class, pass_block=nil)
|
27
27
|
if base.router and match = base.router.recognize(@request)
|
28
|
-
if match.
|
29
|
-
@block_params = match.
|
30
|
-
(@params ||= {}).merge!(match.
|
28
|
+
if match.first.respond_to?(:path)
|
29
|
+
@block_params = match.first.param_values
|
30
|
+
(@params ||= {}).merge!(match.first.params)
|
31
31
|
pass_block = catch(:pass) do
|
32
|
-
route_eval(&match.route.dest)
|
32
|
+
route_eval(&match.first.path.route.dest)
|
33
33
|
end
|
34
|
-
|
34
|
+
elsif match.is_a?(Array)
|
35
35
|
route_eval {
|
36
|
-
match.
|
37
|
-
status match
|
36
|
+
match[1].each{|k,v| response[k] = v}
|
37
|
+
status match[0]
|
38
38
|
}
|
39
39
|
end
|
40
40
|
end
|
data/lib/http_router/node.rb
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Node
|
3
|
+
class Response < Struct.new(:path, :param_values)
|
4
|
+
attr_reader :params
|
5
|
+
def initialize(path, param_values)
|
6
|
+
super
|
7
|
+
if path.splitting_indexes
|
8
|
+
param_values = param_values.dup
|
9
|
+
path.splitting_indexes.each{|i| param_values[i] = param_values[i].split(HttpRouter::Parts::SLASH_RX)}
|
10
|
+
end
|
11
|
+
@params = path.route.default_values ? path.route.default_values.merge(path.hashify_params(param_values)) : path.hashify_params(param_values)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
3
15
|
attr_accessor :value, :variable, :catchall
|
4
16
|
attr_reader :linear, :lookup, :request_node, :arbitrary_node
|
5
17
|
|
@@ -17,9 +29,7 @@ class HttpRouter
|
|
17
29
|
if val.matches_with
|
18
30
|
add_to_linear(val)
|
19
31
|
else
|
20
|
-
|
21
|
-
@catchall.variable = val
|
22
|
-
@catchall
|
32
|
+
add_to_catchall(val)
|
23
33
|
end
|
24
34
|
elsif val.is_a?(Regexp)
|
25
35
|
add_to_linear(val)
|
@@ -29,20 +39,6 @@ class HttpRouter
|
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
|
-
def add_request_methods(options)
|
33
|
-
if !options.empty?
|
34
|
-
generate_request_method_tree(options)
|
35
|
-
elsif @request_node
|
36
|
-
current_node = @request_node
|
37
|
-
while current_node.request_method
|
38
|
-
current_node = (current_node.catchall ||= router.request_node)
|
39
|
-
end
|
40
|
-
[current_node]
|
41
|
-
else
|
42
|
-
[self]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
42
|
def add_to_linear(val)
|
47
43
|
create_linear
|
48
44
|
n = if @linear.assoc(val)
|
@@ -52,13 +48,7 @@ class HttpRouter
|
|
52
48
|
@linear << [val, new_node]
|
53
49
|
new_node
|
54
50
|
end
|
55
|
-
@linear.sort!{|a, b|
|
56
|
-
if a.first.respond_to?(:priority) and b.first.respond_to?(:priority) ; b.first.priority <=> a.first.priority
|
57
|
-
elsif a.first.respond_to?(:priority) ; -1
|
58
|
-
elsif b.first.respond_to?(:priority) ; 1
|
59
|
-
else ; 0
|
60
|
-
end
|
61
|
-
}
|
51
|
+
@linear.sort!{|a, b| b.first.priority <=> a.first.priority }
|
62
52
|
n
|
63
53
|
end
|
64
54
|
|
@@ -80,35 +70,20 @@ class HttpRouter
|
|
80
70
|
target
|
81
71
|
end
|
82
72
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
def transplant_value
|
88
|
-
if @value
|
89
|
-
target_node = @request_node
|
90
|
-
while target_node.request_method
|
91
|
-
target_node = (target_node.catchall ||= router.request_node)
|
92
|
-
end
|
93
|
-
target_node.value = @value
|
94
|
-
@value = nil
|
95
|
-
end
|
73
|
+
def add_to_catchall(val)
|
74
|
+
(@catchall ||= router.node).variable = val
|
75
|
+
@catchall
|
96
76
|
end
|
97
77
|
|
98
|
-
def
|
99
|
-
raise UnsupportedRequestConditionError if (request_options.keys & RequestNode::RequestMethods).size != request_options.size
|
78
|
+
def add_request_methods(request_options)
|
79
|
+
raise UnsupportedRequestConditionError if request_options && (request_options.keys & RequestNode::RequestMethods).size != request_options.size
|
100
80
|
current_nodes = [self]
|
101
81
|
RequestNode::RequestMethods.each do |method|
|
102
|
-
if request_options.key?(method) # so, the request method we care about it ..
|
103
|
-
if current_nodes == [self]
|
104
|
-
current_nodes = [@request_node ||= router.request_node]
|
105
|
-
end
|
106
|
-
|
82
|
+
if request_options && request_options.key?(method) # so, the request method we care about it ..
|
83
|
+
current_nodes = [@request_node ||= router.request_node] if current_nodes == [self]
|
107
84
|
for current_node_index in (0...current_nodes.size)
|
108
85
|
current_node = current_nodes.at(current_node_index)
|
109
|
-
unless current_node.request_method
|
110
|
-
current_node.request_method = method
|
111
|
-
end
|
86
|
+
current_node.request_method = method unless current_node.request_method
|
112
87
|
case RequestNode::RequestMethods.index(method) <=> RequestNode::RequestMethods.index(current_node.request_method)
|
113
88
|
when 0 #use this node
|
114
89
|
Array(request_options[method]).each_with_index do |request_value, index|
|
@@ -134,66 +109,102 @@ class HttpRouter
|
|
134
109
|
end
|
135
110
|
end
|
136
111
|
current_nodes.flatten!
|
137
|
-
|
138
|
-
current_nodes.map!{|n| n.catchall ||= router.request_node}
|
112
|
+
else
|
113
|
+
current_nodes.map!{|n| n.is_a?(RequestNode) && n.request_method == method ? (n.catchall ||= router.request_node) : n}
|
139
114
|
end
|
140
115
|
end
|
141
116
|
transplant_value
|
142
117
|
current_nodes
|
143
118
|
end
|
144
119
|
|
120
|
+
protected
|
121
|
+
|
122
|
+
attr_reader :router
|
123
|
+
|
124
|
+
def transplant_value
|
125
|
+
if @value && @request_node
|
126
|
+
target_node = @request_node
|
127
|
+
while target_node.request_method
|
128
|
+
target_node = (target_node.catchall ||= router.request_node)
|
129
|
+
end
|
130
|
+
target_node.value ||= @value
|
131
|
+
@value = nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
145
135
|
def escape_val(val)
|
146
136
|
val.is_a?(Array) ? val.each{|v| HttpRouter.uri_unescape!(v)} : HttpRouter.uri_unescape!(val)
|
147
137
|
val
|
148
138
|
end
|
149
139
|
|
150
|
-
def find_on_parts(request, parts,
|
140
|
+
def find_on_parts(request, parts, action = :call, params = [])
|
151
141
|
if parts and !parts.empty?
|
152
|
-
if parts.size == 1 and parts.first == ''
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
if @linear && !@linear.empty?
|
157
|
-
response, dupped_parts, dupped_params = nil, nil, nil
|
158
|
-
next_node = @linear.find do |(tester, node)|
|
142
|
+
find_on_parts(request, nil, :"#{action}_with_trailing_slash", params) if parts.size == 1 and parts.first == ''
|
143
|
+
if @linear
|
144
|
+
dupped_parts, dupped_params = nil, nil
|
145
|
+
response = @linear.find do |(tester, node)|
|
159
146
|
if tester.respond_to?(:matches?) and match = tester.matches?(parts)
|
160
147
|
dupped_parts, dupped_params = parts.dup, params.dup
|
161
148
|
dupped_params << escape_val(tester.consume(match, dupped_parts))
|
162
|
-
node.find_on_parts(request, dupped_parts,
|
149
|
+
node.find_on_parts(request, dupped_parts, action, dupped_params)
|
163
150
|
elsif tester.respond_to?(:match) and match = tester.match(parts.whole_path) and match.begin(0) == 0
|
164
151
|
dupped_parts, dupped_params = router.split(parts.whole_path[match[0].size, parts.whole_path.size]), params.dup
|
165
|
-
node.find_on_parts(request, dupped_parts,
|
152
|
+
node.find_on_parts(request, dupped_parts, action, dupped_params)
|
166
153
|
else
|
167
154
|
nil
|
168
155
|
end
|
169
156
|
end
|
170
157
|
end
|
171
158
|
if match = @lookup && @lookup[parts.first]
|
172
|
-
match.find_on_parts(request, parts[1, parts.size - 1],
|
159
|
+
match.find_on_parts(request, parts[1, parts.size - 1], action, params)
|
173
160
|
end
|
174
|
-
if
|
161
|
+
if catchall
|
175
162
|
dupped_parts, dupped_params = parts.dup, params.dup
|
176
|
-
dupped_params << escape_val(
|
177
|
-
|
163
|
+
dupped_params << escape_val(catchall.variable.consume(nil, dupped_parts))
|
164
|
+
catchall.find_on_parts(request, dupped_parts, action, dupped_params)
|
178
165
|
end
|
179
166
|
end
|
180
|
-
if request_node
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
elsif @value
|
185
|
-
process_match(self, parts, params, routes)
|
186
|
-
else
|
187
|
-
nil
|
188
|
-
end
|
167
|
+
request_node.find_on_request_methods(request, parts, action, params) if request_node
|
168
|
+
arbitrary_node.find_on_arbitrary(request, parts, action, params) if arbitrary_node
|
169
|
+
response = process_match(self, parts, params, request, action) if @value
|
170
|
+
nil
|
189
171
|
end
|
190
172
|
|
191
|
-
def process_match(node, parts, params,
|
192
|
-
|
193
|
-
|
194
|
-
|
173
|
+
def process_match(node, parts, params, request, action)
|
174
|
+
env = request.env
|
175
|
+
case action
|
176
|
+
when :call, :call_with_trailing_slash
|
177
|
+
node.value.each do |path|
|
178
|
+
response_struct = Response.new(path, params)
|
179
|
+
previous_params = env['router.params']
|
180
|
+
env['router'] = router
|
181
|
+
env['router.params'] ||= {}
|
182
|
+
env['router.params'].merge!(response_struct.params)
|
183
|
+
env['router.response'] = response_struct
|
184
|
+
env['SCRIPT_NAME'] ||= ''
|
185
|
+
matched = if path.route.partially_match?
|
186
|
+
env['PATH_INFO'] = "#{HttpRouter::Parts::SLASH}#{parts && parts.join(HttpRouter::Parts::SLASH)}"
|
187
|
+
env['SCRIPT_NAME'] += request.path_info[0, request.path_info.size - env['PATH_INFO'].size]
|
188
|
+
true
|
189
|
+
elsif (parts and (action == :call_with_trailing_slash) and (router.ignore_trailing_slash? or (parts.size == 1 and parts.first == ''))) or parts.nil? || parts.empty?
|
190
|
+
env["PATH_INFO"] = ''
|
191
|
+
env["SCRIPT_NAME"] += request.path_info
|
192
|
+
true
|
193
|
+
else
|
194
|
+
false
|
195
|
+
end
|
196
|
+
if matched
|
197
|
+
response = path.route.dest.call(env)
|
198
|
+
env['router.last_repsonse'] = response
|
199
|
+
if response.first != 404 and response.first != 410
|
200
|
+
throw :response, response
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end if node.value
|
204
|
+
when :nocall, :nocall_with_trailing_slash
|
205
|
+
throw :response, node.value.map{|path| Response.new(path, params)}
|
195
206
|
else
|
196
|
-
|
207
|
+
raise
|
197
208
|
end
|
198
209
|
end
|
199
210
|
|
@@ -208,15 +219,17 @@ class HttpRouter
|
|
208
219
|
end
|
209
220
|
|
210
221
|
class ArbitraryNode < Node
|
211
|
-
def find_on_arbitrary(request, parts,
|
222
|
+
def find_on_arbitrary(request, parts, action, params)
|
212
223
|
next_node = @linear && !@linear.empty? && @linear.find { |(procs, node)|
|
213
|
-
params_hash = node.value.hashify_params(params)
|
214
|
-
procs.all?{|p| p.call(request, params_hash
|
224
|
+
params_hash = node.value ? node.value.first.hashify_params(params) : {}
|
225
|
+
procs.all?{|p| p.call(request, params_hash)}
|
215
226
|
}
|
216
227
|
if next_node
|
217
|
-
process_match(next_node.last, parts, params,
|
228
|
+
process_match(next_node.last, parts, params, request, action)
|
218
229
|
elsif @catchall
|
219
|
-
process_match(@catchall, parts, params,
|
230
|
+
process_match(@catchall, parts, params, request, action)
|
231
|
+
elsif @value
|
232
|
+
process_match(self, parts, params, request, action)
|
220
233
|
end
|
221
234
|
end
|
222
235
|
end
|
@@ -224,32 +237,20 @@ class HttpRouter
|
|
224
237
|
class RequestNode < Node
|
225
238
|
RequestMethods = [:request_method, :host, :port, :scheme, :user_agent, :ip, :fullpath, :query_string].freeze
|
226
239
|
attr_accessor :request_method
|
227
|
-
def find_on_request_methods(request, parts,
|
228
|
-
|
240
|
+
def find_on_request_methods(request, parts, action, params)
|
241
|
+
if @request_method
|
229
242
|
request_value = request.send(request_method)
|
230
|
-
|
231
|
-
|
232
|
-
catchall_node(request, parts, params, request_value, routes)
|
233
|
-
end
|
234
|
-
if next_node
|
235
|
-
process_match(next_node, parts, params, routes)
|
236
|
-
else
|
237
|
-
find_on_parts(request, parts, params, routes)
|
238
|
-
end
|
239
|
-
end
|
240
|
-
private
|
241
|
-
def linear_node(request, parts, params, request_value, routes)
|
242
|
-
if @linear && !@linear.empty?
|
243
|
-
node = @linear.find { |(regexp, node)| regexp === request_value }
|
244
|
-
node.last.find_on_request_methods(request, parts, params, routes) if node
|
243
|
+
if @linear && !@linear.empty? && match = @linear.find { |(regexp, node)| regexp === request_value }
|
244
|
+
match.last.find_on_request_methods(request, parts, action, params)
|
245
245
|
end
|
246
|
+
@lookup[request_value].find_on_request_methods(request, parts, action, params) if @lookup and @lookup[request_value]
|
247
|
+
@catchall.find_on_request_methods(request, parts, action, params) if @catchall
|
246
248
|
end
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
@catchall.find_on_request_methods(request, parts, params, routes) if @catchall
|
249
|
+
if @value
|
250
|
+
process_match(self, parts, params, request, action)
|
251
|
+
elsif arbitrary_node
|
252
|
+
arbitrary_node.find_on_arbitrary(request, parts, action, params)
|
252
253
|
end
|
254
|
+
end
|
253
255
|
end
|
254
|
-
|
255
256
|
end
|
@@ -7,11 +7,20 @@ class HttpRouter
|
|
7
7
|
@paths = [""]
|
8
8
|
@chars = path.split('')
|
9
9
|
while !@chars.empty?
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
case @chars.first[0]
|
11
|
+
when ?(
|
12
|
+
@chars.shift and double_paths
|
13
|
+
when ?)
|
14
|
+
@chars.shift and half_paths
|
15
|
+
when ?\\
|
16
|
+
if @chars[1] == ?( || @chars[1] == ?)
|
17
|
+
@chars.shift
|
18
|
+
else
|
19
|
+
add_to_current_set(@chars.shift)
|
20
|
+
end
|
21
|
+
add_to_current_set(@chars.shift)
|
22
|
+
else
|
23
|
+
add_to_current_set(@chars.shift)
|
15
24
|
end
|
16
25
|
end
|
17
26
|
@paths
|
data/lib/http_router/path.rb
CHANGED
@@ -3,30 +3,36 @@ class HttpRouter
|
|
3
3
|
attr_reader :parts, :route, :splitting_indexes, :path
|
4
4
|
def initialize(route, path, parts, splitting_indexes)
|
5
5
|
@route, @path, @parts, @splitting_indexes = route, path, parts, splitting_indexes
|
6
|
-
|
7
6
|
duplicate_variable_names = variable_names.dup.uniq!
|
8
7
|
raise AmbiguousVariableException, "You have duplicate variable name present: #{duplicate_variable_names.join(', ')}" if duplicate_variable_names
|
9
|
-
|
10
|
-
@path_validation_regex =
|
11
|
-
|
8
|
+
regex_parts = path.split(/([:\*][a-zA-Z0-9_]+)/)
|
9
|
+
@path_validation_regex, code = '', ''
|
10
|
+
regex_parts.each_with_index{ |part, index|
|
11
|
+
new_part = case part[0]
|
12
12
|
when ?:, ?*
|
13
|
-
|
13
|
+
if index != 0 && regex_parts[index - 1][-1] == ?\\
|
14
|
+
@path_validation_regex << Regexp.quote(part)
|
15
|
+
code << part
|
16
|
+
else
|
17
|
+
@path_validation_regex << (route.matches_with[part[1, part.size].to_sym] || '.*?').to_s
|
18
|
+
code << "\#{args.shift || (options && options.delete(:#{part[1, part.size]})) || raise(MissingParameterException, \"missing parameter :#{part[1, part.size]}\")}"
|
19
|
+
end
|
14
20
|
else
|
15
|
-
Regexp.quote(part)
|
21
|
+
@path_validation_regex << Regexp.quote(part)
|
22
|
+
code << part
|
16
23
|
end
|
17
|
-
|
24
|
+
new_part
|
25
|
+
}
|
18
26
|
@path_validation_regex = Regexp.new("^#{@path_validation_regex}$")
|
19
|
-
|
20
|
-
eval_path = path.gsub(/[:\*]([a-zA-Z0-9_]+)/) {"\#{args.shift || (options && options.delete(:#{$1})) || raise(MissingParameterException, \"missing parameter #{$1}\")}" }
|
21
27
|
instance_eval "
|
22
28
|
def raw_url(args,options)
|
23
|
-
\"#{
|
29
|
+
\"#{code}\"
|
24
30
|
end
|
25
31
|
"
|
26
32
|
end
|
27
33
|
|
28
34
|
def hashify_params(params)
|
29
|
-
variable_names.zip(params).inject({}) { |h, (k,v)| h[k] = v; h }
|
35
|
+
variable_names && params ? variable_names.zip(params).inject({}) { |h, (k,v)| h[k] = v; h } : {}
|
30
36
|
end
|
31
37
|
|
32
38
|
def ===(other_path)
|
@@ -50,23 +56,7 @@ class HttpRouter
|
|
50
56
|
raise InvalidRouteException if path !~ @path_validation_regex
|
51
57
|
raise TooManyParametersException unless args.empty?
|
52
58
|
HttpRouter.uri_escape!(path)
|
53
|
-
|
54
|
-
path
|
55
|
-
end
|
56
|
-
|
57
|
-
def generate_querystring(uri, params)
|
58
|
-
if params && !params.empty?
|
59
|
-
uri_size = uri.size
|
60
|
-
params.each do |k,v|
|
61
|
-
case v
|
62
|
-
when Array
|
63
|
-
v.each { |v_part| uri << '&' << ::Rack::Utils.escape(k.to_s) << '%5B%5D=' << ::Rack::Utils.escape(v_part.to_s) }
|
64
|
-
else
|
65
|
-
uri << '&' << ::Rack::Utils.escape(k.to_s) << '=' << ::Rack::Utils.escape(v.to_s)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
uri[uri_size] = ??
|
69
|
-
end
|
59
|
+
[path, options]
|
70
60
|
end
|
71
61
|
|
72
62
|
def static?
|
data/lib/http_router/root.rb
CHANGED
@@ -9,40 +9,28 @@ class HttpRouter
|
|
9
9
|
node
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
node, parts, params = catch(:match) { find_on_parts(request, get_parts(request), [], routes) }
|
15
|
-
if !routes.empty?
|
16
|
-
routes.map { |node, parts, params| process_response(node, parts, params, request) }
|
17
|
-
elsif node
|
18
|
-
process_response(node, parts, params, request)
|
19
|
-
elsif !router.request_methods_specified.empty?
|
20
|
-
alternate_methods = (router.request_methods_specified - [request.request_method]).select do |alternate_method|
|
21
|
-
test_request = request.dup
|
22
|
-
test_request.env['REQUEST_METHOD'] = alternate_method
|
23
|
-
routes = []
|
24
|
-
catch(:match) { find_on_parts(test_request, get_parts(request), [], routes) } || !routes.empty?
|
25
|
-
end
|
26
|
-
alternate_methods.empty? ? nil : Response.unmatched(405, {"Allow" => alternate_methods.join(", ")})
|
27
|
-
else
|
28
|
-
nil
|
29
|
-
end
|
12
|
+
def recognize(request)
|
13
|
+
call(request, :nocall)
|
30
14
|
end
|
31
15
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
parts
|
16
|
+
def call(request, action = :call)
|
17
|
+
request = ::Rack::Request.new(request) if request.is_a?(Hash)
|
18
|
+
catch(:response) { find_on_parts(request, get_parts(request), action) } || construct_unmatched(request)
|
36
19
|
end
|
37
20
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
rest = '/' << parts.join('/')
|
44
|
-
Response.matched(node.value, params, request.path_info[0, request.path_info.size - rest.size], rest)
|
21
|
+
def construct_unmatched(request)
|
22
|
+
alternate_methods = (router.request_methods_specified - [request.request_method]).select do |alternate_method|
|
23
|
+
test_request = ::Rack::Request.new(request.env.dup)
|
24
|
+
test_request.env['REQUEST_METHOD'] = alternate_method
|
25
|
+
catch(:response) { find_on_parts(test_request, get_parts(request), :nocall) }
|
45
26
|
end
|
27
|
+
alternate_methods.empty? ? nil : ::Rack::Response.new("Method not found", 405, {"Allow" => alternate_methods.join(", ")}).finish
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_parts(request)
|
31
|
+
parts = router.split(request.path_info)
|
32
|
+
parts << '' if request.path_info.size > 1 && request.path_info[-1] == ?/
|
33
|
+
parts
|
46
34
|
end
|
47
35
|
end
|
48
36
|
end
|