Merge "Added inheritance for hooks from parent classes"

This commit is contained in:
Jenkins
2014-06-25 21:08:55 +00:00
committed by Gerrit Code Review
3 changed files with 182 additions and 4 deletions

View File

@@ -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
--------------------------

View File

@@ -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(

View File

@@ -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):