Permalink
Browse files

continue support, more harmony between recognize/cal

  • Loading branch information...
1 parent d9b8823 commit e179967bdef022db97dbd85a9f828797730e08d2 Josh Hull committed Mar 17, 2011
View
@@ -20,7 +20,6 @@ class HttpRouter
def initialize(opts = nil, &blk)
reset!
@ignore_trailing_slash = opts && opts.key?(:ignore_trailing_slash) ? opts[:ignore_trailing_slash] : true
- @handle_unavailable_route = Proc.new{ raise UngeneratableRouteException }
instance_eval(&blk) if blk
end
@@ -53,19 +52,15 @@ def recognize(env)
def call(env, perform_call = true)
rack_request = Rack::Request.new(env)
request = Request.new(rack_request.path_info, rack_request, perform_call)
- response = catch(:success) {
- @root[request]
- }
- response = request.matched_paths if !perform_call && !request.matched_paths.empty?
- if !response && env['router.request_miss']
+ response = catch(:success) { @root[request] }
+ if !response
supported_methods = (@known_methods - [env['REQUEST_METHOD']]).select do |m|
test_env = Rack::Request.new(rack_request.env.clone)
test_env.env['REQUEST_METHOD'] = m
- request = Request.new(test_env.path_info, Rack::Request.new(test_env), false)
- @root[request]
- request.matched_paths
+ test_request = Request.new(test_env.path_info, test_env, false)
+ catch(:success) { @root[test_request] }
end
- [405, {'Allow' => supported_methods.sort.join(", ")}, []]
+ supported_methods.empty? ? @default_app.call(env) : [405, {'Allow' => supported_methods.sort.join(", ")}, []]
elsif response
response
else
@@ -78,13 +73,14 @@ def reset!
@default_app = Proc.new{ |env| Rack::Response.new("Your request couldn't be found", 404).finish }
@routes = []
@named_routes = {}
- @known_methods = Set.new
+ @known_methods = ['GET', "POST", "PUT", "DELETE"]
end
def url(route, *args)
case route
when Symbol then url(@named_routes[route], *args)
when Route then route.url(*args)
+ else raise UngeneratableRouteException
end
end
View
@@ -61,12 +61,12 @@ def destination(request_obj, match_partially = true)
request(request_obj)
arbitrary(request_obj)
if match_partially or request_obj.path.empty?
- @destination && @destination.each do |d|
+ @destination && @destination.each do |d|
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 == '')
if request_obj.perform_call
env = request_obj.rack_request.dup.env
env['router.params'] ||= {}
- env['router.params'].merge!(Hash[d.param_names.zip(request_obj.params)])
+ env['router.params'].merge!(d.hashify_params(request_obj.params))
matched = if d.route.match_partially?
env['PATH_INFO'] = "/#{request_obj.path.join('/')}"
env['SCRIPT_NAME'] += request_obj.rack_request.path_info[0, request_obj.rack_request.path_info.size - env['PATH_INFO'].size]
@@ -76,7 +76,7 @@ def destination(request_obj, match_partially = true)
end
throw :success, d.route.dest.call(env)
else
- (request_obj.matched_paths ||= []) << Response.new(request_obj, d)
+ throw :success, Response.new(request_obj, d)
end
end
end
@@ -7,8 +7,9 @@ def initialize(router, blk, param_names)
def [](request)
request = request.clone
- params = Hash[@param_names.zip(request.params)]
- @blk.call(request.rack_request, params) and super(request)
+ request.continue = proc { |state| destination(request) if state }
+ params = @param_names.nil? ? {} : Hash[@param_names.zip(request.params)]
+ @blk.call(request, params)
end
end
end
@@ -1,6 +1,7 @@
class HttpRouter
class Node
class FreeRegex < Node
+ attr_reader :matcher
def initialize(router, matcher)
@router, @matcher = router, matcher
end
@@ -10,7 +11,7 @@ def [](request)
if match = @matcher.match(whole_path)
request = request.clone
request.extra_env['router.regex_match'] = match
- destination(request)
+ super
end
end
end
@@ -2,7 +2,7 @@ class HttpRouter
class Node
class Request < Node
def self.request_methods
- [:host, :request_method, :scheme]
+ [:host, :request_method, :scheme, :user_agent]
end
def initialize(router)
@@ -30,15 +30,14 @@ def add_linear(matcher)
def [](request)
matched = false
if @request_method
- val = request.rack_request.send(@request_method)
- @linear.each { |(matcher, node)| matched = true; node[request] if matcher === val }
+ val = request.rack_request.send(@request_method.to_sym)
+ @linear.each { |(matcher, node)| node[request] if matcher === val }
@lookup[val][request] if @lookup.key?(val)
@catchall[request] if @catchall
matched = @lookup.key?(val) || !@catchall.nil?
else
super(request)
end
- request.rack_request.env['router.request_miss'] = 'true' if @request_method == :request_method && !matched
end
end
end
View
@@ -45,6 +45,10 @@ def url(args, options)
[path, options]
end
+ def original_path
+ @path
+ end
+
private
def raw_url(args,options)
raise UngeneratableRouteException
@@ -2,7 +2,10 @@ class HttpRouter
class RegexRoute < Route
def initialize(router, path, opts = {})
@router, @path, @opts = router, path, opts
- @router.root.add_free_match(path).add_destination(Path.new(self, path, []))
+ end
+
+ def compile
+ add_non_path_to_tree(@router.root.add_free_match(path), path, [])
@compiled = true
end
View
@@ -1,16 +1,16 @@
class HttpRouter
class Request
- attr_reader :extra_env, :perform_call
- attr_accessor :path, :params, :rack_request, :matched_paths
+ attr_reader :perform_call
+ attr_accessor :path, :params, :rack_request, :extra_env, :continue
+ alias_method :rack, :rack_request
def initialize(path, rack_request, perform_call)
- @matched_paths = []
+ @rack_request, @perform_call = rack_request, perform_call
@path = (path[0] == ?/ ? path[1, path.size] : path).split(/\//)
@path << '' if path.size > 1 && path[-1] == ?/
- @rack_request, @perform_call = rack_request, perform_call
@extra_env = {}
@params = []
end
-
+
def to_s
"request path, #{path.inspect}"
end
@@ -19,7 +19,7 @@ def clone
dup_obj = super
dup_obj.path = path.dup
dup_obj.params = params.dup
- dup_obj.matched_paths = matched_paths
+ dup_obj.extra_env = extra_env.dup
dup_obj
end
end
@@ -0,0 +1,13 @@
+class HttpRouter
+ class Response < Struct.new(:request, :path)
+ attr_reader :params
+ def initialize(request, path)
+ super(request, path)
+ @params = path.hashify_params(request.params)
+ end
+
+ def param_values
+ request.params
+ end
+ end
+end
View
@@ -1,6 +1,6 @@
class HttpRouter
class Route
- attr_reader :default_values, :matches_with, :router, :path
+ attr_reader :default_values, :matches_with, :router, :path, :conditions
def initialize(router, path, opts = {})
@router = router
@@ -68,6 +68,10 @@ def scheme(scheme)
((@conditions ||= {})[:scheme] ||= []) << scheme; self
end
+ def user_agent(user_agent)
+ ((@conditions ||= {})[:user_agent] ||= []) << user_agent; self
+ end
+
def matching(matchers)
@opts.merge!(matchers)
self
@@ -107,6 +111,12 @@ def delete; request_method('DELETE'); end
def head; request_method('HEAD'); end
def arbitrary(blk = nil, &blk2)
+ arbitrary_with_continue { |req, params|
+ req.continue[(blk || blk2)[req, params]]
+ }
+ end
+
+ def arbitrary_with_continue(blk = nil, &blk2)
(@arbitrary ||= []) << (blk || blk2)
self
end
@@ -215,20 +225,21 @@ def compile
node = node.add_match(Regexp.new("#{regex}$"), capturing_indicies, priority)
end
end
- nodes = if @conditions && !@conditions.empty?
- Array(@conditions[:request_method]).each {|m| @router.known_methods << m} if @conditions[:request_method]
- node.add_request(@conditions)
- else
- [node]
- end
- if @arbitrary && !@arbitrary.empty?
- Array(@arbitrary).each{|a| nodes.map!{|n| n.add_arbitrary(a, param_names)} }
- end
- path_obj = Path.new(self, path, param_names)
- nodes.each{|n| n.add_destination(path_obj)}
- path_obj
+ add_non_path_to_tree(node, path, param_names)
end
@compiled = true
end
+
+ def add_non_path_to_tree(node, path, names)
+ nodes = if @conditions && !@conditions.empty?
+ node.add_request(@conditions)
+ else
+ [node]
+ end
+ @arbitrary.each{|a| nodes.map!{|n| n.add_arbitrary(a, names)} } if @arbitrary
+ path_obj = Path.new(self, path, names)
+ nodes.each{|n| n.add_destination(path_obj)}
+ path_obj
+ end
end
end
View
@@ -1,19 +1,19 @@
class TestArbitrary < MiniTest::Unit::TestCase
def test_match
hello, love80, love8080 = router {
- add('test').arbitrary(Proc.new{|req, params| req.host == 'hellodooly' })
- add("test").arbitrary(Proc.new{|req, params| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 80}
- add("test").arbitrary(Proc.new{|req, params| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 8080}
+ add('test').arbitrary(Proc.new{|req, params| req.rack.host == 'hellodooly' })
+ add("test").arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 80}
+ add("test").arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 8080}
}
assert_route love8080, 'http://lovelove:8080/test'
end
def test_less_specific_node
general, hello, love80, love8080 = router {
add("/test")
- add("/test").arbitrary(Proc.new{|req, params| req.host == 'hellodooly' })
- add("/test").arbitrary(Proc.new{|req, params| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 80}
- add("/test").arbitrary(Proc.new{|req, params| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 8080}
+ add("/test").arbitrary(Proc.new{|req, params| req.rack.host == 'hellodooly' })
+ add("/test").arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 80}
+ add("/test").arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 8080}
}
assert_route general, 'http://lovelove:8081/test'
assert_route hello, 'http://hellodooly:8081/test'
@@ -23,17 +23,17 @@ def test_less_specific_node
def test_match_request
love80, love8080 = router {
- add("/test").get.arbitrary(Proc.new{|req, params| req.host == 'lovelove' }).arbitrary{|req, params| req.port == 80}
- add("/test").get.arbitrary(Proc.new{|req, params| req.host == 'lovelove' }).arbitrary{|req, params| req.port == 8080}
+ add("/test").get.arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 80}
+ add("/test").get.arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 8080}
}
assert_route love80, 'http://lovelove:80/test'
assert_route love8080, 'http://lovelove:8080/test'
end
def test_less_specific_with_request
love80, love8080, general = router {
- add("test").post.arbitrary(Proc.new{|req, params| req.host == 'lovelove' }).arbitrary{|req, params| req.port == 80}
- add("test").post.arbitrary(Proc.new{|req, params| req.host == 'lovelove' }).arbitrary{|req, params| req.port == 8080}
+ add("test").post.arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 80}
+ add("test").post.arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 8080}
add("test").post
}
assert_route love8080, Rack::MockRequest.env_for('http://lovelove:8080/test', :method => :post)
@@ -47,4 +47,12 @@ def test_pass_params
}
assert_route r, '/test', {:test => 'test'}
end
+
+ def test_continue
+ no, yes = router {
+ add('test').arbitrary_with_continue{|req, p| req.continue[false]}
+ add('test').arbitrary_with_continue{|req, p| req.continue[true]}
+ }
+ assert_route yes, '/test'
+ end
end

0 comments on commit e179967

Please sign in to comment.