http_router 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Rakefile +0 -15
- data/VERSION +1 -1
- data/http_router.gemspec +36 -0
- data/lib/http_router/glob.rb +0 -1
- data/lib/http_router/node.rb +68 -42
- data/lib/http_router/path.rb +4 -9
- data/lib/http_router/response.rb +2 -3
- data/lib/http_router/root.rb +5 -27
- data/lib/http_router/route.rb +24 -33
- data/lib/http_router/variable.rb +3 -8
- data/lib/http_router.rb +4 -0
- data/spec/misc_spec.rb +4 -5
- data/spec/recognize_spec.rb +53 -10
- metadata +23 -5
data/.gitignore
ADDED
data/Rakefile
CHANGED
@@ -1,18 +1,3 @@
|
|
1
|
-
begin
|
2
|
-
require 'jeweler'
|
3
|
-
Jeweler::Tasks.new do |s|
|
4
|
-
s.name = "http_router"
|
5
|
-
s.description = s.summary = "A kick-ass HTTP router for use in Rack & Sinatra"
|
6
|
-
s.email = "joshbuddy@gmail.com"
|
7
|
-
s.homepage = "http://github.com/joshbuddy/http_router"
|
8
|
-
s.authors = ["Joshua Hull"]
|
9
|
-
s.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
|
10
|
-
end
|
11
|
-
Jeweler::GemcutterTasks.new
|
12
|
-
rescue LoadError
|
13
|
-
puts "Jeweler not available. Install it with: gem install jeweler"
|
14
|
-
end
|
15
|
-
|
16
1
|
require 'spec'
|
17
2
|
require 'spec/rake/spectask'
|
18
3
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/http_router.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{http_router}
|
5
|
+
s.version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).strip
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Joshua Hull"]
|
9
|
+
s.date = %q{2010-05-30}
|
10
|
+
s.description = %q{A kick-ass HTTP router for use in Rack & Sinatra}
|
11
|
+
s.email = %q{joshbuddy@gmail.com}
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"README.rdoc"
|
14
|
+
]
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.homepage = %q{http://github.com/joshbuddy/http_router}
|
17
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubygems_version = %q{1.3.7}
|
20
|
+
s.summary = %q{A kick-ass HTTP router for use in Rack & Sinatra}
|
21
|
+
s.test_files = `git ls-files spec`.split("\n")
|
22
|
+
|
23
|
+
# dependencies
|
24
|
+
s.add_dependency "rack", ">= 1.0.0"
|
25
|
+
|
26
|
+
if s.respond_to? :specification_version then
|
27
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
28
|
+
s.specification_version = 3
|
29
|
+
|
30
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
31
|
+
else
|
32
|
+
end
|
33
|
+
else
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
data/lib/http_router/glob.rb
CHANGED
data/lib/http_router/node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Node
|
3
3
|
attr_accessor :value, :variable, :catchall
|
4
|
-
attr_reader :linear, :lookup, :request_node, :
|
4
|
+
attr_reader :linear, :lookup, :request_node, :arbitrary_node
|
5
5
|
|
6
6
|
def initialize(base)
|
7
7
|
@router = base
|
@@ -36,11 +36,6 @@ class HttpRouter
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def add_extension(ext)
|
40
|
-
@extension_node ||= router.node
|
41
|
-
@extension_node.add(ext)
|
42
|
-
end
|
43
|
-
|
44
39
|
def add_request_methods(options)
|
45
40
|
if !options.empty?
|
46
41
|
generate_request_method_tree(options)
|
@@ -55,6 +50,24 @@ class HttpRouter
|
|
55
50
|
end
|
56
51
|
end
|
57
52
|
|
53
|
+
def add_arbitrary(procs)
|
54
|
+
target = self
|
55
|
+
if procs && !procs.empty?
|
56
|
+
@arbitrary_node ||= router.arbitrary_node
|
57
|
+
@arbitrary_node.create_linear
|
58
|
+
target = router.node
|
59
|
+
@arbitrary_node.linear << [procs, target]
|
60
|
+
if @value
|
61
|
+
@arbitrary_node.catchall = router.node
|
62
|
+
@arbitrary_node.catchall.value = @value
|
63
|
+
@value = nil
|
64
|
+
end
|
65
|
+
elsif @arbitrary_node
|
66
|
+
target = @arbitrary_node.catchall = router.node
|
67
|
+
end
|
68
|
+
target
|
69
|
+
end
|
70
|
+
|
58
71
|
protected
|
59
72
|
|
60
73
|
attr_reader :router
|
@@ -120,45 +133,44 @@ class HttpRouter
|
|
120
133
|
current_nodes
|
121
134
|
end
|
122
135
|
|
123
|
-
def find_on_parts(request, parts,
|
124
|
-
if
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
params << new_params
|
137
|
-
node
|
138
|
-
else
|
139
|
-
nil
|
140
|
-
end
|
136
|
+
def find_on_parts(request, parts, params)
|
137
|
+
if @linear && !@linear.empty?
|
138
|
+
whole_path = parts.join('/')
|
139
|
+
next_node = @linear.find do |(tester, node)|
|
140
|
+
if tester.is_a?(Regexp) and match = tester.match(whole_path) #and match.index == 0 TODO
|
141
|
+
whole_path.slice!(0,match[0].size)
|
142
|
+
parts.replace(router.split(whole_path))
|
143
|
+
node
|
144
|
+
elsif tester.respond_to?(:matches) and new_params = tester.matches(request.env, parts, whole_path)
|
145
|
+
params << new_params
|
146
|
+
node
|
147
|
+
else
|
148
|
+
nil
|
141
149
|
end
|
142
|
-
return next_node.last.find_on_parts(request, parts, extension, params) if next_node
|
143
150
|
end
|
144
|
-
if match =
|
145
|
-
|
146
|
-
match.find_on_parts(request, parts, extension, params)
|
147
|
-
elsif @catchall
|
148
|
-
params << @catchall.variable.matches(request.env, parts, whole_path)
|
149
|
-
parts.shift
|
150
|
-
@catchall.find_on_parts(request, parts, extension, params)
|
151
|
-
elsif parts.size == 1 && parts.first == '' && (value && value.route.trailing_slash_ignore?)
|
152
|
-
parts.shift
|
153
|
-
find_on_parts(request, parts, extension, params)
|
154
|
-
elsif request_node
|
155
|
-
request_node.find_on_request_methods(request)
|
156
|
-
elsif @value
|
157
|
-
self
|
158
|
-
else
|
159
|
-
nil
|
151
|
+
if next_node and match = next_node.last.find_on_parts(request, parts, params)
|
152
|
+
return match
|
160
153
|
end
|
161
154
|
end
|
155
|
+
if match = @lookup && @lookup[parts.first]
|
156
|
+
parts.shift
|
157
|
+
match.find_on_parts(request, parts, params)
|
158
|
+
elsif @catchall
|
159
|
+
params << @catchall.variable.matches(request.env, parts, whole_path)
|
160
|
+
parts.shift
|
161
|
+
@catchall.find_on_parts(request, parts, params)
|
162
|
+
elsif parts.size == 1 && parts.first == '' && (value && value.route.trailing_slash_ignore? || router.ignore_trailing_slash?)
|
163
|
+
parts.shift
|
164
|
+
find_on_parts(request, parts, params)
|
165
|
+
elsif request_node
|
166
|
+
request_node.find_on_request_methods(request)
|
167
|
+
elsif arbitrary_node
|
168
|
+
arbitrary_node.find_on_arbitrary(request)
|
169
|
+
elsif @value
|
170
|
+
self
|
171
|
+
else
|
172
|
+
nil
|
173
|
+
end
|
162
174
|
end
|
163
175
|
|
164
176
|
def create_linear
|
@@ -169,6 +181,18 @@ class HttpRouter
|
|
169
181
|
@lookup ||= {}
|
170
182
|
end
|
171
183
|
end
|
184
|
+
|
185
|
+
class ArbitraryNode < Node
|
186
|
+
def find_on_arbitrary(request)
|
187
|
+
if @linear && !@linear.empty?
|
188
|
+
next_node = @linear.find do |(procs, node)|
|
189
|
+
procs.all?{|p| p.call(request)}
|
190
|
+
end
|
191
|
+
return next_node.last if next_node
|
192
|
+
end
|
193
|
+
@catchall
|
194
|
+
end
|
195
|
+
end
|
172
196
|
|
173
197
|
class RequestNode < Node
|
174
198
|
RequestMethods = [:request_method, :host, :port, :scheme]
|
@@ -191,7 +215,9 @@ class HttpRouter
|
|
191
215
|
end
|
192
216
|
end
|
193
217
|
|
194
|
-
if @
|
218
|
+
if @arbitrary_node
|
219
|
+
@arbitrary_node.find_on_arbitrary(request)
|
220
|
+
elsif @value
|
195
221
|
self
|
196
222
|
else
|
197
223
|
current_node = request_method == :request_method ? Response.unmatched(405, {"Allow" => @lookup.keys.join(", ")}) : nil
|
data/lib/http_router/path.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'cgi'
|
2
2
|
class HttpRouter
|
3
3
|
class Path
|
4
|
-
attr_reader :parts
|
4
|
+
attr_reader :parts
|
5
5
|
attr_accessor :route
|
6
|
-
def initialize(path, parts
|
7
|
-
@path, @parts
|
6
|
+
def initialize(path, parts)
|
7
|
+
@path, @parts = path, parts
|
8
8
|
if duplicate_variable_names = variable_names.dup.uniq!
|
9
9
|
raise AmbiguousVariableException.new("You have duplicate variable name present: #{duplicate_variable_names.join(', ')}")
|
10
10
|
end
|
@@ -22,7 +22,7 @@ class HttpRouter
|
|
22
22
|
@parts.each_with_index {|p,i|
|
23
23
|
return unless compare_parts(p, other_path.parts[i])
|
24
24
|
}
|
25
|
-
|
25
|
+
true
|
26
26
|
end
|
27
27
|
|
28
28
|
def compare_parts(p1, p2)
|
@@ -60,7 +60,6 @@ class HttpRouter
|
|
60
60
|
def variables
|
61
61
|
unless @variables
|
62
62
|
@variables = @parts.select{|p| p.is_a?(Variable)}
|
63
|
-
@variables << @extension if @extension.is_a?(Variable)
|
64
63
|
end
|
65
64
|
@variables
|
66
65
|
end
|
@@ -68,9 +67,5 @@ class HttpRouter
|
|
68
67
|
def variable_names
|
69
68
|
@variable_names ||= variables.map{|v| v.name}
|
70
69
|
end
|
71
|
-
|
72
|
-
def matches_extension?(extension)
|
73
|
-
@extension.nil? || @extension === (extension)
|
74
|
-
end
|
75
70
|
end
|
76
71
|
end
|
data/lib/http_router/response.rb
CHANGED
@@ -15,14 +15,13 @@ class HttpRouter
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
class Matched < Struct.new(:path, :params, :
|
18
|
+
class Matched < Struct.new(:path, :params, :matched_path, :remaining_path)
|
19
19
|
attr_reader :params_as_hash, :route
|
20
20
|
|
21
|
-
def initialize(path, params,
|
21
|
+
def initialize(path, params, matched_path, remaining_path = nil)
|
22
22
|
raise if matched_path.nil?
|
23
23
|
super
|
24
24
|
@params_as_hash = path.variable_names.zip(params).inject({}) {|h, (k,v)| h[k] = v; h }
|
25
|
-
@params_as_hash[path.extension.name] = extension if path.extension && path.extension.is_a?(Variable)
|
26
25
|
end
|
27
26
|
|
28
27
|
def matched?
|
data/lib/http_router/root.rb
CHANGED
@@ -2,38 +2,32 @@ class HttpRouter
|
|
2
2
|
class Root < Node
|
3
3
|
def add_path(path)
|
4
4
|
node = path.parts.inject(self) { |node, part| node.add(part) }
|
5
|
-
if path.extension
|
6
|
-
node = node.add_extension(path.extension)
|
7
|
-
end
|
8
5
|
node
|
9
6
|
end
|
10
7
|
|
11
8
|
def find(request)
|
12
9
|
path = request.path_info.dup
|
13
|
-
path.slice!(-1) if router.ignore_trailing_slash? && path[-1] == ?/
|
14
|
-
extension = extract_extension(path)
|
15
10
|
parts = router.split(path)
|
16
11
|
parts << '' if path[path.size - 1] == ?/
|
17
12
|
params = []
|
18
13
|
process_response(
|
19
|
-
find_on_parts(request, parts,
|
14
|
+
find_on_parts(request, parts, params),
|
20
15
|
parts,
|
21
|
-
extension,
|
22
16
|
params,
|
23
17
|
request
|
24
18
|
)
|
25
19
|
end
|
26
20
|
|
27
21
|
private
|
28
|
-
def process_response(node, parts,
|
22
|
+
def process_response(node, parts, params, request)
|
29
23
|
if node.respond_to?(:matched?) && !node.matched?
|
30
24
|
node
|
31
25
|
elsif node && node.value
|
32
26
|
if parts.empty?
|
33
|
-
|
27
|
+
Response.matched(node.value, params, request.path_info)
|
34
28
|
elsif node.value.route.partially_match?
|
35
|
-
rest = '/' << parts.join('/')
|
36
|
-
|
29
|
+
rest = '/' << parts.join('/')
|
30
|
+
Response.matched(node.value, params, request.path_info[0, request.path_info.size - rest.size], rest)
|
37
31
|
else
|
38
32
|
nil
|
39
33
|
end
|
@@ -41,21 +35,5 @@ class HttpRouter
|
|
41
35
|
nil
|
42
36
|
end
|
43
37
|
end
|
44
|
-
|
45
|
-
def extract_extension(path)
|
46
|
-
if path.gsub!(/\.([^\/\.]+)$/, '')
|
47
|
-
extension = $1
|
48
|
-
else
|
49
|
-
nil
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def post_match(path, params, extension, matched_path, remaining_path = nil)
|
54
|
-
if path.route.partially_match? || path.matches_extension?(extension)
|
55
|
-
Response.matched(path, params, extension, matched_path, remaining_path)
|
56
|
-
else
|
57
|
-
nil
|
58
|
-
end
|
59
|
-
end
|
60
38
|
end
|
61
39
|
end
|
data/lib/http_router/route.rb
CHANGED
@@ -9,9 +9,8 @@ class HttpRouter
|
|
9
9
|
@original_path = path.dup
|
10
10
|
@partially_match = extract_partial_match(path)
|
11
11
|
@trailing_slash_ignore = extract_trailing_slash(path)
|
12
|
-
@variable_store = {}
|
13
12
|
@matches_with = {}
|
14
|
-
@
|
13
|
+
@arbitrary = []
|
15
14
|
@conditions = {}
|
16
15
|
@default_values = {}
|
17
16
|
end
|
@@ -96,13 +95,9 @@ class HttpRouter
|
|
96
95
|
match.each do |var_name, matchers|
|
97
96
|
matchers = Array(matchers)
|
98
97
|
matchers.each do |m|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
@matches_with.key?(var_name) ?
|
103
|
-
raise :
|
104
|
-
@matches_with[var_name] = m
|
105
|
-
end
|
98
|
+
@matches_with.key?(var_name) ?
|
99
|
+
raise :
|
100
|
+
@matches_with[var_name] = m
|
106
101
|
end
|
107
102
|
end
|
108
103
|
self
|
@@ -122,6 +117,11 @@ class HttpRouter
|
|
122
117
|
@partially_match = match
|
123
118
|
self
|
124
119
|
end
|
120
|
+
|
121
|
+
def arbitrary(proc = nil, &block)
|
122
|
+
@arbitrary << (proc || block)
|
123
|
+
self
|
124
|
+
end
|
125
125
|
|
126
126
|
def compiled?
|
127
127
|
!@paths.nil?
|
@@ -139,6 +139,7 @@ class HttpRouter
|
|
139
139
|
path.route = self
|
140
140
|
current_node = router.root.add_path(path)
|
141
141
|
working_set = current_node.add_request_methods(@conditions)
|
142
|
+
working_set.map!{|node| node.add_arbitrary(@arbitrary)}
|
142
143
|
working_set.each do |current_node|
|
143
144
|
current_node.value = path
|
144
145
|
end
|
@@ -226,18 +227,6 @@ class HttpRouter
|
|
226
227
|
path[-2, 2] == '/?' && path.slice!(-2, 2)
|
227
228
|
end
|
228
229
|
|
229
|
-
def extract_extension(path)
|
230
|
-
if match = path.match(/^(.*)(\.:([a-zA-Z_]+))$/)
|
231
|
-
path.replace(match[1])
|
232
|
-
v_name = match[3].to_sym
|
233
|
-
router.variable(match[3].to_sym, @matches_with[v_name], @additional_matchers && @additional_matchers[v_name])
|
234
|
-
elsif match = path.match(/^(.*)(\.([a-zA-Z_]+))$/)
|
235
|
-
path.replace(match[1])
|
236
|
-
match[3]
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
|
241
230
|
def compile_optionals(path)
|
242
231
|
start_index = 0
|
243
232
|
end_index = 1
|
@@ -269,26 +258,28 @@ class HttpRouter
|
|
269
258
|
paths = compile_optionals(@path)
|
270
259
|
paths.map do |path|
|
271
260
|
original_path = path.dup
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
v_name
|
280
|
-
|
261
|
+
index = -1
|
262
|
+
split_path = router.split(path)
|
263
|
+
new_path = split_path.map do |part|
|
264
|
+
index += 1
|
265
|
+
case part
|
266
|
+
when /^:([a-zA-Z_0-9]+)$/
|
267
|
+
v_name = $1.to_sym
|
268
|
+
router.variable(v_name, @matches_with[v_name])
|
269
|
+
when /^\*([a-zA-Z_0-9]+)$/
|
270
|
+
v_name = $1.to_sym
|
271
|
+
router.glob(v_name, @matches_with[v_name])
|
281
272
|
else
|
282
273
|
generate_interstitial_parts(part)
|
283
274
|
end
|
284
275
|
end
|
285
276
|
new_path.flatten!
|
286
|
-
Path.new(original_path, new_path
|
277
|
+
Path.new(original_path, new_path)
|
287
278
|
end
|
288
279
|
end
|
289
280
|
|
290
281
|
def generate_interstitial_parts(part)
|
291
|
-
part_segments = part.
|
282
|
+
part_segments = part.scan(/:[a-zA-Z_0-9]+|[^:]+/)
|
292
283
|
if part_segments.size > 1
|
293
284
|
index = 0
|
294
285
|
part_segments.map do |seg|
|
@@ -301,7 +292,7 @@ class HttpRouter
|
|
301
292
|
else
|
302
293
|
/^#{matcher || '.*?'}(?=#{Regexp.quote(part_segments[next_index])})/
|
303
294
|
end
|
304
|
-
|
295
|
+
router.variable(v_name, scan_regex)
|
305
296
|
else
|
306
297
|
/^#{Regexp.quote(seg)}/
|
307
298
|
end
|
data/lib/http_router/variable.rb
CHANGED
@@ -2,27 +2,22 @@ class HttpRouter
|
|
2
2
|
class Variable
|
3
3
|
attr_reader :name, :matches_with
|
4
4
|
|
5
|
-
def initialize(base, name, matches_with = nil
|
5
|
+
def initialize(base, name, matches_with = nil)
|
6
6
|
@router = base
|
7
7
|
@name = name
|
8
8
|
@matches_with = matches_with
|
9
|
-
@additional_matchers = additional_matchers
|
10
9
|
end
|
11
10
|
|
12
11
|
def matches(env, parts, whole_path)
|
13
12
|
if @matches_with.nil?
|
14
|
-
|
15
|
-
elsif @matches_with and match = @matches_with.match(whole_path)
|
13
|
+
parts.first
|
14
|
+
elsif @matches_with and match = @matches_with.match(whole_path)
|
16
15
|
whole_path.slice!(0, match[0].size)
|
17
16
|
parts.replace(router.split(whole_path))
|
18
17
|
match[0]
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
22
|
-
def additional_matchers(env, test)
|
23
|
-
@additional_matchers.nil? || @additional_matchers.all?{|m| m.call(env, test)}
|
24
|
-
end
|
25
|
-
|
26
21
|
def ===(part)
|
27
22
|
@matches_with.nil?
|
28
23
|
end
|
data/lib/http_router.rb
CHANGED
data/spec/misc_spec.rb
CHANGED
@@ -15,12 +15,11 @@ describe "HttpRouter" do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
context "instance_eval block" do
|
18
|
-
HttpRouter.new {
|
19
|
-
|
20
|
-
}.recognize(Rack::MockRequest.env_for('/test', :method => 'GET')).dest.should == :test
|
21
|
-
|
18
|
+
#HttpRouter.new {
|
19
|
+
# add('/test').to :test
|
20
|
+
#}.recognize(Rack::MockRequest.env_for('/test', :method => 'GET')).dest.should == :test
|
22
21
|
end
|
23
|
-
|
22
|
+
|
24
23
|
context "exceptions" do
|
25
24
|
it "should be smart about multiple optionals" do
|
26
25
|
proc {@router.add("/:var1(/:var2)(/:var3)").compile}.should raise_error(HttpRouter::AmbiguousRouteException)
|
data/spec/recognize_spec.rb
CHANGED
@@ -31,11 +31,37 @@ describe "HttpRouter#recognize" do
|
|
31
31
|
|
32
32
|
context("proc acceptance") do
|
33
33
|
it "should match optionally with a proc" do
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
response.
|
34
|
+
@router.add("/test").arbitrary(Proc.new{|req| req.host == 'hellodooly' }).to(:test1)
|
35
|
+
@router.add("/test").arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 80}.to(:test2)
|
36
|
+
@router.add("/test").arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 8080}.to(:test3)
|
37
|
+
response = @router.recognize(Rack::MockRequest.env_for('http://lovelove:8080/test'))
|
38
|
+
response.dest.should == :test3
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should still use an existing less specific node if possible" do
|
42
|
+
@router.add("/test").to(:test4)
|
43
|
+
@router.add("/test").arbitrary(Proc.new{|req| req.host == 'hellodooly' }).to(:test1)
|
44
|
+
@router.add("/test").arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 80}.to(:test2)
|
45
|
+
@router.add("/test").arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 8080}.to(:test3)
|
46
|
+
response = @router.recognize(Rack::MockRequest.env_for('http://lovelove:8081/test'))
|
47
|
+
response.dest.should == :test4
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should match optionally with a proc and request conditions" do
|
51
|
+
@router.add("/test").get.arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 80}.to(:test1)
|
52
|
+
@router.add("/test").get.arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 8080}.to(:test2)
|
53
|
+
response = @router.recognize(Rack::MockRequest.env_for('http://lovelove:8080/test'))
|
54
|
+
response.dest.should == :test2
|
38
55
|
end
|
56
|
+
|
57
|
+
it "should still use an existing less specific node if possible with request conditions" do
|
58
|
+
@router.add("/test").get.arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 80}.to(:test1)
|
59
|
+
@router.add("/test").get.arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 8080}.to(:test2)
|
60
|
+
@router.add("/test").get.to(:test3)
|
61
|
+
response = @router.recognize(Rack::MockRequest.env_for('http://lovelove:8081/test'))
|
62
|
+
response.dest.should == :test3
|
63
|
+
end
|
64
|
+
|
39
65
|
end
|
40
66
|
|
41
67
|
context("trailing slashes") do
|
@@ -55,6 +81,10 @@ describe "HttpRouter#recognize" do
|
|
55
81
|
route = @router.add("/test").to(:test)
|
56
82
|
@router.recognize(Rack::MockRequest.env_for('/test/')).should be_nil
|
57
83
|
end
|
84
|
+
it "should not capture the trailing slash in a variable normally" do
|
85
|
+
route = @router.add("/:test").to(:test)
|
86
|
+
@router.recognize(Rack::MockRequest.env_for('/test/')).params.first.should == 'test'
|
87
|
+
end
|
58
88
|
end
|
59
89
|
|
60
90
|
end
|
@@ -110,7 +140,6 @@ describe "HttpRouter#recognize" do
|
|
110
140
|
route = @router.add('/test.:format').to(:test)
|
111
141
|
response = @router.recognize(Rack::MockRequest.env_for('/test.html'))
|
112
142
|
response.route.should == route
|
113
|
-
response.extension.should == 'html'
|
114
143
|
response.params_as_hash[:format].should == 'html'
|
115
144
|
@router.recognize(Rack::MockRequest.env_for('/test')).should be_nil
|
116
145
|
end
|
@@ -119,11 +148,9 @@ describe "HttpRouter#recognize" do
|
|
119
148
|
route = @router.add('/test(.:format)').to(:test)
|
120
149
|
response = @router.recognize(Rack::MockRequest.env_for('/test.html'))
|
121
150
|
response.route.should == route
|
122
|
-
response.extension.should == 'html'
|
123
151
|
response.params_as_hash[:format].should == 'html'
|
124
152
|
response = @router.recognize(Rack::MockRequest.env_for('/test'))
|
125
153
|
response.route.should == route
|
126
|
-
response.extension.should be_nil
|
127
154
|
response.params_as_hash[:format].should be_nil
|
128
155
|
end
|
129
156
|
|
@@ -131,7 +158,6 @@ describe "HttpRouter#recognize" do
|
|
131
158
|
route = @router.add('/:test.:format').to(:test)
|
132
159
|
response = @router.recognize(Rack::MockRequest.env_for('/hey.html'))
|
133
160
|
response.route.should == route
|
134
|
-
response.extension.should == 'html'
|
135
161
|
response.params_as_hash[:format].should == 'html'
|
136
162
|
response.params_as_hash[:test].should == 'hey'
|
137
163
|
end
|
@@ -140,12 +166,10 @@ describe "HttpRouter#recognize" do
|
|
140
166
|
route = @router.add('/:test(.:format)').to(:test)
|
141
167
|
response = @router.recognize(Rack::MockRequest.env_for('/hey.html'))
|
142
168
|
response.route.should == route
|
143
|
-
response.extension.should == 'html'
|
144
169
|
response.params_as_hash[:format].should == 'html'
|
145
170
|
response.params_as_hash[:test].should == 'hey'
|
146
171
|
response = @router.recognize(Rack::MockRequest.env_for('/hey'))
|
147
172
|
response.route.should == route
|
148
|
-
response.extension.should be_nil
|
149
173
|
response.params_as_hash[:format].should be_nil
|
150
174
|
response.params_as_hash[:test].should == 'hey'
|
151
175
|
end
|
@@ -184,6 +208,14 @@ describe "HttpRouter#recognize" do
|
|
184
208
|
response.route.should == route
|
185
209
|
response.params_as_hash[:variable].should == '123'
|
186
210
|
end
|
211
|
+
|
212
|
+
it "should recognize interstitial variable when there is an extension" do
|
213
|
+
route = @router.add('/hey.:greed.html').to(:test)
|
214
|
+
response = @router.recognize(Rack::MockRequest.env_for('/hey.greedyboy.html'))
|
215
|
+
response.route.should == route
|
216
|
+
response.params_as_hash[:greed].should == 'greedyboy'
|
217
|
+
end
|
218
|
+
|
187
219
|
end
|
188
220
|
|
189
221
|
context("dynamic greedy paths") do
|
@@ -196,5 +228,16 @@ describe "HttpRouter#recognize" do
|
|
196
228
|
response = @router.recognize(Rack::MockRequest.env_for('/asd'))
|
197
229
|
response.should be_nil
|
198
230
|
end
|
231
|
+
|
232
|
+
it "should capture the trailing slash in a greedy variable" do
|
233
|
+
route = @router.add("/:test").matching(:test => /.*/).to(:test)
|
234
|
+
@router.recognize(Rack::MockRequest.env_for('/test/')).params.first.should == 'test/'
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should capture the extension in a greedy variable" do
|
238
|
+
route = @router.add("/:test").matching(:test => /.*/).to(:test)
|
239
|
+
@router.recognize(Rack::MockRequest.env_for('/test.html')).params.first.should == 'test.html'
|
240
|
+
end
|
241
|
+
|
199
242
|
end
|
200
243
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http_router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joshua Hull
|
@@ -17,8 +17,23 @@ cert_chain: []
|
|
17
17
|
|
18
18
|
date: 2010-05-30 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rack
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 1.0.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
22
37
|
description: A kick-ass HTTP router for use in Rack & Sinatra
|
23
38
|
email: joshbuddy@gmail.com
|
24
39
|
executables: []
|
@@ -28,9 +43,11 @@ extensions: []
|
|
28
43
|
extra_rdoc_files:
|
29
44
|
- README.rdoc
|
30
45
|
files:
|
46
|
+
- .gitignore
|
31
47
|
- README.rdoc
|
32
48
|
- Rakefile
|
33
49
|
- VERSION
|
50
|
+
- http_router.gemspec
|
34
51
|
- lib/ext/rack/rack_mapper.rb
|
35
52
|
- lib/ext/rack/uri_escape.rb
|
36
53
|
- lib/http_router.rb
|
@@ -93,4 +110,5 @@ test_files:
|
|
93
110
|
- spec/rack/route_spec.rb
|
94
111
|
- spec/recognize_spec.rb
|
95
112
|
- spec/sinatra/recognize_spec.rb
|
113
|
+
- spec/spec.opts
|
96
114
|
- spec/spec_helper.rb
|