http_router 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,20 +0,0 @@
1
- class HttpRouter
2
- class Glob < Variable
3
- def matches?(parts)
4
- return if @matches_with.nil? or parts.empty? or !match.begin(0)
5
- @matches_with.match(parts.first)
6
- end
7
-
8
- def consume(match, parts)
9
- if @matches_with
10
- params = [parts.shift]
11
- params << parts.shift while matches?(parts)
12
- params
13
- else
14
- params = parts.dup
15
- parts.clear
16
- params
17
- end
18
- end
19
- end
20
- end
@@ -1,149 +0,0 @@
1
- require 'http_router'
2
-
3
- class HttpRouter
4
- module Interface
5
- class Sinatra
6
-
7
- def initialize
8
- ::Sinatra.send(:include, Extension)
9
- end
10
-
11
- module Extension
12
-
13
- def self.registered(app)
14
- app.send(:include, Extension)
15
- end
16
-
17
- def self.included(base)
18
- base.extend ClassMethods
19
- end
20
-
21
- def generate(name, *params)
22
- self.class.generate(name, *params)
23
- end
24
-
25
- private
26
- def route!(base=self.class, pass_block=nil)
27
- if base.router and match = base.router.recognize(@request)
28
- if match.first.respond_to?(:path)
29
- @block_params = match.first.param_values
30
- (@params ||= {}).merge!(match.first.params)
31
- pass_block = catch(:pass) do
32
- route_eval(&match.first.path.route.dest)
33
- end
34
- elsif match.is_a?(Array)
35
- route_eval {
36
- match[1].each{|k,v| response[k] = v}
37
- status match[0]
38
- }
39
- end
40
- end
41
-
42
- # Run routes defined in superclass.
43
- if base.superclass.respond_to?(:router)
44
- route! base.superclass, pass_block
45
- return
46
- end
47
-
48
- route_eval(&pass_block) if pass_block
49
-
50
- route_missing
51
- end
52
-
53
- module ClassMethods
54
-
55
- def new(*args, &bk)
56
- configure! unless @_configured
57
- super(*args, &bk)
58
- end
59
-
60
- def route(verb, path, options={}, &block)
61
- name = options.delete(:name)
62
-
63
- define_method "#{verb} #{path}", &block
64
- unbound_method = instance_method("#{verb} #{path}")
65
- block = block.arity.zero? ?
66
- proc { unbound_method.bind(self).call } :
67
- proc { unbound_method.bind(self).call(*@block_params) }
68
-
69
- invoke_hook(:route_added, verb, path, block)
70
-
71
- route = router.add(path)
72
-
73
- route.matching(options[:matching]) if options.key?(:matching)
74
-
75
- route.request_method(verb)
76
- route.host(options[:host]) if options.key?(:host)
77
-
78
- route.name(name) if name
79
- route.to(block)
80
- route
81
- end
82
-
83
- def router
84
- @router ||= HttpRouter.new
85
- block_given? ? yield(@router) : @router
86
- end
87
-
88
- def generate(name, *params)
89
- router.url(name, *params)
90
- end
91
-
92
- def reset!
93
- router.reset!
94
- super
95
- end
96
-
97
- def configure!
98
- configure :development do
99
- error 404 do
100
- content_type 'text/html'
101
-
102
- (<<-HTML).gsub(/^ {17}/, '')
103
- <!DOCTYPE html>
104
- <html>
105
- <head>
106
- <style type="text/css">
107
- body { text-align:center;font-family:helvetica,arial;font-size:22px;
108
- color:#888;margin:20px}
109
- #c {margin:0 auto;width:500px;text-align:left}
110
- </style>
111
- </head>
112
- <body>
113
- <h2>Sinatra doesn't know this ditty.</h2>
114
- <div id="c">
115
- Try this:
116
- <pre>#{request.request_method.downcase} '#{request.path_info}' do\n "Hello World"\nend</pre>
117
- </div>
118
- </body>
119
- </html>
120
- HTML
121
- end
122
- error 405 do
123
- content_type 'text/html'
124
-
125
- (<<-HTML).gsub(/^ {17}/, '')
126
- <!DOCTYPE html>
127
- <html>
128
- <head>
129
- <style type="text/css">
130
- body { text-align:center;font-family:helvetica,arial;font-size:22px;
131
- color:#888;margin:20px}
132
- #c {margin:0 auto;width:500px;text-align:left}
133
- </style>
134
- </head>
135
- <body>
136
- <h2>Sinatra sorta knows this ditty, but the request method is not allowed.</h2>
137
- </body>
138
- </html>
139
- HTML
140
- end
141
- end
142
-
143
- @_configured = true
144
- end
145
- end # ClassMethods
146
- end # Extension
147
- end # Sinatra
148
- end # Interface
149
- end # HttpRouter
@@ -1,24 +0,0 @@
1
- class HttpRouter
2
- class Parts < Array
3
- SLASH = '/'.freeze
4
- SLASH_RX = Regexp.new(SLASH)
5
-
6
- def initialize(path)
7
- super((path[0] == ?/ ? path[1, path.size] : path).split(SLASH_RX))
8
- end
9
-
10
- def whole_path
11
- @whole_path ||= join(SLASH)
12
- end
13
-
14
- def shift
15
- @whole_path = nil
16
- super
17
- end
18
-
19
- def replace(ary)
20
- @whole_path = nil
21
- super
22
- end
23
- end
24
- end
@@ -1,18 +0,0 @@
1
- class HttpRouter
2
- module Rack
3
- autoload :URLMap, 'http_router/rack/url_map'
4
- autoload :Builder, 'http_router/rack/buidler'
5
-
6
- # Monkey-patches Rack::Builder to use HttpRouter.
7
- # See examples/rack_mapper.rb
8
- def self.override_rack_builder!
9
- ::Rack.class_eval("OriginalBuilder = Builder; HttpRouterBuilder = HttpRouter::Rack::Builder; remove_const :Builder; Builder = HttpRouterBuilder")
10
- end
11
-
12
- # Monkey-patches Rack::URLMap to use HttpRouter.
13
- # See examples/rack_mapper.rb
14
- def self.override_rack_urlmap!
15
- ::Rack.class_eval("OriginalURLMap = URLMap; HttpRouterURLMap = HttpRouter::Rack::URLMap; remove_const :URLMap; URLMap = HttpRouterURLMap")
16
- end
17
- end
18
- end
@@ -1,60 +0,0 @@
1
- # Replacement for {Rack::Builder} which using HttpRouter to map requests instead of a simple Hash.
2
- # As well, add convenience methods for the request methods.
3
- class HttpRouter::Rack::Builder < ::Rack::Builder
4
- def initialize(&block)
5
- super
6
- end
7
-
8
- def router
9
- @router ||= HttpRouter.new
10
- end
11
-
12
- # Maps a path to a block.
13
- # @param path [String] Path to map to.
14
- # @param options [Hash] Options for added path.
15
- # @see HttpRouter#add
16
- def map(path, options = nil, &block)
17
- router.add(path).with_options(options).to(&block)
18
- @ins << router unless @ins.last == router
19
- end
20
-
21
- # Maps a path with request methods `HEAD` and `GET` to a block.
22
- # @param path [String] Path to map to.
23
- # @param options [Hash] Options for added path.
24
- # @see HttpRouter#add
25
- def get(path, options = nil, &block)
26
- router.get(path).with_options(options).to(&block)
27
- end
28
-
29
- # Maps a path with request methods `POST` to a block.
30
- # @param path [String] Path to map to.
31
- # @param options [Hash] Options for added path.
32
- # @see HttpRouter#add
33
- def post(path, options = nil, &block)
34
- router.post(path).with_options(options).to(&block)
35
- end
36
-
37
- # Maps a path with request methods `PUT` to a block.
38
- # @param path [String] Path to map to.
39
- # @param options [Hash] Options for added path.
40
- # @see HttpRouter#add
41
- def put(path, options = nil, &block)
42
- router.put(path).with_options(options).to(&block)
43
- end
44
-
45
- # Maps a path with request methods `DELETE` to a block.
46
- # @param path [String] Path to map to.
47
- # @param options [Hash] Options for added path.
48
- # @see HttpRouter#add
49
- def delete(path, options = nil, &block)
50
- router.delete(path).with_options(options).to(&block)
51
- end
52
-
53
- # Maps a path with request methods `HEAD` to a block.
54
- # @param path [String] Path to map to.
55
- # @param options [Hash] Options for added path.
56
- # @see HttpRouter#add
57
- def head(path, options = nil, &block)
58
- router.head(path).with_options(options).to(&block)
59
- end
60
- end
@@ -1,10 +0,0 @@
1
- class HttpRouter::Rack::URLMap < ::Rack::URLMap
2
- def initialize(map = {})
3
- @router = HttpRouter.new
4
- map.each { |path, app| (path =~ /^(https?):\/\/(.*?)(\/.*)/ ? @router.add($3).host($2).scheme($1) : @router.add(path)).partial.to(app) }
5
- end
6
-
7
- def call(env)
8
- @router.call(env)
9
- end
10
- end
@@ -1,36 +0,0 @@
1
- class HttpRouter
2
- class Root < Node
3
- class AlternativeRequestMethods < Array
4
- attr_accessor :request_method_found
5
- end
6
-
7
- def add_path(path)
8
- node = path.parts.inject(self) { |node, part| node.add(part) }
9
- node
10
- end
11
-
12
- def recognize(request)
13
- call(request, :nocall)
14
- end
15
-
16
- def call(request, action = :call)
17
- request = ::Rack::Request.new(request) if request.is_a?(Hash)
18
- catch(:response) { find_on_parts(request, get_parts(request), action) } || construct_unmatched(request)
19
- end
20
-
21
- def construct_unmatched(request)
22
- alternate_methods = (router.request_methods_specified - [request.request_method]).select do |alternate_method|
23
- test_request = ::Rack::Request.new(request.env.dup)
24
- test_request.env['REQUEST_METHOD'] = alternate_method
25
- catch(:response) { find_on_parts(test_request, get_parts(request), :nocall) }
26
- end
27
- alternate_methods.empty? ? nil : ::Rack::Response.new("Method not found", 405, {"Allow" => alternate_methods.join(", ")}).finish
28
- end
29
-
30
- def get_parts(request)
31
- parts = router.split(request.path_info)
32
- parts << '' if request.path_info.size > 1 && request.path_info[-1] == ?/
33
- parts
34
- end
35
- end
36
- end
@@ -1,5 +0,0 @@
1
- class HttpRouter
2
- class Static < Regexp
3
- attr_accessor :priority
4
- end
5
- end
@@ -1,30 +0,0 @@
1
- class HttpRouter
2
- class Variable
3
- attr_reader :name, :matches_with
4
- attr_accessor :priority
5
-
6
- def initialize(router, name, matches_with = nil, priority = 0)
7
- @router, @name, @matches_with, @priority = router, name, matches_with, priority
8
- end
9
-
10
- def matches?(parts)
11
- @matches_with.nil? or (@matches_with and match = @matches_with.match(parts.whole_path) and match.begin(0) == 0) ? match : nil
12
- end
13
-
14
- def consume(match, parts)
15
- if @matches_with
16
- parts.replace(router.split(parts.whole_path[match.end(0), parts.whole_path.size]))
17
- match[0]
18
- else
19
- parts.shift
20
- end
21
- end
22
-
23
- def ===(part)
24
- @matches_with.nil?
25
- end
26
-
27
- protected
28
- attr_reader :router
29
- end
30
- end
@@ -1,168 +0,0 @@
1
- require 'spec_helper'
2
- require "sinatra"
3
- require "http_router/interface/sinatra"
4
-
5
- describe "HttpRouter (for Sinatra) route recognition" do
6
- before(:each) do
7
- @app = Sinatra.new { register HttpRouter::Interface::Sinatra::Extension }
8
- @app.extend(CallWithMockRequestMixin)
9
- @app.reset!
10
- end
11
-
12
- describe "basic functionality" do
13
- it "should map not found" do
14
- response = @app.call_with_mock_request('/bar')
15
- response.status.should == 404
16
- end
17
-
18
- it "should map index" do
19
- @app.get("/") { "index" }
20
- response = @app.call_with_mock_request('/')
21
- response.status.should == 200
22
- response.body.should == "index"
23
- end
24
-
25
- it "should ignore trailing delimiters" do
26
- @app.get("/foo") { "foo" }
27
- response = @app.call_with_mock_request('/foo')
28
- response.status.should == 200
29
- response.body.should == "foo"
30
- response = @app.call_with_mock_request('/foo/')
31
- response.status.should == 200
32
- response.body.should == "foo"
33
- end
34
-
35
- it "should ignore trailing delimiters in a more advanced route" do
36
- @app.get("/foo") { "foo" }
37
- @app.get("/foo/bar") { "bar" }
38
- response = @app.call_with_mock_request('/foo')
39
- response.status.should == 200
40
- response.body.should == "foo"
41
- response = @app.call_with_mock_request('/foo/bar')
42
- response.status.should == 200
43
- response.body.should == "bar"
44
- response = @app.call_with_mock_request('/foo/')
45
- response.status.should == 200
46
- response.body.should == "foo"
47
- response = @app.call_with_mock_request('/foo/bar/')
48
- response.status.should == 200
49
- response.body.should == "bar"
50
- end
51
-
52
- it "should ignore trailing delimiters with an optional param" do
53
- @app.get("/foo/(:bar)") { params[:bar] }
54
- @app.get("/bar(/:foo)") { params[:foo] }
55
- response = @app.call_with_mock_request('/foo/bar')
56
- response.status.should == 200
57
- response.body.should == "bar"
58
- response = @app.call_with_mock_request('/bar/foo')
59
- response.status.should == 200
60
- response.body.should == "foo"
61
- response = @app.call_with_mock_request('/bar')
62
- response.status.should == 200
63
- response.body.should == ""
64
- response = @app.call_with_mock_request('/bar/')
65
- response.status.should == 200
66
- response.body.should == ""
67
- end
68
-
69
- it "should use sinatra optionals trailing delimiters" do
70
- @app.get("/foo/?") { "foo" }
71
- response = @app.call_with_mock_request('/foo')
72
- response.status.should == 200
73
- response.body.should == "foo"
74
- response = @app.call_with_mock_request('/foo/')
75
- response.status.should == 200
76
- response.body.should == "foo"
77
- end
78
- end
79
-
80
- describe "mapping functionality" do
81
-
82
- it "should map a basic route" do
83
- @app.get('/hi', :name => :hi) { generate(:hi) }
84
- response = @app.call_with_mock_request('/hi')
85
- response.status.should == 200
86
- response.body.should == "/hi"
87
- end
88
-
89
- it "should map a basic route ignoring trailing delimiters" do
90
- @app.get('/hi', :name => :hi) { generate(:hi) }
91
- response = @app.call_with_mock_request('/hi/')
92
- response.status.should == 200
93
- response.body.should == "/hi"
94
- end
95
-
96
- it "should map a basic route with params" do
97
- @app.get('/hi/:id', :name => :hi) { generate(:hi, :id => 18) }
98
- response = @app.call_with_mock_request('/hi/1')
99
- response.status.should == 200
100
- response.body.should == "/hi/18"
101
- end
102
-
103
- it "should map route with params" do
104
- @app.get('/hi-:id', :name => :hi) { generate(:hi, :id => 18) }
105
- response = @app.call_with_mock_request('/hi-1')
106
- response.status.should == 200
107
- response.body.should == "/hi-18"
108
- end
109
-
110
- it "should map route with complex params" do
111
- @app.get('/hi/:foo/:bar/:baz(.:format)') { "/#{params[:foo]}/#{params[:bar]}/#{params[:baz]}/#{params[:format]}" }
112
- response = @app.call_with_mock_request('/hi/foo/bar/baz')
113
- response.status.should == 200
114
- response.body.should == "/foo/bar/baz/"
115
- response = @app.call_with_mock_request('/hi/foo/bar-bax/baz')
116
- response.status.should == 200
117
- response.body.should == "/foo/bar-bax/baz/"
118
- end
119
- end
120
-
121
- describe "matching by regexp" do
122
- before :each do
123
- @app.get('/numbers/:digits', :matching => { :digits => /\d+/ }) { params[:digits] }
124
- end
125
-
126
- describe "when regexp is matched" do
127
- before :each do
128
- @response = @app.call_with_mock_request('/numbers/2010')
129
- end
130
-
131
- it "should map successfully" do
132
- @response.status.should == 200
133
- @response.body.should == "2010"
134
- end
135
- end
136
-
137
- describe "when regexp is not matched" do
138
- before :each do
139
- @response = @app.call_with_mock_request('/numbers/boobs')
140
- end
141
-
142
- it "should not map" do
143
- @response.status.should == 404
144
- end
145
- end
146
- end
147
-
148
- describe "not found" do
149
-
150
- it "should correctly generate a not found page without images" do
151
- response = @app.call_with_mock_request('/bar')
152
- response.status.should == 404
153
- response.body.should_not match(/__sinatra__/)
154
- end
155
- end
156
-
157
- describe "method not allowed" do
158
-
159
- it "should correctly generate a not found page without images and return a 405" do
160
- @app.post('/bar') { 'found' }
161
- @app.put('/bar') { 'found' }
162
- response = @app.call_with_mock_request('/bar')
163
- response.status.should == 405
164
- response.headers['Allow'].should == 'POST, PUT'
165
- response.body.should_not match(/__sinatra__/)
166
- end
167
- end
168
- end