http_router 0.0.1
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/README.rdoc +60 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/lib/http_router/glob.rb +18 -0
- data/lib/http_router/node.rb +107 -0
- data/lib/http_router/path.rb +38 -0
- data/lib/http_router/response.rb +20 -0
- data/lib/http_router/root.rb +103 -0
- data/lib/http_router/route.rb +102 -0
- data/lib/http_router/sinatra.rb +149 -0
- data/lib/http_router/variable.rb +26 -0
- data/lib/http_router.rb +250 -0
- data/lib/rack/uri_escape.rb +38 -0
- data/spec/generate_spec.rb +54 -0
- data/spec/rack/dispatch_spec.rb +113 -0
- data/spec/rack/generate_spec.rb +27 -0
- data/spec/rack/route_spec.rb +70 -0
- data/spec/recognize_spec.rb +176 -0
- data/spec/sinatra/recognize_spec.rb +140 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +24 -0
- metadata +88 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
describe "HttpRouter#recognize" do
|
2
|
+
before(:each) do
|
3
|
+
@router = HttpRouter.new
|
4
|
+
end
|
5
|
+
|
6
|
+
context("static paths") do
|
7
|
+
['/', '/test', '/test/time', '/one/more/what', '/test.html'].each do |path|
|
8
|
+
it "should recognize #{path.inspect}" do
|
9
|
+
route = @router.add(path)
|
10
|
+
@router.recognize(Rack::MockRequest.env_for(path)).route.should == route
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context("with optional parts") do
|
15
|
+
it "work either way" do
|
16
|
+
route = @router.add("/test(/optional)")
|
17
|
+
@router.recognize(Rack::MockRequest.env_for('/test')).route.should == route
|
18
|
+
@router.recognize(Rack::MockRequest.env_for('/test/optional')).route.should == route
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context("partial matching") do
|
23
|
+
it "should match partially or completely" do
|
24
|
+
route = @router.add("/test*")
|
25
|
+
@router.recognize(Rack::MockRequest.env_for('/test')).route.should == route
|
26
|
+
response = @router.recognize(Rack::MockRequest.env_for('/test/optional'))
|
27
|
+
response.route.should == route
|
28
|
+
response.remaining_path.should == '/optional'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context("trailing slashes") do
|
33
|
+
it "should ignore a trailing slash" do
|
34
|
+
route = @router.add("/test")
|
35
|
+
@router.recognize(Rack::MockRequest.env_for('/test/')).route.should == route
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not recognize a trailing slash when used with the /? syntax and ignore_trailing_slash disabled" do
|
39
|
+
@router = HttpRouter.new(:ignore_trailing_slash => false)
|
40
|
+
route = @router.add("/test/?")
|
41
|
+
@router.recognize(Rack::MockRequest.env_for('/test/')).route.should == route
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should recognize a trailing slash when used with the /? syntax and ignore_trailing_slash enabled" do
|
45
|
+
@router = HttpRouter.new(:ignore_trailing_slash => false)
|
46
|
+
route = @router.add("/test")
|
47
|
+
@router.recognize(Rack::MockRequest.env_for('/test/')).should be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
context "request methods" do
|
54
|
+
it "should pick a specific request_method" do
|
55
|
+
route = @router.add("/test", :conditions => {:request_method => 'POST'})
|
56
|
+
@router.recognize(Rack::MockRequest.env_for('/test', :method => 'POST')).route.should == route
|
57
|
+
@router.recognize(Rack::MockRequest.env_for('/test', :method => 'GET')).status.should == 405
|
58
|
+
@router.recognize(Rack::MockRequest.env_for('/test', :method => 'GET')).headers['Allow'].should == "POST"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should pick a specific request_method with other paths all through it" do
|
62
|
+
@router.add("/test", :conditions => {:request_method => 'POST'}).name(:test_post)
|
63
|
+
@router.add("/test/post", :conditions => {:request_method => 'POST'}).name(:test_post_post)
|
64
|
+
@router.add("/test", :conditions => {:request_method => 'GET'}).name(:test_get)
|
65
|
+
@router.add("/test/post", :conditions => {:request_method => 'GET'}).name(:test_post_get)
|
66
|
+
@router.add("/test/post").name(:test_post_catchall)
|
67
|
+
@router.add("/test").name(:test_catchall)
|
68
|
+
@router.recognize(Rack::MockRequest.env_for('/test', :method => 'POST')).route.named.should == :test_post
|
69
|
+
@router.recognize(Rack::MockRequest.env_for('/test', :method => 'GET')).route.named.should == :test_get
|
70
|
+
@router.recognize(Rack::MockRequest.env_for('/test', :method => 'PUT')).route.named.should == :test_catchall
|
71
|
+
@router.recognize(Rack::MockRequest.env_for('/test/post', :method => 'POST')).route.named.should == :test_post_post
|
72
|
+
@router.recognize(Rack::MockRequest.env_for('/test/post', :method => 'GET')).route.named.should == :test_post_get
|
73
|
+
@router.recognize(Rack::MockRequest.env_for('/test/post', :method => 'PUT')).route.named.should == :test_post_catchall
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should move an endpoint to the non-specific request method when a more specific route gets added" do
|
77
|
+
@router.add("/test").name(:test_catchall)
|
78
|
+
@router.add("/test", :conditions => {:request_method => 'POST'}).name(:test_post)
|
79
|
+
@router.recognize(Rack::MockRequest.env_for('/test', :method => 'POST')).route.named.should == :test_post
|
80
|
+
@router.recognize(Rack::MockRequest.env_for('/test', :method => 'PUT')).route.named.should == :test_catchall
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
context("dynamic paths") do
|
86
|
+
it "should recognize '/:variable'" do
|
87
|
+
route = @router.add('/:variable')
|
88
|
+
response = @router.recognize(Rack::MockRequest.env_for('/value'))
|
89
|
+
response.route.should == route
|
90
|
+
response.params.should == ['value']
|
91
|
+
response.params_as_hash[:variable].should == 'value'
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should recognize '/test.:format'" do
|
95
|
+
route = @router.add('/test.:format')
|
96
|
+
response = @router.recognize(Rack::MockRequest.env_for('/test.html'))
|
97
|
+
response.route.should == route
|
98
|
+
response.extension.should == 'html'
|
99
|
+
response.params_as_hash[:format].should == 'html'
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should recognize '/test(.:format)'" do
|
103
|
+
route = @router.add('/test(.:format)')
|
104
|
+
response = @router.recognize(Rack::MockRequest.env_for('/test.html'))
|
105
|
+
response.route.should == route
|
106
|
+
response.extension.should == 'html'
|
107
|
+
response.params_as_hash[:format].should == 'html'
|
108
|
+
response = @router.recognize(Rack::MockRequest.env_for('/test'))
|
109
|
+
response.route.should == route
|
110
|
+
response.extension.should be_nil
|
111
|
+
response.params_as_hash[:format].should be_nil
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should recognize '/:test.:format'" do
|
115
|
+
route = @router.add('/:test.:format')
|
116
|
+
response = @router.recognize(Rack::MockRequest.env_for('/hey.html'))
|
117
|
+
response.route.should == route
|
118
|
+
response.extension.should == 'html'
|
119
|
+
response.params_as_hash[:format].should == 'html'
|
120
|
+
response.params_as_hash[:test].should == 'hey'
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should recognize '/:test(.:format)'" do
|
124
|
+
route = @router.add('/:test(.:format)')
|
125
|
+
response = @router.recognize(Rack::MockRequest.env_for('/hey.html'))
|
126
|
+
response.route.should == route
|
127
|
+
response.extension.should == 'html'
|
128
|
+
response.params_as_hash[:format].should == 'html'
|
129
|
+
response.params_as_hash[:test].should == 'hey'
|
130
|
+
response = @router.recognize(Rack::MockRequest.env_for('/hey'))
|
131
|
+
response.route.should == route
|
132
|
+
response.extension.should be_nil
|
133
|
+
response.params_as_hash[:format].should be_nil
|
134
|
+
response.params_as_hash[:test].should == 'hey'
|
135
|
+
end
|
136
|
+
|
137
|
+
context "globs" do
|
138
|
+
it "should recognize a glob" do
|
139
|
+
route = @router.add('/test/*variable')
|
140
|
+
response = @router.recognize(Rack::MockRequest.env_for('/test/one/two/three'))
|
141
|
+
response.route.should == route
|
142
|
+
response.params.should == [['one', 'two', 'three']]
|
143
|
+
end
|
144
|
+
it "should recognize a glob with a regexp" do
|
145
|
+
route = @router.add('/test/*variable/anymore', :matches_with => {:variable => /^\d+$/})
|
146
|
+
response = @router.recognize(Rack::MockRequest.env_for('/test/123/345/567/anymore'))
|
147
|
+
response.route.should == route
|
148
|
+
response.params.should == [['123', '345', '567']]
|
149
|
+
response = @router.recognize(Rack::MockRequest.env_for('/test/123/345/567'))
|
150
|
+
response.should be_nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
context("interstitial variables") do
|
157
|
+
it "should recognize interstitial variables" do
|
158
|
+
route = @router.add('/one-:variable-time')
|
159
|
+
response = @router.recognize(Rack::MockRequest.env_for('/one-value-time'))
|
160
|
+
response.route.should == route
|
161
|
+
response.params_as_hash[:variable].should == 'value'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context("dynamic greedy paths") do
|
166
|
+
it "should recognize greedy variables" do
|
167
|
+
route = @router.add('/:variable', :matches_with => { :variable => /\d+/})
|
168
|
+
response = @router.recognize(Rack::MockRequest.env_for('/123'))
|
169
|
+
response.route.should == route
|
170
|
+
response.params.should == ['123']
|
171
|
+
response.params_as_hash[:variable].should == '123'
|
172
|
+
response = @router.recognize(Rack::MockRequest.env_for('/asd'))
|
173
|
+
response.should be_nil
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require "sinatra"
|
2
|
+
require "http_router/sinatra"
|
3
|
+
|
4
|
+
describe "Usher (for Sinatra) route recognition" do
|
5
|
+
before(:each) do
|
6
|
+
@app = Sinatra.new { register HttpRouter::Interface::Sinatra::Extension }
|
7
|
+
@app.extend(CallWithMockRequestMixin)
|
8
|
+
@app.reset!
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "basic functionality" do
|
12
|
+
it "should map not found" do
|
13
|
+
response = @app.call_with_mock_request('/bar')
|
14
|
+
response.status.should == 404
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should map index" do
|
18
|
+
@app.get("/") { "index" }
|
19
|
+
response = @app.call_with_mock_request('/')
|
20
|
+
response.status.should == 200
|
21
|
+
response.body.should == "index"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should ignore trailing delimiters" do
|
25
|
+
@app.get("/foo") { "foo" }
|
26
|
+
response = @app.call_with_mock_request('/foo')
|
27
|
+
response.status.should == 200
|
28
|
+
response.body.should == "foo"
|
29
|
+
response = @app.call_with_mock_request('/foo/')
|
30
|
+
response.status.should == 200
|
31
|
+
response.body.should == "foo"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should ignore trailing delimiters in a more advanced route" do
|
35
|
+
@app.get("/foo") { "foo" }
|
36
|
+
@app.get("/foo/bar") { "bar" }
|
37
|
+
response = @app.call_with_mock_request('/foo')
|
38
|
+
response.status.should == 200
|
39
|
+
response.body.should == "foo"
|
40
|
+
response = @app.call_with_mock_request('/foo/bar')
|
41
|
+
response.status.should == 200
|
42
|
+
response.body.should == "bar"
|
43
|
+
response = @app.call_with_mock_request('/foo/')
|
44
|
+
response.status.should == 200
|
45
|
+
response.body.should == "foo"
|
46
|
+
response = @app.call_with_mock_request('/foo/bar/')
|
47
|
+
response.status.should == 200
|
48
|
+
response.body.should == "bar"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should ignore trailing delimiters with an optional param" do
|
52
|
+
@app.get("/foo/(:bar)") { params[:bar] }
|
53
|
+
@app.get("/bar(/:foo)") { params[:foo] }
|
54
|
+
response = @app.call_with_mock_request('/foo/bar')
|
55
|
+
response.status.should == 200
|
56
|
+
response.body.should == "bar"
|
57
|
+
response = @app.call_with_mock_request('/bar/foo')
|
58
|
+
response.status.should == 200
|
59
|
+
response.body.should == "foo"
|
60
|
+
response = @app.call_with_mock_request('/bar')
|
61
|
+
response.status.should == 200
|
62
|
+
response.body.should == ""
|
63
|
+
response = @app.call_with_mock_request('/bar/')
|
64
|
+
response.status.should == 200
|
65
|
+
response.body.should == ""
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should use sinatra optionals trailing delimiters" do
|
69
|
+
@app.get("/foo/?") { "foo" }
|
70
|
+
response = @app.call_with_mock_request('/foo')
|
71
|
+
response.status.should == 200
|
72
|
+
response.body.should == "foo"
|
73
|
+
response = @app.call_with_mock_request('/foo/')
|
74
|
+
response.status.should == 200
|
75
|
+
response.body.should == "foo"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "mapping functionality" do
|
80
|
+
|
81
|
+
it "should map a basic route" do
|
82
|
+
@app.get('/hi', :name => :hi) { generate(:hi) }
|
83
|
+
response = @app.call_with_mock_request('/hi')
|
84
|
+
response.status.should == 200
|
85
|
+
response.body.should == "/hi"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should map a basic route ignoring trailing delimiters" do
|
89
|
+
@app.get('/hi', :name => :hi) { generate(:hi) }
|
90
|
+
response = @app.call_with_mock_request('/hi/')
|
91
|
+
response.status.should == 200
|
92
|
+
response.body.should == "/hi"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should map a basic route with params" do
|
96
|
+
@app.get('/hi/:id', :name => :hi) { generate(:hi, :id => 18) }
|
97
|
+
response = @app.call_with_mock_request('/hi/1')
|
98
|
+
response.status.should == 200
|
99
|
+
response.body.should == "/hi/18"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should map route with params" do
|
103
|
+
@app.get('/hi-:id', :name => :hi) { generate(:hi, :id => 18) }
|
104
|
+
response = @app.call_with_mock_request('/hi-1')
|
105
|
+
response.status.should == 200
|
106
|
+
response.body.should == "/hi-18"
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should map route with complex params" do
|
110
|
+
@app.get('/hi/:foo/:bar/:baz(.:format)') { "/#{params[:foo]}/#{params[:bar]}/#{params[:baz]}/#{params[:format]}" }
|
111
|
+
response = @app.call_with_mock_request('/hi/foo/bar/baz')
|
112
|
+
response.status.should == 200
|
113
|
+
response.body.should == "/foo/bar/baz/"
|
114
|
+
response = @app.call_with_mock_request('/hi/foo/bar-bax/baz')
|
115
|
+
response.status.should == 200
|
116
|
+
response.body.should == "/foo/bar-bax/baz/"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "not found" do
|
121
|
+
|
122
|
+
it "should correctly generate a not found page without images" do
|
123
|
+
response = @app.call_with_mock_request('/bar')
|
124
|
+
response.status.should == 404
|
125
|
+
response.body.should_not match(/__sinatra__/)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "method not allowed" do
|
130
|
+
|
131
|
+
it "should correctly generate a not found page without images and return a 405" do
|
132
|
+
@app.post('/bar') { 'found' }
|
133
|
+
@app.put('/bar') { 'found' }
|
134
|
+
response = @app.call_with_mock_request('/bar')
|
135
|
+
response.status.should == 405
|
136
|
+
response.headers['Allow'].should == 'POST, PUT'
|
137
|
+
response.body.should_not match(/__sinatra__/)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module CallWithMockRequestMixin
|
4
|
+
def call_with_mock_request(url = "/sample", method = "GET", params = Hash.new)
|
5
|
+
params.merge!(:method => method)
|
6
|
+
request = Rack::MockRequest.new(self)
|
7
|
+
request.request(method, url, params)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MockApp
|
12
|
+
attr_accessor :status, :headers, :body, :env
|
13
|
+
def initialize(body)
|
14
|
+
@status = 200
|
15
|
+
@headers = {"Content-Type" => "text/html"}
|
16
|
+
@body = body
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
@env = env
|
21
|
+
@headers.merge("Content-Length" => @body.length.to_s)
|
22
|
+
[@status, @headers, [@body]]
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: http_router
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Joshua Hull
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-25 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: A kick-ass HTTP router for use in Rack & Sinatra
|
22
|
+
email: joshbuddy@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.rdoc
|
29
|
+
files:
|
30
|
+
- README.rdoc
|
31
|
+
- Rakefile
|
32
|
+
- VERSION
|
33
|
+
- lib/http_router.rb
|
34
|
+
- lib/http_router/glob.rb
|
35
|
+
- lib/http_router/node.rb
|
36
|
+
- lib/http_router/path.rb
|
37
|
+
- lib/http_router/response.rb
|
38
|
+
- lib/http_router/root.rb
|
39
|
+
- lib/http_router/route.rb
|
40
|
+
- lib/http_router/sinatra.rb
|
41
|
+
- lib/http_router/variable.rb
|
42
|
+
- lib/rack/uri_escape.rb
|
43
|
+
- spec/generate_spec.rb
|
44
|
+
- spec/rack/dispatch_spec.rb
|
45
|
+
- spec/rack/generate_spec.rb
|
46
|
+
- spec/rack/route_spec.rb
|
47
|
+
- spec/recognize_spec.rb
|
48
|
+
- spec/sinatra/recognize_spec.rb
|
49
|
+
- spec/spec.opts
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://github.com/joshbuddy/http_router
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options:
|
57
|
+
- --charset=UTF-8
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.3.6
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: A kick-ass HTTP router for use in Rack & Sinatra
|
81
|
+
test_files:
|
82
|
+
- spec/generate_spec.rb
|
83
|
+
- spec/rack/dispatch_spec.rb
|
84
|
+
- spec/rack/generate_spec.rb
|
85
|
+
- spec/rack/route_spec.rb
|
86
|
+
- spec/recognize_spec.rb
|
87
|
+
- spec/sinatra/recognize_spec.rb
|
88
|
+
- spec/spec_helper.rb
|