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.
- data/lib/http_router.rb +58 -210
- data/lib/http_router/node.rb +124 -226
- data/lib/http_router/node/arbitrary.rb +16 -0
- data/lib/http_router/node/free_regex.rb +19 -0
- data/lib/http_router/node/glob.rb +16 -0
- data/lib/http_router/node/glob_regex.rb +9 -0
- data/lib/http_router/node/regex.rb +26 -0
- data/lib/http_router/node/request.rb +44 -0
- data/lib/http_router/node/spanning_regex.rb +16 -0
- data/lib/http_router/node/variable.rb +11 -0
- data/lib/http_router/optional_compiler.rb +8 -15
- data/lib/http_router/path.rb +36 -49
- data/lib/http_router/regex_route.rb +20 -0
- data/lib/http_router/request.rb +26 -0
- data/lib/http_router/response.rb +13 -0
- data/lib/http_router/route.rb +121 -299
- data/lib/http_router/version.rb +1 -1
- data/test/helper.rb +11 -9
- data/test/rack/test_urlmap.rb +9 -9
- data/test/test_arbitrary.rb +18 -10
- data/test/test_misc.rb +28 -28
- data/test/test_mounting.rb +81 -81
- data/test/test_request.rb +6 -0
- data/test/test_trailing_slash.rb +0 -4
- data/test/test_variable.rb +1 -9
- metadata +16 -16
- data/lib/http_router/glob.rb +0 -20
- data/lib/http_router/interface/sinatra.rb +0 -149
- data/lib/http_router/parts.rb +0 -24
- data/lib/http_router/rack.rb +0 -18
- data/lib/http_router/rack/builder.rb +0 -60
- data/lib/http_router/rack/url_map.rb +0 -10
- data/lib/http_router/root.rb +0 -36
- data/lib/http_router/static.rb +0 -5
- data/lib/http_router/variable.rb +0 -30
- data/test/sinatra/recognize_spec.rb +0 -168
- data/test/sinatra/test_recognize.rb +0 -150
data/lib/http_router.rb
CHANGED
@@ -1,243 +1,91 @@
|
|
1
|
-
require 'rack'
|
2
1
|
require 'set'
|
3
|
-
require '
|
2
|
+
require 'rack'
|
4
3
|
require 'http_router/node'
|
5
|
-
require 'http_router/
|
6
|
-
require 'http_router/
|
7
|
-
require 'http_router/static'
|
8
|
-
require 'http_router/glob'
|
4
|
+
require 'http_router/request'
|
5
|
+
require 'http_router/response'
|
9
6
|
require 'http_router/route'
|
10
7
|
require 'http_router/path'
|
8
|
+
require 'http_router/regex_route'
|
11
9
|
require 'http_router/optional_compiler'
|
12
|
-
require 'http_router/parts'
|
13
|
-
require 'http_router/version'
|
14
|
-
require 'http_router/rack'
|
15
10
|
|
16
11
|
class HttpRouter
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
MissingParameterException = Class.new(RuntimeError)
|
21
|
-
# Raised when a Route is generated that isn't valid.
|
22
|
-
InvalidRouteException = Class.new(RuntimeError)
|
23
|
-
# Raised when a Route is not able to be generated due to too many parameters being passed in.
|
24
|
-
TooManyParametersException = Class.new(RuntimeError)
|
25
|
-
# Raised when an already inserted Route has more conditions added.
|
26
|
-
AlreadyCompiledException = Class.new(RuntimeError)
|
27
|
-
# Raised when an ambiguous Route is added. For example, this will be raised if you attempt to add "/foo(/:bar)(/:baz)".
|
28
|
-
AmbiguousRouteException = Class.new(RuntimeError)
|
29
|
-
# Raised when a request condition is added that is not recognized.
|
30
|
-
UnsupportedRequestConditionError = Class.new(RuntimeError)
|
31
|
-
# Raised when there is a potential conflict of variable names within your Route.
|
32
|
-
AmbiguousVariableException = Class.new(RuntimeError)
|
12
|
+
|
13
|
+
attr_reader :root, :routes, :known_methods, :named_routes
|
14
|
+
attr_accessor :default_app
|
33
15
|
|
34
|
-
|
35
|
-
|
16
|
+
UngeneratableRouteException = Class.new(RuntimeError)
|
17
|
+
InvalidRouteException = Class.new(RuntimeError)
|
18
|
+
MissingParameterException = Class.new(RuntimeError)
|
36
19
|
|
37
|
-
|
38
|
-
# Can be called with either <tt>HttpRouter.new(proc{|env| ... }, { .. options .. })</tt> or with the first argument omitted.
|
39
|
-
# If there is a proc first, then it's used as the default app in the case of a non-match.
|
40
|
-
# Supported options are
|
41
|
-
# * :default_app -- Default application used if there is a non-match on #call. Defaults to 404 generator.
|
42
|
-
# * :ignore_trailing_slash -- Ignore a trailing / when attempting to match. Defaults to +true+.
|
43
|
-
# * :redirect_trailing_slash -- On trailing /, redirect to the same path without the /. Defaults to +false+.
|
44
|
-
def initialize(*args, &block)
|
45
|
-
default_app, options = args.first.is_a?(Hash) ? [nil, args.first] : [args.first, args[1]]
|
46
|
-
@options = options
|
47
|
-
@default_app = default_app || options && options[:default_app] || proc{|env| ::Rack::Response.new("Not Found", 404).finish }
|
48
|
-
@ignore_trailing_slash = options && options.key?(:ignore_trailing_slash) ? options[:ignore_trailing_slash] : true
|
49
|
-
@redirect_trailing_slash = options && options.key?(:redirect_trailing_slash) ? options[:redirect_trailing_slash] : false
|
50
|
-
@init_block = block
|
51
|
-
@handle_unavailable_route = Proc.new{ raise UngeneratableRouteException }
|
20
|
+
def initialize(opts = nil, &blk)
|
52
21
|
reset!
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# Ignore trailing slash feature enabled? See #initialize for details.
|
57
|
-
def ignore_trailing_slash?
|
58
|
-
@ignore_trailing_slash
|
59
|
-
end
|
60
|
-
|
61
|
-
# Redirect trailing slash feature enabled? See #initialize for details.
|
62
|
-
def redirect_trailing_slash?
|
63
|
-
@redirect_trailing_slash
|
22
|
+
@ignore_trailing_slash = opts && opts.key?(:ignore_trailing_slash) ? opts[:ignore_trailing_slash] : true
|
23
|
+
instance_eval(&blk) if blk
|
64
24
|
end
|
65
25
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
# Assigns the default application.
|
76
|
-
def default(app)
|
77
|
-
@default_app = app
|
78
|
-
end
|
79
|
-
|
80
|
-
# Adds a path to be recognized.
|
81
|
-
#
|
82
|
-
# To assign a part of the path to a specific variable, use :variable_name within the route.
|
83
|
-
# For example, <tt>add('/path/:id')</tt> would match <tt>/path/test</tt>, with the variable <tt>:id</tt> having the value <tt>"test"</tt>.
|
84
|
-
#
|
85
|
-
# You can receive mulitple parts into a single variable by using the glob syntax.
|
86
|
-
# 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>.
|
87
|
-
#
|
88
|
-
# 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.
|
89
|
-
#
|
90
|
-
# 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.
|
91
|
-
#
|
92
|
-
# 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.
|
93
|
-
#
|
94
|
-
# Returns the route object.
|
95
|
-
def add(path, options = nil)
|
96
|
-
add_route route(path.dup).with_options(options)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Adds a route to be recognized. This must be a HttpRouter::Route object. Returns the route just added.
|
100
|
-
def add_route(route)
|
26
|
+
def add(path, opts = {}, &app)
|
27
|
+
route = case path
|
28
|
+
when Regexp
|
29
|
+
RegexRoute.new(self, path, opts)
|
30
|
+
else
|
31
|
+
Route.new(self, path, opts)
|
32
|
+
end
|
101
33
|
@routes << route
|
34
|
+
route.to(app) if app
|
102
35
|
route
|
103
36
|
end
|
104
37
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
add(path, options).get
|
38
|
+
def add_with_request_method(path, method, opts = {}, &app)
|
39
|
+
route = add(path, opts).send(method.to_sym)
|
40
|
+
route.to(app) if app
|
41
|
+
route
|
110
42
|
end
|
111
43
|
|
112
|
-
|
113
|
-
|
114
|
-
# Returns the route object.
|
115
|
-
def post(path, options = nil)
|
116
|
-
add(path, options).post
|
44
|
+
[:post, :get, :delete, :put, :head].each do |rm|
|
45
|
+
class_eval "def #{rm}(path, opts = {}, &app); add_with_request_method(path, #{rm.inspect}, opts, &app); end", __FILE__, __LINE__
|
117
46
|
end
|
118
47
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
48
|
+
def recognize(env)
|
49
|
+
call(env, false)
|
50
|
+
end
|
51
|
+
|
52
|
+
def call(env, perform_call = true)
|
53
|
+
rack_request = Rack::Request.new(env)
|
54
|
+
request = Request.new(rack_request.path_info, rack_request, perform_call)
|
55
|
+
response = catch(:success) { @root[request] }
|
56
|
+
if !response
|
57
|
+
supported_methods = (@known_methods - [env['REQUEST_METHOD']]).select do |m|
|
58
|
+
test_env = Rack::Request.new(rack_request.env.clone)
|
59
|
+
test_env.env['REQUEST_METHOD'] = m
|
60
|
+
test_request = Request.new(test_env.path_info, test_env, false)
|
61
|
+
catch(:success) { @root[test_request] }
|
62
|
+
end
|
63
|
+
supported_methods.empty? ? @default_app.call(env) : [405, {'Allow' => supported_methods.sort.join(", ")}, []]
|
64
|
+
elsif response
|
65
|
+
response
|
66
|
+
else
|
67
|
+
@default_app.call(env)
|
68
|
+
end
|
124
69
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
70
|
+
|
71
|
+
def reset!
|
72
|
+
@root = Node.new(self)
|
73
|
+
@default_app = Proc.new{ |env| Rack::Response.new("Your request couldn't be found", 404).finish }
|
74
|
+
@routes = []
|
75
|
+
@named_routes = {}
|
76
|
+
@known_methods = ['GET', "POST", "PUT", "DELETE"]
|
131
77
|
end
|
132
78
|
|
133
|
-
# Generate a URL for a specified route. This will accept a list of variable values plus any other variable names named as a hash.
|
134
|
-
# This first value must be either the Route object or the name of the route.
|
135
|
-
#
|
136
|
-
# Example:
|
137
|
-
# router = HttpRouter.new
|
138
|
-
# router.add('/:foo.:format).name(:test).compile
|
139
|
-
# router.url(:test, 123, 'html')
|
140
|
-
# # ==> "/123.html"
|
141
|
-
# router.url(:test, 123, :format => 'html')
|
142
|
-
# # ==> "/123.html"
|
143
|
-
# router.url(:test, :foo => 123, :format => 'html')
|
144
|
-
# # ==> "/123.html"
|
145
|
-
# router.url(:test, :foo => 123, :format => 'html', :fun => 'inthesun')
|
146
|
-
# # ==> "/123.html?fun=inthesun"
|
147
79
|
def url(route, *args)
|
148
80
|
case route
|
149
81
|
when Symbol then url(@named_routes[route], *args)
|
150
82
|
when Route then route.url(*args)
|
151
|
-
|
152
|
-
else
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def url_with_params(route, *args)
|
157
|
-
case route
|
158
|
-
when Symbol then url_with_params(@named_routes[route], *args)
|
159
|
-
when Route then route.url_with_params(*args)
|
160
|
-
when nil then @handle_unavailable_route.call(:url_with_params, *args)
|
161
|
-
else
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# Rack compatible #call. If matching route is found, and +dest+ value responds to #call, processing will pass to the matched route. Otherwise,
|
166
|
-
# the default application will be called. The router will be available in the env under the key <tt>router</tt>. And parameters matched will
|
167
|
-
# be available under the key <tt>router.params</tt>. The HttpRouter::Response object will be available under the key <tt>router.response</tt> if
|
168
|
-
# a response is available.
|
169
|
-
def call(env)
|
170
|
-
request = ::Rack::Request.new(env)
|
171
|
-
if redirect_trailing_slash? && (request.head? || request.get?) && request.path_info[-1] == ?/
|
172
|
-
response = ::Rack::Response.new
|
173
|
-
response.redirect(request.path_info[0, request.path_info.size - 1], 302)
|
174
|
-
response.finish
|
175
|
-
else
|
176
|
-
@root.call(request) || @default_app.call(request.env)
|
83
|
+
else raise UngeneratableRouteException
|
177
84
|
end
|
178
85
|
end
|
179
86
|
|
180
|
-
def
|
181
|
-
@
|
182
|
-
end
|
183
|
-
|
184
|
-
# Returns a new node
|
185
|
-
def node(*args)
|
186
|
-
Node.new(self, *args)
|
187
|
-
end
|
188
|
-
|
189
|
-
# Returns a new request node
|
190
|
-
def request_node(*args)
|
191
|
-
RequestNode.new(self, *args)
|
192
|
-
end
|
193
|
-
|
194
|
-
def arbitrary_node(*args)
|
195
|
-
ArbitraryNode.new(self, *args)
|
196
|
-
end
|
197
|
-
|
198
|
-
# Returns a new variable
|
199
|
-
def variable(*args)
|
200
|
-
Variable.new(self, *args)
|
201
|
-
end
|
202
|
-
|
203
|
-
# Returns a new glob
|
204
|
-
def glob(*args)
|
205
|
-
Glob.new(self, *args)
|
206
|
-
end
|
207
|
-
|
208
|
-
# Returns a new route
|
209
|
-
def route(*args)
|
210
|
-
Route.new(self, *args)
|
211
|
-
end
|
212
|
-
|
213
|
-
# Creates a deep-copy of the router.
|
214
|
-
def clone(klass = self.class)
|
215
|
-
cloned_router = klass.new(@default_app, @options)
|
216
|
-
@routes.each do |route|
|
217
|
-
new_route = route.clone(cloned_router)
|
218
|
-
cloned_router.add_route(new_route).compile
|
219
|
-
new_route.name(route.named) if route.named
|
220
|
-
if route.dest
|
221
|
-
begin
|
222
|
-
new_route.to route.dest.clone
|
223
|
-
rescue
|
224
|
-
new_route.to route.dest
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
cloned_router
|
229
|
-
end
|
230
|
-
|
231
|
-
def split(path)
|
232
|
-
Parts.new(path)
|
233
|
-
end
|
234
|
-
|
235
|
-
def self.uri_escape!(s)
|
236
|
-
s.to_s.gsub!(/([^:\/?\[\]\-_~\.!\$&'\(\)\*\+,;=@a-zA-Z0-9]+)/n) { "%#{$1.unpack('H2'*$1.size).join('%').upcase}" }
|
237
|
-
end
|
238
|
-
|
239
|
-
def self.uri_unescape!(s)
|
240
|
-
s.to_s.gsub!(/((?:%[0-9a-fA-F]{2})+)/n){ [$1.delete('%')].pack('H*') }
|
87
|
+
def ignore_trailing_slash?
|
88
|
+
@ignore_trailing_slash
|
241
89
|
end
|
242
90
|
|
243
91
|
def append_querystring(uri, params)
|
data/lib/http_router/node.rb
CHANGED
@@ -1,265 +1,163 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Node
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@params = path.route.default_values ? path.route.default_values.merge(path.hashify_params(param_values)) : path.hashify_params(param_values)
|
12
|
-
end
|
13
|
-
end
|
3
|
+
autoload :Glob, 'http_router/node/glob'
|
4
|
+
autoload :Variable, 'http_router/node/variable'
|
5
|
+
autoload :Regex, 'http_router/node/regex'
|
6
|
+
autoload :SpanningRegex, 'http_router/node/spanning_regex'
|
7
|
+
autoload :GlobRegex, 'http_router/node/glob_regex'
|
8
|
+
autoload :FreeRegex, 'http_router/node/free_regex'
|
9
|
+
autoload :Arbitrary, 'http_router/node/arbitrary'
|
10
|
+
autoload :Request, 'http_router/node/request'
|
14
11
|
|
15
|
-
|
16
|
-
attr_reader :linear, :lookup, :request_node, :arbitrary_node
|
12
|
+
attr_reader :priority, :router
|
17
13
|
|
18
14
|
def initialize(router)
|
19
15
|
@router = router
|
20
|
-
reset!
|
21
16
|
end
|
22
17
|
|
23
|
-
def
|
24
|
-
|
18
|
+
def [](request)
|
19
|
+
destination(request, false)
|
20
|
+
unless request.path.empty?
|
21
|
+
linear(request)
|
22
|
+
lookup(request)
|
23
|
+
variable(request)
|
24
|
+
glob(request)
|
25
|
+
end
|
26
|
+
destination(request)
|
25
27
|
end
|
26
28
|
|
27
|
-
def
|
28
|
-
|
29
|
-
if val.matches_with
|
30
|
-
add_to_linear(val)
|
31
|
-
else
|
32
|
-
add_to_catchall(val)
|
33
|
-
end
|
34
|
-
elsif val.is_a?(Regexp)
|
35
|
-
add_to_linear(val)
|
36
|
-
else
|
37
|
-
create_lookup
|
38
|
-
@lookup[val] ||= router.node
|
39
|
-
end
|
29
|
+
def linear(request)
|
30
|
+
@linear && @linear.each{|n| n[request]}
|
40
31
|
end
|
41
|
-
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
@
|
46
|
-
else
|
47
|
-
new_node = router.node
|
48
|
-
@linear << [val, new_node]
|
49
|
-
new_node
|
32
|
+
|
33
|
+
def lookup(request)
|
34
|
+
if @lookup && @lookup[request.path.first]
|
35
|
+
request = request.clone
|
36
|
+
@lookup[request.path.shift][request]
|
50
37
|
end
|
51
|
-
@linear.sort!{|a, b| b.first.priority <=> a.first.priority }
|
52
|
-
n
|
53
38
|
end
|
54
|
-
|
55
|
-
def
|
56
|
-
|
57
|
-
if procs && !procs.empty?
|
58
|
-
@arbitrary_node ||= router.arbitrary_node
|
59
|
-
@arbitrary_node.create_linear
|
60
|
-
target = router.node
|
61
|
-
@arbitrary_node.linear << [procs, target]
|
62
|
-
if @value
|
63
|
-
@arbitrary_node.catchall = router.node
|
64
|
-
@arbitrary_node.catchall.value = @value
|
65
|
-
@value = nil
|
66
|
-
end
|
67
|
-
elsif @arbitrary_node
|
68
|
-
target = @arbitrary_node.catchall = router.node
|
69
|
-
end
|
70
|
-
target
|
39
|
+
|
40
|
+
def variable(request)
|
41
|
+
@variable && @variable[request]
|
71
42
|
end
|
72
|
-
|
73
|
-
def
|
74
|
-
|
75
|
-
|
43
|
+
|
44
|
+
def glob(request)
|
45
|
+
@glob && @glob[request]
|
46
|
+
end
|
47
|
+
|
48
|
+
def request(request)
|
49
|
+
@request && @request[request]
|
76
50
|
end
|
77
|
-
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
51
|
+
|
52
|
+
def arbitrary(request)
|
53
|
+
@arbitrary && @arbitrary.each{|n| n[request]}
|
54
|
+
end
|
55
|
+
|
56
|
+
def unescape(val)
|
57
|
+
val.to_s.gsub(/((?:%[0-9a-fA-F]{2})+)/n){ [$1.delete('%')].pack('H*') }
|
58
|
+
end
|
59
|
+
|
60
|
+
def destination(request_obj, match_partially = true)
|
61
|
+
request(request_obj)
|
62
|
+
arbitrary(request_obj)
|
63
|
+
if match_partially or request_obj.path.empty?
|
64
|
+
@destination && @destination.each do |d|
|
65
|
+
if d.route.match_partially? or request_obj.path.empty? or (@router.ignore_trailing_slash? and request_obj.path.size == 1 and request_obj.path.last == '')
|
66
|
+
if request_obj.perform_call
|
67
|
+
env = request_obj.rack_request.dup.env
|
68
|
+
env['router.params'] ||= {}
|
69
|
+
env['router.params'].merge!(d.hashify_params(request_obj.params))
|
70
|
+
matched = if d.route.match_partially?
|
71
|
+
env['PATH_INFO'] = "/#{request_obj.path.join('/')}"
|
72
|
+
env['SCRIPT_NAME'] += request_obj.rack_request.path_info[0, request_obj.rack_request.path_info.size - env['PATH_INFO'].size]
|
73
|
+
else
|
74
|
+
env["PATH_INFO"] = ''
|
75
|
+
env["SCRIPT_NAME"] += request_obj.rack_request.path_info
|
100
76
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
next_node = current_node.dup
|
105
|
-
current_node.reset!
|
106
|
-
current_node.request_method = method
|
107
|
-
current_node.catchall ||= next_node
|
108
|
-
redo
|
77
|
+
throw :success, d.route.dest.call(env)
|
78
|
+
else
|
79
|
+
throw :success, Response.new(request_obj, d)
|
109
80
|
end
|
110
81
|
end
|
111
|
-
current_nodes.flatten!
|
112
|
-
else
|
113
|
-
current_nodes.map!{|n| n.is_a?(RequestNode) && n.request_method == method ? (n.catchall ||= router.request_node) : n}
|
114
82
|
end
|
115
83
|
end
|
116
|
-
transplant_value
|
117
|
-
current_nodes
|
118
84
|
end
|
119
85
|
|
120
|
-
|
121
|
-
|
122
|
-
attr_reader :router
|
123
|
-
|
124
|
-
def transplant_value
|
125
|
-
if @value && @request_node
|
126
|
-
target_node = @request_node
|
127
|
-
while target_node.request_method
|
128
|
-
target_node = (target_node.catchall ||= router.request_node)
|
129
|
-
end
|
130
|
-
target_node.value ||= @value
|
131
|
-
@value = nil
|
132
|
-
end
|
86
|
+
def add_variable
|
87
|
+
@variable ||= Variable.new(@router)
|
133
88
|
end
|
134
|
-
|
135
|
-
def
|
136
|
-
|
137
|
-
val
|
89
|
+
|
90
|
+
def add_glob
|
91
|
+
@glob ||= Glob.new(@router)
|
138
92
|
end
|
139
93
|
|
140
|
-
def
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
nil
|
94
|
+
def add_request(opts)
|
95
|
+
@request ||= Request.new(@router)
|
96
|
+
next_requests = [@request]
|
97
|
+
Request.request_methods.each do |method|
|
98
|
+
next_requests.map! do |next_request|
|
99
|
+
next_request.request_method = method
|
100
|
+
(opts[method].nil? ? [nil] : Array(opts[method])).map do |request_matcher|
|
101
|
+
case request_matcher
|
102
|
+
when nil
|
103
|
+
next_request.add_catchall
|
104
|
+
when String
|
105
|
+
next_request.add_lookup(request_matcher)
|
106
|
+
when Regexp
|
107
|
+
next_request.add_linear(request_matcher)
|
155
108
|
end
|
156
109
|
end
|
157
110
|
end
|
158
|
-
|
159
|
-
match.find_on_parts(request, parts[1, parts.size - 1], action, params)
|
160
|
-
end
|
161
|
-
if catchall
|
162
|
-
dupped_parts, dupped_params = parts.dup, params.dup
|
163
|
-
dupped_params << escape_val(catchall.variable.consume(nil, dupped_parts))
|
164
|
-
catchall.find_on_parts(request, dupped_parts, action, dupped_params)
|
165
|
-
end
|
111
|
+
next_requests.flatten!
|
166
112
|
end
|
167
|
-
|
168
|
-
arbitrary_node.find_on_arbitrary(request, parts, action, params) if arbitrary_node
|
169
|
-
response = process_match(self, parts, params, request, action) if @value
|
170
|
-
nil
|
113
|
+
next_requests
|
171
114
|
end
|
172
115
|
|
173
|
-
def
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
env['PATH_INFO'] = "#{HttpRouter::Parts::SLASH}#{parts && parts.join(HttpRouter::Parts::SLASH)}"
|
187
|
-
env['SCRIPT_NAME'] += request.path_info[0, request.path_info.size - env['PATH_INFO'].size]
|
188
|
-
true
|
189
|
-
elsif (parts and (action == :call_with_trailing_slash) and (router.ignore_trailing_slash? or (parts.size == 1 and parts.first == ''))) or parts.nil? || parts.empty?
|
190
|
-
env["PATH_INFO"] = ''
|
191
|
-
env["SCRIPT_NAME"] += request.path_info
|
192
|
-
true
|
193
|
-
else
|
194
|
-
false
|
195
|
-
end
|
196
|
-
if matched
|
197
|
-
response = path.route.dest.call(env)
|
198
|
-
env['router.last_repsonse'] = response
|
199
|
-
if response.first != 404 and response.first != 410
|
200
|
-
throw :response, response
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end if node.value
|
204
|
-
when :nocall, :nocall_with_trailing_slash
|
205
|
-
responses = node.value.select do |path|
|
206
|
-
matched = if path.route.partially_match?
|
207
|
-
true
|
208
|
-
elsif (parts and (action == :nocall_with_trailing_slash) and (router.ignore_trailing_slash? or (parts.size == 1 and parts.first == ''))) or parts.nil? || parts.empty?
|
209
|
-
true
|
210
|
-
else
|
211
|
-
false
|
116
|
+
def add_arbitrary(blk, param_names)
|
117
|
+
@arbitrary ||= []
|
118
|
+
@arbitrary << Arbitrary.new(@router, blk, param_names)
|
119
|
+
@arbitrary.last
|
120
|
+
end
|
121
|
+
|
122
|
+
def add_match(regexp, matching_indicies = [0], priority = 0)
|
123
|
+
@linear ||= []
|
124
|
+
if priority != 0
|
125
|
+
@linear.each_with_index { |n, i|
|
126
|
+
if priority > (n.priority || 0)
|
127
|
+
@linear[i, 0] = Regex.new(@router, regexp, matching_indicies, priority)
|
128
|
+
return @linear[i]
|
212
129
|
end
|
213
|
-
|
214
|
-
throw :response, responses.map{|r| Response.new(r, params)} unless responses.empty?
|
215
|
-
else
|
216
|
-
raise
|
130
|
+
}
|
217
131
|
end
|
132
|
+
@linear << Regex.new(@router, regexp, matching_indicies, priority)
|
133
|
+
@linear.last
|
218
134
|
end
|
219
135
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
def create_lookup
|
226
|
-
@lookup ||= {}
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
class ArbitraryNode < Node
|
231
|
-
def find_on_arbitrary(request, parts, action, params)
|
232
|
-
next_node = @linear && !@linear.empty? && @linear.find { |(procs, node)|
|
233
|
-
params_hash = node.value ? node.value.first.hashify_params(params) : {}
|
234
|
-
procs.all?{|p| p.call(request, params_hash)}
|
235
|
-
}
|
236
|
-
if next_node
|
237
|
-
process_match(next_node.last, parts, params, request, action)
|
238
|
-
elsif @catchall
|
239
|
-
process_match(@catchall, parts, params, request, action)
|
240
|
-
elsif @value
|
241
|
-
process_match(self, parts, params, request, action)
|
242
|
-
end
|
136
|
+
def add_spanning_match(regexp, matching_indicies = [0])
|
137
|
+
@linear ||= []
|
138
|
+
@linear << SpanningRegex.new(@router, regexp, matching_indicies)
|
139
|
+
@linear.last
|
243
140
|
end
|
244
|
-
end
|
245
141
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
elsif arbitrary_node
|
261
|
-
arbitrary_node.find_on_arbitrary(request, parts, action, params)
|
262
|
-
end
|
142
|
+
def add_free_match(regexp)
|
143
|
+
@linear ||= []
|
144
|
+
@linear << FreeRegex.new(@router, regexp)
|
145
|
+
@linear.last
|
146
|
+
end
|
147
|
+
|
148
|
+
def add_destination(route)
|
149
|
+
@destination ||= []
|
150
|
+
@destination << route
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_lookup(part)
|
154
|
+
@lookup ||= {}
|
155
|
+
@lookup[part] ||= Node.new(@router)
|
263
156
|
end
|
157
|
+
|
158
|
+
def join_whole_path(request)
|
159
|
+
request.path.join('/')
|
160
|
+
end
|
161
|
+
|
264
162
|
end
|
265
|
-
end
|
163
|
+
end
|