diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-10-10 02:30:50 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-10-10 02:30:50 +0000 |
| commit | ce8f9b5eadf324b4cc3154004bcf9f1f03e7d6c8 (patch) | |
| tree | 6dd39091d8e27561541a9ed8ea4f1d0d54b4298f | |
| parent | 048496dbbbbd6679afca52387ddbea2aefd596dd (diff) | |
| download | pyramid-ce8f9b5eadf324b4cc3154004bcf9f1f03e7d6c8.tar.gz pyramid-ce8f9b5eadf324b4cc3154004bcf9f1f03e7d6c8.tar.bz2 pyramid-ce8f9b5eadf324b4cc3154004bcf9f1f03e7d6c8.zip | |
- The ``notfound`` and ``forbidden`` ZCML directives now accept the
following addtional attributes: ``attr``, ``renderer``, and
``wrapper``. These have the same meaning as they do in the context
of a ZCML ``view`` directive.
| -rw-r--r-- | CHANGES.txt | 5 | ||||
| -rw-r--r-- | docs/narr/hooks.rst | 77 | ||||
| -rw-r--r-- | repoze/bfg/includes/meta.zcml | 4 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 73 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 51 |
5 files changed, 192 insertions, 18 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 65365ba65..a16e41296 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,11 @@ Next release Documentation ------------- +- The ``notfound`` and ``forbidden`` ZCML directives now accept the + following addtional attributes: ``attr``, ``renderer``, and + ``wrapper``. These have the same meaning as they do in the context + of a ZCML ``view`` directive. + - Added a diagram of model graph traversal to the "Traversal" narrative chapter of the documentation. diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 65e21d3f2..42d354c89 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -40,6 +40,44 @@ implements a minimal NotFound view: This error will be different when the ``debug_notfound`` environment setting is true than it is when it is false. +Other available attributes of the ``notfound`` ZCML directive are as +follows: + +attr + + The attribute of the view callable to use if ``__call__`` is not + correct (has the same meaning as in the context of + :ref:`the_view_zcml_directive`; see the description of ``attr`` + there). + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. + +renderer + + This is either a single string term (e.g. ``json``) or a string + implying a path or :term:`resource specification` + (e.g. ``templates/views.pt``) used when the view returns a + non-:term:`response` object. This attribute has the same meaning as + it would in the context of :ref:`the_view_zcml_directive`; see the + description of ``renderer`` there). + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. + +wrapper + + The :term:`view name` (*not* an object dotted name) of another view + declared elsewhere in ZCML (or via the ``@bfg_view`` decorator) + which will receive the response body of this view as the + ``request.wrapped_body`` attribute of its own request, and the + response returned by this view as the ``request.wrapped_response`` + attribute of its own request. This attribute has the same meaning + as it would in the context of :ref:`the_view_zcml_directive`; see + the description of ``wrapper`` there). Note that the wrapper view + *should not* be protected by any permission; behavior is undefined + if it does. + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. + .. _changing_the_forbidden_view: Changing the Forbidden View @@ -86,6 +124,45 @@ code that implements a minimal forbidden view: an alternate forbidden view. For example, it would make sense to return a response with a ``403 Forbidden`` status code. +Other available attributes of the ``forbidden`` ZCML directive are as +follows: + +attr + + The attribute of the view callable to use if ``__call__`` is not + correct (has the same meaning as in the context of + :ref:`the_view_zcml_directive`; see the description of ``attr`` + there). + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. + +renderer + + This is either a single string term (e.g. ``json``) or a string + implying a path or :term:`resource specification` + (e.g. ``templates/views.pt``) used when the view returns a + non-:term:`response` object. This attribute has the same meaning as + it would in the context of :ref:`the_view_zcml_directive`; see the + description of ``renderer`` there). + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. + +wrapper + + The :term:`view name` (*not* an object dotted name) of another view + declared elsewhere in ZCML (or via the ``@bfg_view`` decorator) + which will receive the response body of this view as the + ``request.wrapped_body`` attribute of its own request, and the + response returned by this view as the ``request.wrapped_response`` + attribute of its own request. This attribute has the same meaning + as it would in the context of :ref:`the_view_zcml_directive`; see + the description of ``wrapper`` there). Note that the wrapper view + *should not* be protected by any permission; behavior is undefined + if it does. + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. + + Changing the response factory ----------------------------- diff --git a/repoze/bfg/includes/meta.zcml b/repoze/bfg/includes/meta.zcml index 2eeb87314..4c9d18873 100644 --- a/repoze/bfg/includes/meta.zcml +++ b/repoze/bfg/includes/meta.zcml @@ -18,13 +18,13 @@ <meta:directive name="notfound" - schema="repoze.bfg.zcml.INotFoundViewDirective" + schema="repoze.bfg.zcml.ISystemViewDirective" handler="repoze.bfg.zcml.notfound" /> <meta:directive name="forbidden" - schema="repoze.bfg.zcml.IForbiddenViewDirective" + schema="repoze.bfg.zcml.ISystemViewDirective" handler="repoze.bfg.zcml.forbidden" /> diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index 35584f301..7cbadd1e2 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -947,6 +947,12 @@ class TestViewDirective(unittest.TestCase): self.assertEqual(wrapper, view) class TestNotFoundDirective(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + def _callFUT(self, context, view): from repoze.bfg.zcml import notfound return notfound(context, view) @@ -972,6 +978,12 @@ class TestNotFoundDirective(unittest.TestCase): self.assertEqual(derived_view.__name__, view.__name__) class TestForbiddenDirective(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + def _callFUT(self, context, view): from repoze.bfg.zcml import forbidden return forbidden(context, view) @@ -996,6 +1008,67 @@ class TestForbiddenDirective(unittest.TestCase): self.assertEqual(derived_view(None, None), 'OK') self.assertEqual(derived_view.__name__, view.__name__) +class TestViewUtility(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, context, view, attr, renderer, wrapper, iface): + from repoze.bfg.zcml import view_utility + return view_utility(context, view, attr, renderer, wrapper, iface) + + def test_no_view_no_renderer(self): + from zope.configuration.exceptions import ConfigurationError + context = DummyContext() + self.assertRaises(ConfigurationError, self._callFUT, context, + None, None, None, None, None) + + def test_no_view_with_renderer(self): + from zope.component import getSiteManager + from zope.interface import Interface + from repoze.bfg.interfaces import IRendererFactory + sm = getSiteManager() + def renderer(path): + return lambda *arg: 'OK' + sm.registerUtility(renderer, IRendererFactory, name='dummy') + class IDummy(Interface): + pass + context = DummyContext() + self._callFUT(context, None, None, 'dummy', None, IDummy) + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IDummy) + register = regadapt['callable'] + register() + derived_view = sm.getUtility(IDummy) + request = DummyRequest() + self.assertEqual(derived_view(None, request).body, 'OK') + + def test_template_renderer(self): + from zope.component import getSiteManager + from zope.interface import Interface + from repoze.bfg.interfaces import IRendererFactory + sm = getSiteManager() + def renderer(path): + return lambda *arg: 'OK' + sm.registerUtility(renderer, IRendererFactory, name='.pt') + class IDummy(Interface): + pass + context = DummyContext() + self._callFUT(context, None, None, 'fixtures/minimal.pt', None, IDummy) + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IDummy) + register = regadapt['callable'] + register() + derived_view = sm.getUtility(IDummy) + request = DummyRequest() + self.assertEqual(derived_view(None, request).body, 'OK') + class TestRepozeWho1AuthenticationPolicyDirective(unittest.TestCase): def setUp(self): cleanUp() diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index 3faadb24b..adb1f1f31 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -279,29 +279,49 @@ def view( _view = view # for directives that take a view arg -class INotFoundViewDirective(Interface): +class ISystemViewDirective(Interface): view = GlobalObject( title=u"", - description=u"The notfound view callable", - required=True, + description=u"The view function", + required=False, ) -def notfound(_context, view): - view_utility(_context, view, INotFoundView) + attr = TextLine( + title=u'The callable attribute of the view object(default is __call__)', + description=u'', + required=False) -class IForbiddenViewDirective(Interface): - view = GlobalObject( - title=u"", - description=u"The forbidden view callable", - required=True, - ) + renderer = TextLine( + title=u'The renderer asssociated with the view', + description=u'', + required=False) -def forbidden(_context, view): - view_utility(_context, view, IForbiddenView) + wrapper = TextLine( + title = u'The *name* of the view that acts as a wrapper for this view.', + description = u'', + required=False) + +def notfound(_context, view=None, attr=None, renderer=None, wrapper=None): + view_utility(_context, view, attr, renderer, wrapper, INotFoundView) + +def forbidden(_context, view=None, attr=None, renderer=None, wrapper=None): + view_utility(_context, view, attr, renderer, wrapper, IForbiddenView) + +def view_utility(_context, view, attr, renderer, wrapper, iface): + if not view: + if renderer: + def view(context, request): + return {} + else: + raise ConfigurationError('"view" attribute was not specified and ' + 'no renderer specified') + + if renderer and '.' in renderer: + renderer = resource_spec(renderer, package_name(_context.resolve('.'))) -def view_utility(_context, view, iface): def register(): - derived_view = derive_view(view) + derived_view = derive_view(view, attr=attr, renderer_name=renderer, + wrapper_viewname=wrapper) sm = getSiteManager() sm.registerUtility(derived_view, iface, '', _context.info) @@ -310,7 +330,6 @@ def view_utility(_context, view, iface): callable = register, ) - class IResourceDirective(Interface): """ Directive for specifying that one package may override resources from |
