Merge "Added inheritance for hooks from parent classes"
This commit is contained in:
@@ -130,9 +130,106 @@ when we run the app and browse the application from our web browser.
|
||||
|
||||
about to enter the controller...
|
||||
DO SOMETHING!
|
||||
method: GET
|
||||
response: 200 OK
|
||||
method: GET
|
||||
response: 200 OK
|
||||
|
||||
Hooks can be inherited from parent class or mixins. Just make sure to
|
||||
subclass from :class:`~pecan.hooks.HookController`.
|
||||
|
||||
::
|
||||
|
||||
from pecan import expose
|
||||
from pecan.hooks import PecanHook, HookController
|
||||
|
||||
class ParentHook(PecanHook):
|
||||
|
||||
priority = 1
|
||||
|
||||
def before(self, state):
|
||||
print "\nabout to enter the parent controller..."
|
||||
|
||||
class CommonHook(PecanHook):
|
||||
|
||||
priority = 2
|
||||
|
||||
def before(self, state):
|
||||
print "\njust a common hook..."
|
||||
|
||||
class SubHook(PecanHook):
|
||||
|
||||
def before(self, state):
|
||||
print "\nabout to enter the subcontroller..."
|
||||
|
||||
class SubMixin(object):
|
||||
__hooks__ = [SubHook()]
|
||||
|
||||
# We'll use the same instance for both controllers,
|
||||
# to avoid double calls
|
||||
common = CommonHook()
|
||||
|
||||
class SubController(HookController, SubMixin):
|
||||
|
||||
__hooks__ = [common]
|
||||
|
||||
@expose('json')
|
||||
def index(self):
|
||||
print "\nI AM THE SUB!"
|
||||
return dict()
|
||||
|
||||
class RootController(HookController):
|
||||
|
||||
__hooks__ = [common, ParentHook()]
|
||||
|
||||
@expose('json')
|
||||
def index(self):
|
||||
print "\nI AM THE ROOT!"
|
||||
return dict()
|
||||
|
||||
sub = SubController()
|
||||
|
||||
Let's see what happens when we run the app.
|
||||
First loading the root controller:
|
||||
|
||||
::
|
||||
|
||||
pecan serve config.py
|
||||
serving on 0.0.0.0:8080 view at http://127.0.0.1:8080
|
||||
|
||||
GET / HTTP/1.1" 200
|
||||
|
||||
about to enter the parent controller...
|
||||
|
||||
just a common hook
|
||||
|
||||
I AM THE ROOT!
|
||||
|
||||
Then loading the sub controller:
|
||||
|
||||
::
|
||||
|
||||
pecan serve config.py
|
||||
serving on 0.0.0.0:8080 view at http://127.0.0.1:8080
|
||||
|
||||
GET /sub HTTP/1.1" 200
|
||||
|
||||
about to enter the parent controller...
|
||||
|
||||
just a common hook
|
||||
|
||||
about to enter the subcontroller...
|
||||
|
||||
I AM THE SUB!
|
||||
|
||||
.. note::
|
||||
|
||||
Make sure to set proper priority values for nested hooks in order
|
||||
to get them executed in the desired order.
|
||||
|
||||
.. warning::
|
||||
|
||||
Two hooks of the same type will be added/executed twice, if passed as
|
||||
different instances to a parent and a child controller.
|
||||
If passed as one instance variable - will be invoked once for both controllers.
|
||||
|
||||
Hooks That Come with Pecan
|
||||
--------------------------
|
||||
|
||||
@@ -13,6 +13,10 @@ __all__ = [
|
||||
|
||||
def walk_controller(root_class, controller, hooks):
|
||||
if not isinstance(controller, (int, dict)):
|
||||
for hook in getattr(controller, '__hooks__', []):
|
||||
# Append hooks from controller class definition
|
||||
hooks.add(hook)
|
||||
|
||||
for name, value in getmembers(controller):
|
||||
if name == 'controller':
|
||||
continue
|
||||
@@ -21,7 +25,7 @@ def walk_controller(root_class, controller, hooks):
|
||||
|
||||
if iscontroller(value):
|
||||
for hook in hooks:
|
||||
value._pecan.setdefault('hooks', []).append(hook)
|
||||
value._pecan.setdefault('hooks', set()).add(hook)
|
||||
elif hasattr(value, '__class__'):
|
||||
if name.startswith('__') and name.endswith('__'):
|
||||
continue
|
||||
@@ -36,7 +40,12 @@ class HookControllerMeta(type):
|
||||
'''
|
||||
|
||||
def __init__(cls, name, bases, dict_):
|
||||
walk_controller(cls, cls, dict_.get('__hooks__', []))
|
||||
hooks = set(dict_.get('__hooks__', []))
|
||||
for base in bases:
|
||||
# Add hooks from parent class and mixins
|
||||
for hook in getattr(base, '__hooks__', []):
|
||||
hooks.add(hook)
|
||||
walk_controller(cls, cls, hooks)
|
||||
|
||||
|
||||
HookController = HookControllerMeta(
|
||||
|
||||
@@ -339,6 +339,78 @@ class TestHooks(PecanTestCase):
|
||||
assert run_hook[4] == 'after1'
|
||||
assert run_hook[5] == 'after2'
|
||||
|
||||
def test_mixin_hooks(self):
|
||||
run_hook = []
|
||||
|
||||
class HelperHook(PecanHook):
|
||||
priority = 2
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('helper - before hook')
|
||||
|
||||
# we'll use the same hook instance to avoid duplicate calls
|
||||
helper_hook = HelperHook()
|
||||
|
||||
class LastHook(PecanHook):
|
||||
priority = 200
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('last - before hook')
|
||||
|
||||
class SimpleHook(PecanHook):
|
||||
priority = 1
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('simple - before hook')
|
||||
|
||||
class HelperMixin(object):
|
||||
__hooks__ = [helper_hook]
|
||||
|
||||
class LastMixin(object):
|
||||
__hooks__ = [LastHook()]
|
||||
|
||||
class SubController(HookController, HelperMixin):
|
||||
__hooks__ = [LastHook()]
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
return "This is sub controller!"
|
||||
|
||||
class RootController(HookController, LastMixin):
|
||||
__hooks__ = [SimpleHook(), helper_hook]
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
run_hook.append('inside')
|
||||
return 'Hello, World!'
|
||||
|
||||
sub = SubController()
|
||||
|
||||
papp = make_app(RootController())
|
||||
app = TestApp(papp)
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert response.body == b_('Hello, World!')
|
||||
|
||||
assert len(run_hook) == 4
|
||||
assert run_hook[0] == 'simple - before hook', run_hook[0]
|
||||
assert run_hook[1] == 'helper - before hook', run_hook[1]
|
||||
assert run_hook[2] == 'last - before hook', run_hook[2]
|
||||
assert run_hook[3] == 'inside', run_hook[3]
|
||||
|
||||
run_hook = []
|
||||
response = app.get('/sub/')
|
||||
assert response.status_int == 200
|
||||
assert response.body == b_('This is sub controller!')
|
||||
|
||||
assert len(run_hook) == 4, run_hook
|
||||
assert run_hook[0] == 'simple - before hook', run_hook[0]
|
||||
assert run_hook[1] == 'helper - before hook', run_hook[1]
|
||||
assert run_hook[2] == 'last - before hook', run_hook[2]
|
||||
# LastHook is invoked once again -
|
||||
# for each different instance of the Hook in the two Controllers
|
||||
assert run_hook[3] == 'last - before hook', run_hook[3]
|
||||
|
||||
|
||||
class TestTransactionHook(PecanTestCase):
|
||||
def test_transaction_hook(self):
|
||||
|
||||
Reference in New Issue
Block a user