http_router 0.5.4 → 0.6.0

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.
@@ -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