http_router 0.2.3 → 0.2.4
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 +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
|