Adding an on_route hook

This commit is contained in:
Yoann Roman 2011-01-03 15:57:05 -05:00
parent 5db48d30ce
commit 15892e5360
4 changed files with 119 additions and 83 deletions

View File

@ -9,17 +9,20 @@ There is nothing wrong with WSGI Middleware, and actually, it is really easy to
use middleware with Pecan, but it is rather hard (sometimes impossible) to have
access to Pecan's internals.
Hooks offer three ways of dealing with a request: *before*, *after* and
*on_error* . Obviously,
an *after* hook will be triggered once the request has been processed, a *before* hook will
be dealt before anything and a *on_error* will be called when the request
generated an error.
Hooks offer four ways of dealing with a request:
* ``on_route``: called before Pecan attempts to route a request
* ``before``: called after routing but before processing the request
* ``after``: called after a request has been processed
* ``on_error``: called when a request generates an error
Implementation
--------------
They are very easy to plug into Pecan. In the below example we will go through
a simple hooks that will gather some information about the request and then it
a simple hook that will gather some information about the request and then it
will print it out to ``stdout``.
Your hook needs to import ``PecanHook`` which will be used to inherit from.

View File

@ -34,6 +34,9 @@ class HookController(object):
class PecanHook(object):
priority = 100
def on_route(self ,state):
return
def before(self, state):
return
@ -57,7 +60,7 @@ class TransactionHook(PecanHook):
return True
return False
def before(self, state):
def on_route(self, state):
state.request.error = False
if self.is_transactional(state):
state.request.transactional = True

View File

@ -94,16 +94,19 @@ class Pecan(MonitorableProcess):
if not controller.pecan['check_permissions']():
raise exc.HTTPUnauthorized
def determine_hooks(self, controller):
def determine_hooks(self, controller=None):
controller_hooks = []
if controller:
controller_hooks = controller.pecan.get('hooks', [])
return list(
sorted(
chain(controller.pecan.get('hooks', []), self.hooks),
chain(controller_hooks, self.hooks),
lambda x,y: cmp(x.priority, y.priority)
)
)
def handle_hooks(self, hook_type, *args):
if hook_type == 'before':
if hook_type in ['before', 'on_route']:
hooks = state.hooks
else:
hooks = reversed(state.hooks)
@ -146,6 +149,13 @@ class Pecan(MonitorableProcess):
return schema.to_python(to_validate)
def handle_request(self):
# get a sorted list of hooks, by priority (no controller hooks yet)
state.hooks = self.determine_hooks()
# handle "on_route" hooks
self.handle_hooks('on_route', state)
# lookup the controller, respecting content-type as requested
# by the file extension on the URI
path = state.request.path

View File

@ -17,6 +17,9 @@ class TestHooks(object):
return 'Hello, World!'
class SimpleHook(PecanHook):
def on_route(self, state):
run_hook.append('on_route')
def before(self, state):
run_hook.append('before')
@ -31,10 +34,11 @@ class TestHooks(object):
assert response.status_int == 200
assert response.body == 'Hello, World!'
assert len(run_hook) == 3
assert run_hook[0] == 'before'
assert run_hook[1] == 'inside'
assert run_hook[2] == 'after'
assert len(run_hook) == 4
assert run_hook[0] == 'on_route'
assert run_hook[1] == 'before'
assert run_hook[2] == 'inside'
assert run_hook[3] == 'after'
def test_basic_multi_hook(self):
run_hook = []
@ -49,6 +53,9 @@ class TestHooks(object):
def __init__(self, id):
self.id = str(id)
def on_route(self, state):
run_hook.append('on_route'+self.id)
def before(self, state):
run_hook.append('before'+self.id)
@ -65,14 +72,17 @@ class TestHooks(object):
assert response.status_int == 200
assert response.body == 'Hello, World!'
assert len(run_hook) == 7
assert run_hook[0] == 'before1'
assert run_hook[1] == 'before2'
assert run_hook[2] == 'before3'
assert run_hook[3] == 'inside'
assert run_hook[4] == 'after3'
assert run_hook[5] == 'after2'
assert run_hook[6] == 'after1'
assert len(run_hook) == 10
assert run_hook[0] == 'on_route1'
assert run_hook[1] == 'on_route2'
assert run_hook[2] == 'on_route3'
assert run_hook[3] == 'before1'
assert run_hook[4] == 'before2'
assert run_hook[5] == 'before3'
assert run_hook[6] == 'inside'
assert run_hook[7] == 'after3'
assert run_hook[8] == 'after2'
assert run_hook[9] == 'after1'
def test_prioritized_hooks(self):
run_hook = []
@ -87,6 +97,9 @@ class TestHooks(object):
def __init__(self, id):
self.id = str(id)
def on_route(self, state):
run_hook.append('on_route'+self.id)
def before(self, state):
run_hook.append('before'+self.id)
@ -104,16 +117,19 @@ class TestHooks(object):
assert response.status_int == 200
assert response.body == 'Hello, World!'
assert len(run_hook) == 7
assert run_hook[0] == 'before1'
assert run_hook[1] == 'before2'
assert run_hook[2] == 'before3'
assert run_hook[3] == 'inside'
assert run_hook[4] == 'after3'
assert run_hook[5] == 'after2'
assert run_hook[6] == 'after1'
assert len(run_hook) == 10
assert run_hook[0] == 'on_route1'
assert run_hook[1] == 'on_route2'
assert run_hook[2] == 'on_route3'
assert run_hook[3] == 'before1'
assert run_hook[4] == 'before2'
assert run_hook[5] == 'before3'
assert run_hook[6] == 'inside'
assert run_hook[7] == 'after3'
assert run_hook[8] == 'after2'
assert run_hook[9] == 'after1'
for i in range(len(run_hook)): run_hook.pop()
run_hook = []
state.app.hooks[0].priority = 3
state.app.hooks[1].priority = 2
@ -123,14 +139,17 @@ class TestHooks(object):
assert response.status_int == 200
assert response.body == 'Hello, World!'
assert len(run_hook) == 7
assert run_hook[0] == 'before3'
assert run_hook[1] == 'before2'
assert run_hook[2] == 'before1'
assert run_hook[3] == 'inside'
assert run_hook[4] == 'after1'
assert run_hook[5] == 'after2'
assert run_hook[6] == 'after3'
assert len(run_hook) == 10
assert run_hook[0] == 'on_route3'
assert run_hook[1] == 'on_route2'
assert run_hook[2] == 'on_route1'
assert run_hook[3] == 'before3'
assert run_hook[4] == 'before2'
assert run_hook[5] == 'before1'
assert run_hook[6] == 'inside'
assert run_hook[7] == 'after1'
assert run_hook[8] == 'after2'
assert run_hook[9] == 'after3'
def test_transaction_hook(self):
run_hook = []
@ -145,19 +164,6 @@ class TestHooks(object):
def error(self):
return [][1]
class SimpleHook(PecanHook):
def __init__(self, id):
self.id = str(id)
def before(self, state):
run_hook.append('before'+self.id)
def after(self, state):
run_hook.append('after'+self.id)
def on_error(self, state, e):
run_hook.append('error'+self.id)
def gen(event):
return lambda: run_hook.append(event)
@ -180,7 +186,7 @@ class TestHooks(object):
assert run_hook[1] == 'inside'
assert run_hook[2] == 'clear'
for i in range(len(run_hook)): run_hook.pop()
run_hook = []
response = app.post('/')
assert response.status_int == 200
@ -192,7 +198,7 @@ class TestHooks(object):
assert run_hook[2] == 'commit'
assert run_hook[3] == 'clear'
for i in range(len(run_hook)): run_hook.pop()
run_hook = []
try:
response = app.post('/error')
except IndexError:
@ -207,6 +213,9 @@ class TestHooks(object):
run_hook = []
class SimpleHook(PecanHook):
def on_route(self, state):
run_hook.append('on_route')
def before(self, state):
run_hook.append('before')
@ -240,7 +249,7 @@ class TestHooks(object):
assert len(run_hook) == 1
assert run_hook[0] == 'inside'
for i in range(len(run_hook)): run_hook.pop()
run_hook = []
response = app.get('/sub')
assert response.status_int == 200
@ -258,6 +267,9 @@ class TestHooks(object):
def __init__(self, id):
self.id = str(id)
def on_route(self, state):
run_hook.append('on_route'+self.id)
def before(self, state):
run_hook.append('before'+self.id)
@ -288,23 +300,25 @@ class TestHooks(object):
assert response.status_int == 200
assert response.body == 'Hello, World!'
assert len(run_hook) == 3
assert run_hook[0] == 'before1'
assert run_hook[1] == 'inside'
assert run_hook[2] == 'after1'
assert len(run_hook) == 4
assert run_hook[0] == 'on_route1'
assert run_hook[1] == 'before1'
assert run_hook[2] == 'inside'
assert run_hook[3] == 'after1'
for i in range(len(run_hook)): run_hook.pop()
run_hook = []
response = app.get('/sub')
assert response.status_int == 200
assert response.body == 'Inside here!'
assert len(run_hook) == 5
assert run_hook[0] == 'before2'
assert run_hook[1] == 'before1'
assert run_hook[2] == 'inside_sub'
assert run_hook[3] == 'after1'
assert run_hook[4] == 'after2'
assert len(run_hook) == 6
assert run_hook[0] == 'on_route1'
assert run_hook[1] == 'before2'
assert run_hook[2] == 'before1'
assert run_hook[3] == 'inside_sub'
assert run_hook[4] == 'after1'
assert run_hook[5] == 'after2'
def test_hooks_with_validation(self):
run_hook = []
@ -322,6 +336,9 @@ class TestHooks(object):
]
class SimpleHook(PecanHook):
def on_route(self, state):
run_hook.append('on_route')
def before(self, state):
run_hook.append('before')
@ -372,11 +389,12 @@ class TestHooks(object):
))
assert r.status_int == 200
assert r.body == 'False'
assert len(run_hook) == 3
assert run_hook[0] == 'before'
assert run_hook[1] == 'inside'
assert run_hook[2] == 'after'
for i in range(len(run_hook)): run_hook.pop()
assert len(run_hook) == 4
assert run_hook[0] == 'on_route'
assert run_hook[1] == 'before'
assert run_hook[2] == 'inside'
assert run_hook[3] == 'after'
run_hook = []
# test that the hooks get properly run with validation errors
app = TestApp(make_app(RootController(), hooks=[SimpleHook()]))
@ -391,11 +409,12 @@ class TestHooks(object):
))
assert r.status_int == 200
assert r.body == 'True'
assert len(run_hook) == 3
assert run_hook[0] == 'before'
assert run_hook[1] == 'inside'
assert run_hook[2] == 'after'
for i in range(len(run_hook)): run_hook.pop()
assert len(run_hook) == 4
assert run_hook[0] == 'on_route'
assert run_hook[1] == 'before'
assert run_hook[2] == 'inside'
assert run_hook[3] == 'after'
run_hook = []
# test that the hooks get properly run with validation errors
# and an error handler
@ -411,10 +430,11 @@ class TestHooks(object):
))
assert r.status_int == 200
assert r.body == 'errors'
assert len(run_hook) == 5
assert run_hook[0] == 'before'
assert run_hook[1] == 'after'
assert run_hook[2] == 'before'
assert run_hook[3] == 'inside'
assert run_hook[4] == 'after'
for i in range(len(run_hook)): run_hook.pop()
assert len(run_hook) == 7
assert run_hook[0] == 'on_route'
assert run_hook[1] == 'before'
assert run_hook[2] == 'after'
assert run_hook[3] == 'on_route'
assert run_hook[4] == 'before'
assert run_hook[5] == 'inside'
assert run_hook[6] == 'after'