http_router 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +43 -0
- data/Rakefile +1 -4
- data/VERSION +1 -1
- data/http_router.gemspec +2 -1
- data/lib/http_router/glob.rb +4 -4
- data/lib/http_router/node.rb +5 -6
- data/lib/http_router/parts.rb +21 -0
- data/lib/http_router/path.rb +2 -3
- data/lib/http_router/route.rb +27 -15
- data/lib/http_router/variable.rb +4 -5
- data/lib/http_router.rb +24 -11
- data/spec/generate_spec.rb +92 -6
- data/spec/misc_spec.rb +5 -4
- data/spec/mounting_spec.rb +5 -0
- data/spec/rack/dispatch_spec.rb +21 -23
- data/spec/rack/generate_spec.rb +4 -2
- data/spec/rack/middleware_spec.rb +3 -1
- data/spec/rack/route_spec.rb +2 -0
- data/spec/recognize_spec.rb +7 -6
- data/spec/sinatra/recognize_spec.rb +6 -5
- data/spec/spec_helper.rb +6 -2
- metadata +23 -3
data/.gitignore
CHANGED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
dependencies:
|
3
|
+
rake:
|
4
|
+
group:
|
5
|
+
- :development
|
6
|
+
version: ">= 0"
|
7
|
+
url_mount:
|
8
|
+
group:
|
9
|
+
- :default
|
10
|
+
version: ">= 0.2.1"
|
11
|
+
rspec:
|
12
|
+
group:
|
13
|
+
- :development
|
14
|
+
version: ">= 0"
|
15
|
+
rack:
|
16
|
+
group:
|
17
|
+
- :default
|
18
|
+
version: ">= 1.0"
|
19
|
+
sinatra:
|
20
|
+
group:
|
21
|
+
- :development
|
22
|
+
version: ">= 0"
|
23
|
+
rbench:
|
24
|
+
group:
|
25
|
+
- :development
|
26
|
+
version: ">= 0"
|
27
|
+
specs:
|
28
|
+
- rake:
|
29
|
+
version: 0.8.7
|
30
|
+
- rack:
|
31
|
+
version: 1.1.0
|
32
|
+
- rbench:
|
33
|
+
version: 0.2.3
|
34
|
+
- rspec:
|
35
|
+
version: 1.3.0
|
36
|
+
- sinatra:
|
37
|
+
version: "1.0"
|
38
|
+
- url_mount:
|
39
|
+
version: 0.2.1
|
40
|
+
hash: 31358295b6fc19c69280f917c4fa3db0d1a7d48d
|
41
|
+
sources:
|
42
|
+
- Rubygems:
|
43
|
+
uri: http://gemcutter.org
|
data/Rakefile
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
+
require 'rubygems'
|
1
2
|
require 'spec'
|
2
3
|
require 'spec/rake/spectask'
|
3
4
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
4
5
|
t.spec_opts ||= []
|
5
|
-
t.ruby_opts << "-rrubygems"
|
6
|
-
t.ruby_opts << "-Ilib"
|
7
|
-
t.ruby_opts << "-rhttp_router"
|
8
|
-
t.ruby_opts << "-rspec/spec_helper"
|
9
6
|
t.spec_opts << "--options" << "spec/spec.opts"
|
10
7
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
11
8
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.4
|
data/http_router.gemspec
CHANGED
@@ -21,7 +21,8 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.test_files = `git ls-files spec`.split("\n")
|
22
22
|
|
23
23
|
# dependencies
|
24
|
-
s.add_dependency "rack",
|
24
|
+
s.add_dependency "rack", ">= 1.0.0"
|
25
|
+
s.add_dependency "url_mount", ">=0.2"
|
25
26
|
|
26
27
|
if s.respond_to? :specification_version then
|
27
28
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
data/lib/http_router/glob.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Glob < Variable
|
3
|
-
def matches?(parts
|
4
|
-
@matches_with.nil? or (!parts.empty? and match = @matches_with.match(parts.first) and match.begin(0))
|
3
|
+
def matches?(parts)
|
4
|
+
@matches_with.nil? or (!parts.empty? and match = @matches_with.match(parts.first) and match.begin(0)) ? match : nil
|
5
5
|
end
|
6
6
|
|
7
|
-
def consume(
|
7
|
+
def consume(match, parts)
|
8
8
|
if @matches_with
|
9
9
|
params = [parts.shift]
|
10
|
-
params << parts.shift while matches?(parts
|
10
|
+
params << parts.shift while matches?(parts)
|
11
11
|
params
|
12
12
|
else
|
13
13
|
params = parts.dup
|
data/lib/http_router/node.rb
CHANGED
@@ -139,7 +139,6 @@ class HttpRouter
|
|
139
139
|
|
140
140
|
def find_on_parts(request, parts, params)
|
141
141
|
if parts and !parts.empty?
|
142
|
-
whole_path = parts.join('/')
|
143
142
|
if parts.size == 1 and parts.first == ''
|
144
143
|
potential_match = find_on_parts(request, [], params)
|
145
144
|
if potential_match and (router.ignore_trailing_slash? or potential_match.value && potential_match.value.route.trailing_slash_ignore?)
|
@@ -151,12 +150,12 @@ class HttpRouter
|
|
151
150
|
response = nil
|
152
151
|
dupped_parts = nil
|
153
152
|
next_node = @linear.find do |(tester, node)|
|
154
|
-
if tester.respond_to?(:matches?) and tester.matches?(parts
|
153
|
+
if tester.respond_to?(:matches?) and match = tester.matches?(parts)
|
155
154
|
dupped_parts = parts.dup
|
156
|
-
params << tester.consume(
|
155
|
+
params << tester.consume(match, dupped_parts)
|
157
156
|
parts.replace(dupped_parts) if response = node.find_on_parts(request, dupped_parts, params)
|
158
|
-
elsif tester.respond_to?(:match) and match = tester.match(whole_path) and match.begin(0) == 0
|
159
|
-
dupped_parts = router.split(whole_path[match[0].size, whole_path.size])
|
157
|
+
elsif tester.respond_to?(:match) and match = tester.match(parts.whole_path) and match.begin(0) == 0
|
158
|
+
dupped_parts = router.split(parts.whole_path[match[0].size, parts.whole_path.size])
|
160
159
|
parts.replace(dupped_parts) if response = node.find_on_parts(request, dupped_parts, params)
|
161
160
|
else
|
162
161
|
nil
|
@@ -168,7 +167,7 @@ class HttpRouter
|
|
168
167
|
parts.shift
|
169
168
|
return match.find_on_parts(request, parts, params)
|
170
169
|
elsif @catchall
|
171
|
-
params << @catchall.variable.consume(
|
170
|
+
params << @catchall.variable.consume(nil, parts)
|
172
171
|
return @catchall.find_on_parts(request, parts, params)
|
173
172
|
end
|
174
173
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class HttpRouter
|
2
|
+
class Parts < Array
|
3
|
+
def initialize(path)
|
4
|
+
super((path[0] == ?/ ? path[1, path.size] : path).split('/'))
|
5
|
+
end
|
6
|
+
|
7
|
+
def whole_path
|
8
|
+
@whole_path ||= join('/')
|
9
|
+
end
|
10
|
+
|
11
|
+
def shift
|
12
|
+
@whole_path = nil
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def replace(ary)
|
17
|
+
@whole_path = nil
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/http_router/path.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'cgi'
|
2
1
|
class HttpRouter
|
3
2
|
class Path
|
4
3
|
attr_reader :parts, :route
|
@@ -58,9 +57,9 @@ class HttpRouter
|
|
58
57
|
params.each do |k,v|
|
59
58
|
case v
|
60
59
|
when Array
|
61
|
-
v.each { |v_part| uri << '&' <<
|
60
|
+
v.each { |v_part| uri << '&' << Rack::Utils.escape(k.to_s) << '%5B%5D=' << Rack::Utils.escape(v_part.to_s) }
|
62
61
|
else
|
63
|
-
uri << '&' <<
|
62
|
+
uri << '&' << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
|
64
63
|
end
|
65
64
|
end
|
66
65
|
uri[uri_size] = ??
|
data/lib/http_router/route.rb
CHANGED
@@ -30,8 +30,8 @@ class HttpRouter
|
|
30
30
|
end
|
31
31
|
|
32
32
|
# Creates a deep uncompiled copy of this route.
|
33
|
-
def clone
|
34
|
-
Route.new(
|
33
|
+
def clone(new_router)
|
34
|
+
Route.new(new_router, @original_path.dup).with_options(as_options)
|
35
35
|
end
|
36
36
|
|
37
37
|
# Uses an option hash to apply conditions to a Route.
|
@@ -75,27 +75,27 @@ class HttpRouter
|
|
75
75
|
def get
|
76
76
|
request_method('GET')
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
# Causes this route to recognize the POST request method. Returns +self+.
|
80
80
|
def post
|
81
81
|
request_method('POST')
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
# Causes this route to recognize the HEAD request method. Returns +self+.
|
85
85
|
def head
|
86
86
|
request_method('HEAD')
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
# Causes this route to recognize the PUT request method. Returns +self+.
|
90
90
|
def put
|
91
91
|
request_method('PUT')
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
# Causes this route to recognize the DELETE request method. Returns +self+.
|
95
95
|
def delete
|
96
96
|
request_method('DELETE')
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
# Sets a request condition for the route
|
100
100
|
# Returns +self+.
|
101
101
|
#
|
@@ -147,6 +147,11 @@ class HttpRouter
|
|
147
147
|
def to(dest = nil, &block)
|
148
148
|
compile
|
149
149
|
@dest = dest || block
|
150
|
+
if @dest.respond_to?(:url_mount=)
|
151
|
+
urlmount = UrlMount.new(@original_path, @default_values)
|
152
|
+
urlmount.url_mount = router.url_mount if router.url_mount
|
153
|
+
@dest.url_mount = urlmount
|
154
|
+
end
|
150
155
|
self
|
151
156
|
end
|
152
157
|
|
@@ -161,12 +166,12 @@ class HttpRouter
|
|
161
166
|
@arbitrary << (proc || block)
|
162
167
|
self
|
163
168
|
end
|
164
|
-
|
169
|
+
|
165
170
|
# Compile state for route. Returns +true+ or +false+.
|
166
171
|
def compiled?
|
167
172
|
!@paths.nil?
|
168
173
|
end
|
169
|
-
|
174
|
+
|
170
175
|
# Compiles the route and inserts it into the tree. This is called automatically when you add a destination via #to to the route. Until a route
|
171
176
|
# is compiled, it will not be recognized.
|
172
177
|
def compile
|
@@ -189,7 +194,7 @@ class HttpRouter
|
|
189
194
|
end
|
190
195
|
self
|
191
196
|
end
|
192
|
-
|
197
|
+
|
193
198
|
# Sets the destination of this route to redirect to an arbitrary URL.
|
194
199
|
def redirect(path, status = 302)
|
195
200
|
raise(ArgumentError, "Status has to be an integer between 300 and 399") unless (300..399).include?(status)
|
@@ -201,7 +206,7 @@ class HttpRouter
|
|
201
206
|
}
|
202
207
|
self
|
203
208
|
end
|
204
|
-
|
209
|
+
|
205
210
|
# Sets the destination of this route to serve static files from either a directory or a single file.
|
206
211
|
def static(root)
|
207
212
|
if File.directory?(root)
|
@@ -233,11 +238,18 @@ class HttpRouter
|
|
233
238
|
matching_path(args, options)
|
234
239
|
end
|
235
240
|
raise UngeneratableRouteException.new unless path
|
236
|
-
|
241
|
+
|
242
|
+
mount_point = nil
|
243
|
+
if !router.url_mount.nil?
|
244
|
+
mount_point = router.url_mount.url(options)
|
245
|
+
end
|
246
|
+
|
247
|
+
result = path.url(args, options)
|
248
|
+
mount_point.nil? ? result : File.join(mount_point, result)
|
237
249
|
end
|
238
250
|
|
239
251
|
private
|
240
|
-
|
252
|
+
|
241
253
|
attr_reader :router
|
242
254
|
|
243
255
|
def matching_path(params, other_hash = nil)
|
@@ -246,7 +258,7 @@ class HttpRouter
|
|
246
258
|
else
|
247
259
|
if params.is_a?(Array)
|
248
260
|
significant_keys = other_hash && significant_variable_names & other_hash.keys
|
249
|
-
@paths.find { |path|
|
261
|
+
@paths.find { |path|
|
250
262
|
var_count = significant_keys ? params.size + significant_keys.size : params.size
|
251
263
|
path.variables.size == var_count
|
252
264
|
}
|
@@ -262,7 +274,7 @@ class HttpRouter
|
|
262
274
|
end
|
263
275
|
end
|
264
276
|
end
|
265
|
-
|
277
|
+
|
266
278
|
def extract_partial_match(path)
|
267
279
|
path[-1] == ?* && path.slice!(-1)
|
268
280
|
end
|
data/lib/http_router/variable.rb
CHANGED
@@ -8,14 +8,13 @@ class HttpRouter
|
|
8
8
|
@matches_with = matches_with
|
9
9
|
end
|
10
10
|
|
11
|
-
def matches?(parts
|
12
|
-
@matches_with.nil? or (@matches_with and match = @matches_with.match(whole_path) and match.begin(0) == 0)
|
11
|
+
def matches?(parts)
|
12
|
+
@matches_with.nil? or (@matches_with and match = @matches_with.match(parts.whole_path) and match.begin(0) == 0) ? match : nil
|
13
13
|
end
|
14
14
|
|
15
|
-
def consume(
|
15
|
+
def consume(match, parts)
|
16
16
|
if @matches_with
|
17
|
-
match
|
18
|
-
parts.replace(router.split(whole_path[match.end(0), whole_path.size]))
|
17
|
+
parts.replace(router.split(parts.whole_path[match.end(0), parts.whole_path.size]))
|
19
18
|
match[0]
|
20
19
|
else
|
21
20
|
parts.shift
|
data/lib/http_router.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
$LOAD_PATH << File.dirname(__FILE__)
|
1
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
2
3
|
require 'rack'
|
4
|
+
require 'url_mount'
|
3
5
|
require 'ext/rack/uri_escape'
|
4
6
|
require 'http_router/node'
|
5
7
|
require 'http_router/root'
|
@@ -9,6 +11,7 @@ require 'http_router/route'
|
|
9
11
|
require 'http_router/response'
|
10
12
|
require 'http_router/path'
|
11
13
|
require 'http_router/optional_compiler'
|
14
|
+
require 'http_router/parts'
|
12
15
|
|
13
16
|
class HttpRouter
|
14
17
|
# Raised when a Route is not able to be generated.
|
@@ -29,6 +32,7 @@ class HttpRouter
|
|
29
32
|
AmbiguousVariableException = Class.new(RuntimeError)
|
30
33
|
|
31
34
|
attr_reader :named_routes, :routes, :root
|
35
|
+
attr_accessor :url_mount
|
32
36
|
|
33
37
|
# Monkey-patches Rack::Builder to use HttpRouter.
|
34
38
|
# See examples/rack_mapper.rb
|
@@ -83,7 +87,7 @@ class HttpRouter
|
|
83
87
|
def default(app)
|
84
88
|
@default_app = app
|
85
89
|
end
|
86
|
-
|
90
|
+
|
87
91
|
# Adds a path to be recognized.
|
88
92
|
#
|
89
93
|
# To assign a part of the path to a specific variable, use :variable_name within the route.
|
@@ -93,7 +97,7 @@ class HttpRouter
|
|
93
97
|
# For example, <tt>add('/path/*id')</tt> would match <tt>/path/123/456/789</tt>, with the variable <tt>:id</tt> having the value <tt>["123", "456", "789"]</tt>.
|
94
98
|
#
|
95
99
|
# As well, paths can end with two optional parts, <tt>*</tt> and <tt>/?</tt>. If it ends with a <tt>*</tt>, it will match partially, returning the part of the path unmatched in the PATH_INFO value of the env. The part matched to will be returned in the SCRIPT_NAME. If it ends with <tt>/?</tt>, then a trailing / on the path will be optionally matched for that specific route. As trailing /'s are ignored by default, you probably don't actually want to use this option that frequently.
|
96
|
-
#
|
100
|
+
#
|
97
101
|
# Routes can also contain optional parts. There are surrounded with <tt>( )</tt>'s. If you need to match on a bracket in the route itself, you can escape the parentheses with a backslash.
|
98
102
|
#
|
99
103
|
# The second argument, options, is an optional hash that can modify the route in further ways. See HttpRouter::Route#with_options for details. Typically, you want to add further options to the route by calling additional methods on it. See HttpRouter::Route for further details.
|
@@ -144,7 +148,7 @@ class HttpRouter
|
|
144
148
|
|
145
149
|
# Generate a URL for a specified route. This will accept a list of variable values plus any other variable names named as a hash.
|
146
150
|
# This first value must be either the Route object or the name of the route.
|
147
|
-
#
|
151
|
+
#
|
148
152
|
# Example:
|
149
153
|
# router = HttpRouter.new
|
150
154
|
# router.add('/:foo.:format).name(:test).compile
|
@@ -167,7 +171,7 @@ class HttpRouter
|
|
167
171
|
end
|
168
172
|
end
|
169
173
|
|
170
|
-
# Rack compatible #call. If matching route is found, and +dest+ value responds to #call, processing will pass to the matched route. Otherwise,
|
174
|
+
# Rack compatible #call. If matching route is found, and +dest+ value responds to #call, processing will pass to the matched route. Otherwise,
|
171
175
|
# the default application will be called. The router will be available in the env under the key <tt>router</tt>. And parameters matched will
|
172
176
|
# be available under the key <tt>router.params</tt>. The HttpRouter::Response object will be available under the key <tt>router.response</tt> if
|
173
177
|
# a response is available.
|
@@ -192,7 +196,7 @@ class HttpRouter
|
|
192
196
|
@default_app.call(env)
|
193
197
|
end
|
194
198
|
end
|
195
|
-
|
199
|
+
|
196
200
|
# Returns a new node
|
197
201
|
def node(*args)
|
198
202
|
Node.new(self, *args)
|
@@ -211,7 +215,7 @@ class HttpRouter
|
|
211
215
|
def variable(*args)
|
212
216
|
Variable.new(self, *args)
|
213
217
|
end
|
214
|
-
|
218
|
+
|
215
219
|
# Returns a new glob
|
216
220
|
def glob(*args)
|
217
221
|
Glob.new(self, *args)
|
@@ -219,16 +223,25 @@ class HttpRouter
|
|
219
223
|
|
220
224
|
# Creates a deep-copy of the router.
|
221
225
|
def clone
|
222
|
-
cloned_router = HttpRouter.new(@default_app, @options
|
226
|
+
cloned_router = HttpRouter.new(@default_app, @options)
|
223
227
|
@routes.each do |route|
|
224
|
-
new_route = route.clone
|
225
|
-
|
228
|
+
new_route = route.clone(cloned_router)
|
229
|
+
cloned_router.add_route(new_route).compile
|
230
|
+
new_route.name(route.named) if route.named
|
231
|
+
|
232
|
+
if route.dest
|
233
|
+
begin
|
234
|
+
new_route.to route.dest.clone
|
235
|
+
rescue
|
236
|
+
new_route.to route.dest
|
237
|
+
end
|
238
|
+
end
|
226
239
|
end
|
227
240
|
cloned_router
|
228
241
|
end
|
229
242
|
|
230
243
|
def split(path)
|
231
|
-
(path
|
244
|
+
Parts.new(path)
|
232
245
|
end
|
233
246
|
|
234
247
|
private
|
data/spec/generate_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
describe "HttpRouter#generate" do
|
2
3
|
before(:each) do
|
3
4
|
@router = HttpRouter.new
|
@@ -32,7 +33,7 @@ describe "HttpRouter#generate" do
|
|
32
33
|
@router.add("/:var").name(:test).compile
|
33
34
|
@router.url(:test, 'test', :query => 'string').should == '/test?query=string'
|
34
35
|
end
|
35
|
-
|
36
|
+
|
36
37
|
it "should generate with multiple dynamics" do
|
37
38
|
@router.add("/:var/:baz").name(:test).compile
|
38
39
|
@router.url(:test, 'one', 'two').should == '/one/two'
|
@@ -49,7 +50,7 @@ describe "HttpRouter#generate" do
|
|
49
50
|
@router.add("/test.:format").name(:test).compile
|
50
51
|
@router.url(:test, :format => 'html').should == '/test.html'
|
51
52
|
end
|
52
|
-
|
53
|
+
|
53
54
|
it "should generate with format as a symbol" do
|
54
55
|
@router.add("/test.:format").name(:test).compile
|
55
56
|
@router.url(:test, :format => :html).should == '/test.html'
|
@@ -60,24 +61,24 @@ describe "HttpRouter#generate" do
|
|
60
61
|
@router.url(:test, 'html').should == '/test.html'
|
61
62
|
@router.url(:test).should == '/test'
|
62
63
|
end
|
63
|
-
|
64
|
+
|
64
65
|
it "should generate a dynamic path and a format" do
|
65
66
|
@router.add("/:var1.:format").name(:test).compile
|
66
67
|
@router.url(:test, 'var', :format => 'html').should == '/var.html'
|
67
68
|
end
|
68
|
-
|
69
|
+
|
69
70
|
it "should generate a dynamic path and an optional format" do
|
70
71
|
@router.add("/:var1(.:format)").name(:test).compile
|
71
72
|
@router.url(:test, 'var').should == '/var'
|
72
73
|
@router.url(:test, 'var', :format => 'html').should == '/var.html'
|
73
74
|
end
|
74
|
-
|
75
|
+
|
75
76
|
it "should generate multiple dynamics and a format" do
|
76
77
|
@router.add("/:foo/:bar.:format").name(:test).compile
|
77
78
|
@router.url(:test, 'var', 'baz', 'html').should == '/var/baz.html'
|
78
79
|
@router.url(:test, :foo => 'var', :bar => 'baz', :format => 'html').should == '/var/baz.html'
|
79
80
|
end
|
80
|
-
|
81
|
+
|
81
82
|
it "should generate multiple dynamics and an optional format" do
|
82
83
|
@router.add("/:foo/:bar(.:format)").name(:test).compile
|
83
84
|
@router.url(:test, 'var', 'baz').should == '/var/baz'
|
@@ -137,5 +138,90 @@ describe "HttpRouter#generate" do
|
|
137
138
|
end
|
138
139
|
end
|
139
140
|
|
141
|
+
context "url mounting" do
|
142
|
+
context "nested routes" do
|
143
|
+
before(:each) do
|
144
|
+
@r1 = HttpRouter.new
|
145
|
+
@r2 = HttpRouter.new
|
146
|
+
@r2.add("/bar").name(:test).compile
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should set the url mount on a child route" do
|
150
|
+
route = @r1.add("/foo").to(@r2)
|
151
|
+
@r2.url_mount.url.should == "/foo"
|
152
|
+
@r2.url(:test).should == "/foo/bar"
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should set any default values on the url mount" do
|
156
|
+
route = @r1.add("/foo/:bar").default(:bar => "baz").to(@r2)
|
157
|
+
@r2.url(:test).should == "/foo/baz/bar"
|
158
|
+
@r2.url(:test, :bar => "haha").should == "/foo/haha/bar"
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should use multiple variables" do
|
162
|
+
@r1.add("/foo/:bar/:baz").default(:bar => "bar").to(@r2)
|
163
|
+
@r2.url(:test, :baz => "baz").should == "/foo/bar/baz/bar"
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should not steal parameters from the defaults it doesn't need" do
|
167
|
+
route = @r1.add("/foo/:bar").default(:bar => "baz").to(@r2)
|
168
|
+
@r2.url(:test, :bang => "ers").should == "/foo/baz/bar?bang=ers"
|
169
|
+
@r2.url(:test, :bar => "haha", :bang => "ers").should == "/foo/haha/bar?bang=ers"
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should generate on a path with an optional variable" do
|
173
|
+
@r1.add("/foo(/:bar)").to(@r2)
|
174
|
+
@r2.add("/hey(/:there)").name(:test).compile
|
175
|
+
@r2.url(:test).should == "/foo/hey"
|
176
|
+
@r2.url(:test, :bar => "bar").should == "/foo/bar/hey"
|
177
|
+
@r2.url(:test, :bar => "bar", :there => "there").should == "/foo/bar/hey/there"
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should nest 3 times deeply" do
|
181
|
+
@r3 = HttpRouter.new
|
182
|
+
@r1.add("/foo(/:bar)").default(:bar => "barry").to(@r2)
|
183
|
+
@r2.add("/hi").name(:hi).compile
|
184
|
+
@r2.add("/mounted").to(@r3)
|
185
|
+
@r3.add("/endpoint").name(:endpoint).compile
|
186
|
+
|
187
|
+
@r2.url(:hi).should == "/foo/barry/hi"
|
188
|
+
@r3.url(:endpoint).should == "/foo/barry/mounted/endpoint"
|
189
|
+
@r3.url(:endpoint, :bar => "flower").should == "/foo/flower/mounted/endpoint"
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should allow me to set the host via a default" do
|
193
|
+
@r1.add("/mounted").default(:host => "example.com").to(@r2)
|
194
|
+
@r2.url(:test).should == "http://example.com/mounted/bar"
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should allow me to set the host via an option" do
|
198
|
+
@r1.add("/mounted").to(@r2)
|
199
|
+
@r2.url(:test).should == "/mounted/bar"
|
200
|
+
@r2.url(:test, :host => "example.com").should == "http://example.com/mounted/bar"
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should allow me to set the scheme via an option" do
|
204
|
+
@r1.add("/mounted").to(@r2)
|
205
|
+
@r2.url(:test).should == "/mounted/bar"
|
206
|
+
@r2.url(:test, :scheme => "https", :host => "example.com").should == "https://example.com/mounted/bar"
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should clone my nested structure" do
|
210
|
+
@r3 = HttpRouter.new
|
211
|
+
@r1.add("/first").to(@r2)
|
212
|
+
@r2.add("/second").to(@r3)
|
213
|
+
r1 = @r1.clone
|
214
|
+
@r1.routes.first.should_not be_nil
|
215
|
+
r2 = r1.routes.first.dest
|
216
|
+
r2.should_not be_nil
|
217
|
+
@r1.routes.first.dest.object_id.should == @r2.object_id
|
218
|
+
r2.object_id.should_not == @r2.object_id
|
219
|
+
r2.routes.should have(2).route
|
220
|
+
r3 = r2.routes.last.dest
|
221
|
+
r3.should be_an_instance_of(HttpRouter)
|
222
|
+
r3.object_id.should_not == @r3.object_id
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
140
226
|
end
|
141
227
|
end
|
data/spec/misc_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
describe "HttpRouter" do
|
2
3
|
before(:each) do
|
3
4
|
@router = HttpRouter.new
|
@@ -43,17 +44,17 @@ describe "HttpRouter" do
|
|
43
44
|
add('/test').name(:test_route).to :test
|
44
45
|
}
|
45
46
|
r2 = r1.clone
|
46
|
-
|
47
|
+
|
47
48
|
r2.add('/test2').name(:test).to(:test2)
|
48
49
|
r2.routes.size.should == 2
|
49
|
-
|
50
|
+
|
50
51
|
r1.recognize(Rack::MockRequest.env_for('/test2')).should be_nil
|
51
52
|
r2.recognize(Rack::MockRequest.env_for('/test2')).should_not be_nil
|
52
53
|
r1.named_routes[:test_route].should == r1.routes.first
|
53
54
|
r2.named_routes[:test_route].should == r2.routes.first
|
54
55
|
|
55
56
|
r1.add('/another').name(:test).to(:test2)
|
56
|
-
|
57
|
+
|
57
58
|
r1.routes.size.should == r2.routes.size
|
58
59
|
r1.url(:test).should == '/another'
|
59
60
|
r2.url(:test).should == '/test2'
|
@@ -61,4 +62,4 @@ describe "HttpRouter" do
|
|
61
62
|
r2.routes.first.dest.should == :test
|
62
63
|
end
|
63
64
|
end
|
64
|
-
end
|
65
|
+
end
|
data/spec/rack/dispatch_spec.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
route_set = HttpRouter.new
|
2
|
-
route_set.extend(CallWithMockRequestMixin)
|
3
|
-
|
4
1
|
describe "HttpRouter route dispatching with redirect_on_trailing_delimiters" do
|
5
2
|
before(:each) do
|
6
3
|
@route_set = HttpRouter.new(:redirect_trailing_slash => true)
|
@@ -18,78 +15,79 @@ end
|
|
18
15
|
|
19
16
|
describe "HttpRouter route dispatching" do
|
20
17
|
before(:each) do
|
21
|
-
route_set.
|
18
|
+
@route_set = HttpRouter.new(:redirect_trailing_slash => true)
|
19
|
+
@route_set.extend(CallWithMockRequestMixin)
|
22
20
|
@app = MockApp.new("Hello World!")
|
23
21
|
end
|
24
22
|
|
25
23
|
describe "HTTP GET" do
|
26
24
|
before(:each) do
|
27
|
-
route_set.reset!
|
28
|
-
route_set.add('/sample').request_method('GET').to(@app)
|
25
|
+
@route_set.reset!
|
26
|
+
@route_set.add('/sample').request_method('GET').to(@app)
|
29
27
|
end
|
30
28
|
|
31
29
|
it "should dispatch a request" do
|
32
|
-
response = route_set.call_with_mock_request
|
30
|
+
response = @route_set.call_with_mock_request
|
33
31
|
response.body.should eql("Hello World!")
|
34
32
|
end
|
35
33
|
|
36
34
|
it "should write router.params" do
|
37
|
-
response = route_set.call_with_mock_request
|
35
|
+
response = @route_set.call_with_mock_request
|
38
36
|
@app.env["router.params"].should == {}
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
42
40
|
describe "HTTP POST" do
|
43
41
|
before(:each) do
|
44
|
-
route_set.reset!
|
45
|
-
route_set.add('/sample').post.to(@app)
|
46
|
-
route_set.add('/sample').to(MockApp.new("You shouldn't get here if you are using POST"))
|
42
|
+
@route_set.reset!
|
43
|
+
@route_set.add('/sample').post.to(@app)
|
44
|
+
@route_set.add('/sample').to(MockApp.new("You shouldn't get here if you are using POST"))
|
47
45
|
end
|
48
46
|
|
49
47
|
it "should dispatch a POST request" do
|
50
|
-
response = route_set.call_with_mock_request('/sample', 'POST')
|
48
|
+
response = @route_set.call_with_mock_request('/sample', 'POST')
|
51
49
|
response.body.should eql("Hello World!")
|
52
50
|
end
|
53
51
|
|
54
52
|
it "shouldn't dispatch a GET request" do
|
55
|
-
response = route_set.call_with_mock_request('/sample', 'GET')
|
53
|
+
response = @route_set.call_with_mock_request('/sample', 'GET')
|
56
54
|
response.body.should eql("You shouldn't get here if you are using POST")
|
57
55
|
end
|
58
56
|
|
59
57
|
it "should write router.params" do
|
60
|
-
response = route_set.call_with_mock_request("/sample", 'POST')
|
58
|
+
response = @route_set.call_with_mock_request("/sample", 'POST')
|
61
59
|
@app.env["router.params"].should == {}
|
62
60
|
end
|
63
61
|
end
|
64
62
|
|
65
63
|
it "should returns HTTP 405 if the method mis-matches" do
|
66
|
-
route_set.reset!
|
67
|
-
route_set.post('/sample').to(@app)
|
68
|
-
route_set.put('/sample').to(@app)
|
69
|
-
response = route_set.call_with_mock_request('/sample', 'GET')
|
64
|
+
@route_set.reset!
|
65
|
+
@route_set.post('/sample').to(@app)
|
66
|
+
@route_set.put('/sample').to(@app)
|
67
|
+
response = @route_set.call_with_mock_request('/sample', 'GET')
|
70
68
|
response.status.should eql(405)
|
71
69
|
response['Allow'].should == 'POST, PUT'
|
72
70
|
end
|
73
71
|
|
74
72
|
it "should returns HTTP 404 if route doesn't exist" do
|
75
|
-
response = route_set.call_with_mock_request("/not-existing-url")
|
73
|
+
response = @route_set.call_with_mock_request("/not-existing-url")
|
76
74
|
response.status.should eql(404)
|
77
75
|
end
|
78
76
|
|
79
77
|
describe "shortcuts" do
|
80
78
|
describe "get" do
|
81
79
|
before(:each) do
|
82
|
-
route_set.reset!
|
83
|
-
route_set.get('/sample').head.to(@app)
|
80
|
+
@route_set.reset!
|
81
|
+
@route_set.get('/sample').head.to(@app)
|
84
82
|
end
|
85
83
|
|
86
84
|
it "should dispatch a GET request" do
|
87
|
-
response = route_set.call_with_mock_request("/sample", "GET")
|
85
|
+
response = @route_set.call_with_mock_request("/sample", "GET")
|
88
86
|
response.body.should eql("Hello World!")
|
89
87
|
end
|
90
88
|
|
91
89
|
it "should dispatch a HEAD request" do
|
92
|
-
response = route_set.call_with_mock_request("/sample", "HEAD")
|
90
|
+
response = @route_set.call_with_mock_request("/sample", "HEAD")
|
93
91
|
response.body.should eql("Hello World!")
|
94
92
|
end
|
95
93
|
end
|
data/spec/rack/generate_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
1
3
|
route_set = HttpRouter.new
|
2
4
|
route_set.extend(CallWithMockRequestMixin)
|
3
5
|
|
@@ -9,12 +11,12 @@ describe "HttpRouter route generation" do
|
|
9
11
|
route_set.add("/named/simple/:named_simple_var").name(:simple).compile
|
10
12
|
route_set.add("/named/optional(/:named_optional_var)").name(:optional).compile
|
11
13
|
end
|
12
|
-
|
14
|
+
|
13
15
|
describe "named routes" do
|
14
16
|
it "should generate a fixed path" do
|
15
17
|
route_set.url(:fixed).should == "/fixed"
|
16
18
|
end
|
17
|
-
|
19
|
+
|
18
20
|
it "should generate a named path route" do
|
19
21
|
route_set.url(:simple, :named_simple_var => "the_var").should == "/named/simple/the_var"
|
20
22
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
1
3
|
describe "HttpRouter as middleware" do
|
2
4
|
before(:each) do
|
3
5
|
@builder = Rack::Builder.new do
|
@@ -17,4 +19,4 @@ describe "HttpRouter as middleware" do
|
|
17
19
|
@builder.call(Rack::MockRequest.env_for('/test')).last.join.should == 'test'
|
18
20
|
end
|
19
21
|
end
|
20
|
-
|
22
|
+
|
data/spec/rack/route_spec.rb
CHANGED
data/spec/recognize_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
describe "HttpRouter#recognize" do
|
2
3
|
before(:each) do
|
3
4
|
@router = HttpRouter.new
|
@@ -10,7 +11,7 @@ describe "HttpRouter#recognize" do
|
|
10
11
|
@router.recognize(Rack::MockRequest.env_for(path)).route.should == route
|
11
12
|
end
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
context("with optional parts") do
|
15
16
|
it "work either way" do
|
16
17
|
route = @router.add("/test(/optional)").to(:test)
|
@@ -199,7 +200,7 @@ describe "HttpRouter#recognize" do
|
|
199
200
|
response.params_as_hash[:format].should be_nil
|
200
201
|
response.params_as_hash[:test].should == 'hey'
|
201
202
|
end
|
202
|
-
|
203
|
+
|
203
204
|
context "with globs" do
|
204
205
|
it "should recognize" do
|
205
206
|
route = @router.add('/test/*variable').to(:test)
|
@@ -216,9 +217,9 @@ describe "HttpRouter#recognize" do
|
|
216
217
|
response.should be_nil
|
217
218
|
end
|
218
219
|
end
|
219
|
-
|
220
|
+
|
220
221
|
end
|
221
|
-
|
222
|
+
|
222
223
|
context("with interstitial variables") do
|
223
224
|
it "should recognize" do
|
224
225
|
route = @router.add('/one-:variable-time').to(:test)
|
@@ -234,14 +235,14 @@ describe "HttpRouter#recognize" do
|
|
234
235
|
response.route.should == route
|
235
236
|
response.params_as_hash[:variable].should == '123'
|
236
237
|
end
|
237
|
-
|
238
|
+
|
238
239
|
it "should recognize when there is an extension" do
|
239
240
|
route = @router.add('/hey.:greed.html').to(:test)
|
240
241
|
response = @router.recognize(Rack::MockRequest.env_for('/hey.greedyboy.html'))
|
241
242
|
response.route.should == route
|
242
243
|
response.params_as_hash[:greed].should == 'greedyboy'
|
243
244
|
end
|
244
|
-
|
245
|
+
|
245
246
|
end
|
246
247
|
|
247
248
|
context("with dynamic greedy paths") do
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require "sinatra"
|
2
3
|
require "http_router/interface/sinatra"
|
3
4
|
|
@@ -79,28 +80,28 @@ describe "HttpRouter (for Sinatra) route recognition" do
|
|
79
80
|
describe "mapping functionality" do
|
80
81
|
|
81
82
|
it "should map a basic route" do
|
82
|
-
@app.get('/hi', :name => :hi) { generate(:hi) }
|
83
|
+
@app.get('/hi', :name => :hi) { generate(:hi) }
|
83
84
|
response = @app.call_with_mock_request('/hi')
|
84
85
|
response.status.should == 200
|
85
86
|
response.body.should == "/hi"
|
86
87
|
end
|
87
88
|
|
88
89
|
it "should map a basic route ignoring trailing delimiters" do
|
89
|
-
@app.get('/hi', :name => :hi) { generate(:hi) }
|
90
|
+
@app.get('/hi', :name => :hi) { generate(:hi) }
|
90
91
|
response = @app.call_with_mock_request('/hi/')
|
91
92
|
response.status.should == 200
|
92
93
|
response.body.should == "/hi"
|
93
94
|
end
|
94
95
|
|
95
96
|
it "should map a basic route with params" do
|
96
|
-
@app.get('/hi/:id', :name => :hi) { generate(:hi, :id => 18) }
|
97
|
+
@app.get('/hi/:id', :name => :hi) { generate(:hi, :id => 18) }
|
97
98
|
response = @app.call_with_mock_request('/hi/1')
|
98
99
|
response.status.should == 200
|
99
100
|
response.body.should == "/hi/18"
|
100
101
|
end
|
101
102
|
|
102
103
|
it "should map route with params" do
|
103
|
-
@app.get('/hi-:id', :name => :hi) { generate(:hi, :id => 18) }
|
104
|
+
@app.get('/hi-:id', :name => :hi) { generate(:hi, :id => 18) }
|
104
105
|
response = @app.call_with_mock_request('/hi-1')
|
105
106
|
response.status.should == 200
|
106
107
|
response.body.should == "/hi-18"
|
@@ -137,4 +138,4 @@ describe "HttpRouter (for Sinatra) route recognition" do
|
|
137
138
|
response.body.should_not match(/__sinatra__/)
|
138
139
|
end
|
139
140
|
end
|
140
|
-
end
|
141
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'rubygems'
|
3
|
+
require 'http_router'
|
4
|
+
require 'spec'
|
5
|
+
require 'spec/autorun'
|
2
6
|
|
3
7
|
module CallWithMockRequestMixin
|
4
8
|
def call_with_mock_request(url = "/sample", method = "GET", params = Hash.new)
|
@@ -21,4 +25,4 @@ class MockApp
|
|
21
25
|
@headers.merge("Content-Length" => @body.length.to_s)
|
22
26
|
[@status, @headers, [@body]]
|
23
27
|
end
|
24
|
-
end
|
28
|
+
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
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 4
|
10
|
+
version: 0.2.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joshua Hull
|
@@ -34,6 +34,21 @@ dependencies:
|
|
34
34
|
version: 1.0.0
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: url_mount
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 15
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 2
|
49
|
+
version: "0.2"
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
37
52
|
description: A kick-ass HTTP router for use in Rack & Sinatra
|
38
53
|
email: joshbuddy@gmail.com
|
39
54
|
executables: []
|
@@ -44,6 +59,8 @@ extra_rdoc_files:
|
|
44
59
|
- README.rdoc
|
45
60
|
files:
|
46
61
|
- .gitignore
|
62
|
+
- Gemfile
|
63
|
+
- Gemfile.lock
|
47
64
|
- README.rdoc
|
48
65
|
- Rakefile
|
49
66
|
- VERSION
|
@@ -67,6 +84,7 @@ files:
|
|
67
84
|
- lib/http_router/interface/sinatra.rb
|
68
85
|
- lib/http_router/node.rb
|
69
86
|
- lib/http_router/optional_compiler.rb
|
87
|
+
- lib/http_router/parts.rb
|
70
88
|
- lib/http_router/path.rb
|
71
89
|
- lib/http_router/response.rb
|
72
90
|
- lib/http_router/root.rb
|
@@ -74,6 +92,7 @@ files:
|
|
74
92
|
- lib/http_router/variable.rb
|
75
93
|
- spec/generate_spec.rb
|
76
94
|
- spec/misc_spec.rb
|
95
|
+
- spec/mounting_spec.rb
|
77
96
|
- spec/rack/dispatch_spec.rb
|
78
97
|
- spec/rack/generate_spec.rb
|
79
98
|
- spec/rack/middleware_spec.rb
|
@@ -119,6 +138,7 @@ summary: A kick-ass HTTP router for use in Rack & Sinatra
|
|
119
138
|
test_files:
|
120
139
|
- spec/generate_spec.rb
|
121
140
|
- spec/misc_spec.rb
|
141
|
+
- spec/mounting_spec.rb
|
122
142
|
- spec/rack/dispatch_spec.rb
|
123
143
|
- spec/rack/generate_spec.rb
|
124
144
|
- spec/rack/middleware_spec.rb
|