http_router 0.1.1 → 0.1.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/.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
|