Switched over to Rick Copeland's iterative object-dispatch. Proved
everything still works with tests.
This commit is contained in:
8
README
8
README
@@ -3,14 +3,6 @@ much much smaller, with many fewer dependancies.
|
||||
|
||||
TODO
|
||||
====
|
||||
|
||||
* Change to alternate routing code lifted from Rick Copeland's
|
||||
clutch:
|
||||
|
||||
http://code.google.com/p/pyclutch/source/browse/trunk/clutch/controller_lookup.py
|
||||
|
||||
This gives me support for _lookup controllers, which I don't
|
||||
currently have.
|
||||
|
||||
* Switch "hooks" over to Context Managers.
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from templating import renderers
|
||||
from webob import Request, Response, exc
|
||||
from threading import local
|
||||
from routing import lookup_controller
|
||||
|
||||
import string
|
||||
|
||||
@@ -45,59 +46,12 @@ class Pecan(object):
|
||||
'xhtml' : 'text/html',
|
||||
'json' : 'application/json'
|
||||
}.get(format, 'text/html')
|
||||
|
||||
def route(self, node, path):
|
||||
|
||||
curpath = ""
|
||||
nodeconf = {}
|
||||
object_trail = [['root', self.root, nodeconf, curpath]]
|
||||
|
||||
names = [x for x in path.strip('/').split('/') if x] + ['index']
|
||||
iternames = names[:]
|
||||
while iternames:
|
||||
name = iternames[0]
|
||||
objname = name.translate(self.translate)
|
||||
nodeconf = {}
|
||||
subnode = getattr(node, objname, None)
|
||||
name = iternames.pop(0)
|
||||
node = subnode
|
||||
curpath = "/".join((curpath, name))
|
||||
object_trail.append([name, node, nodeconf, curpath])
|
||||
|
||||
# try successive objects (reverse order)
|
||||
num_candidates = len(object_trail) - 1
|
||||
for i in range(num_candidates, -1, -1):
|
||||
name, candidate, nodeconf, curpath = object_trail[i]
|
||||
if candidate is None:
|
||||
continue
|
||||
|
||||
# try a "_route" method on the current leaf.
|
||||
if hasattr(candidate, "_route"):
|
||||
processed_path = object_trail[i][-1]
|
||||
unprocessed_path = object_trail[-1][-1].replace(processed_path, '')
|
||||
return candidate._route(unprocessed_path)
|
||||
|
||||
# try a "_lookup" method on the current leaf.
|
||||
if hasattr(candidate, "_lookup"):
|
||||
lookup = candidate._lookup(object_trail[i+1][0])
|
||||
processed_path = object_trail[i+1][-1]
|
||||
unprocessed_path = object_trail[-2][-1].replace(processed_path, '')
|
||||
return self.route(lookup, unprocessed_path)
|
||||
|
||||
# try a "_default" method on the current leaf.
|
||||
if hasattr(candidate, "_default"):
|
||||
defhandler = candidate._default
|
||||
if getattr(defhandler, 'exposed', False):
|
||||
object_trail.insert(i+1, ["_default", defhandler, {}, curpath])
|
||||
return defhandler
|
||||
|
||||
# try the current leaf.
|
||||
if getattr(candidate, 'exposed', False):
|
||||
return candidate
|
||||
|
||||
# we didn't find anything
|
||||
return None
|
||||
|
||||
def route(self, node, path):
|
||||
path = path.split('/')[1:]
|
||||
node, remainder = lookup_controller(node, path)
|
||||
return node
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# create the request object
|
||||
state.request = Request(environ)
|
||||
|
||||
60
pecan/routing.py
Normal file
60
pecan/routing.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from webob import exc
|
||||
|
||||
|
||||
def lookup_controller(obj, url_path):
|
||||
remainder = url_path
|
||||
notfound_handlers = []
|
||||
while True:
|
||||
try:
|
||||
obj, remainder = find_object(obj, remainder, notfound_handlers)
|
||||
return obj, remainder
|
||||
except exc.HTTPNotFound:
|
||||
while notfound_handlers:
|
||||
name, obj, remainder = notfound_handlers.pop()
|
||||
if name == '_default':
|
||||
# Notfound handler is, in fact, a controller, so stop
|
||||
# traversal
|
||||
return obj, remainder
|
||||
else:
|
||||
# Notfound handler is an internal redirect, so continue
|
||||
# traversal
|
||||
try:
|
||||
result = obj(*remainder)
|
||||
if result:
|
||||
obj, remainder = result
|
||||
break
|
||||
except TypeError, te:
|
||||
print 'Got exception calling lookup(): %s (%s)' % (te, te.args)
|
||||
else:
|
||||
raise exc.HTTPNotFound
|
||||
|
||||
|
||||
def find_object(obj, remainder, notfound_handlers):
|
||||
while True:
|
||||
if obj is None: raise exc.HTTPNotFound
|
||||
if iscontroller(obj): return obj, remainder
|
||||
|
||||
if remainder and remainder[0] == '':
|
||||
index = getattr(obj, 'index', None)
|
||||
if iscontroller(index): return index, remainder[1:]
|
||||
elif not remainder:
|
||||
index = getattr(obj, 'index', None)
|
||||
if iscontroller(index):
|
||||
return index, remainder[1:] # TODO: why did I have to do this instead?
|
||||
#raise exc.HTTPFound(add_slash=True)
|
||||
|
||||
default = getattr(obj, '_default', None)
|
||||
if iscontroller(default):
|
||||
notfound_handlers.append(('_default', default, remainder))
|
||||
|
||||
lookup = getattr(obj, '_lookup', None)
|
||||
if iscontroller(lookup):
|
||||
notfound_handlers.append(('_lookup', lookup, remainder))
|
||||
|
||||
if not remainder: raise exc.HTTPNotFound
|
||||
next, remainder = remainder[0], remainder[1:]
|
||||
obj = getattr(obj, next, None)
|
||||
|
||||
|
||||
def iscontroller(obj):
|
||||
return getattr(obj, 'exposed', False)
|
||||
@@ -78,8 +78,9 @@ class TestBase(object):
|
||||
def index(self):
|
||||
return '/'
|
||||
|
||||
def _lookup(self, someID):
|
||||
return LookupController(someID)
|
||||
@expose()
|
||||
def _lookup(self, someID, *remainder):
|
||||
return LookupController(someID), remainder
|
||||
|
||||
app = TestApp(Pecan(RootController()))
|
||||
response = app.get('/')
|
||||
@@ -93,10 +94,8 @@ class TestBase(object):
|
||||
response = app.get('/100/name')
|
||||
assert response.status_int == 200
|
||||
assert response.body == '/100/name'
|
||||
|
||||
|
||||
|
||||
|
||||
class TestEngines(object):
|
||||
|
||||
def test_genshi(self):
|
||||
|
||||
Reference in New Issue
Block a user