diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-06-19 09:24:49 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-06-19 09:24:49 +0000 |
| commit | b606d97195187bdb33e334a7a40df501b30e2f48 (patch) | |
| tree | 487a09938f408e02757846d62796b582b0902ce3 | |
| parent | 65697f35f2670fc93e695a7ddf083320f8a11f1a (diff) | |
| download | pyramid-b606d97195187bdb33e334a7a40df501b30e2f48.tar.gz pyramid-b606d97195187bdb33e334a7a40df501b30e2f48.tar.bz2 pyramid-b606d97195187bdb33e334a7a40df501b30e2f48.zip | |
- A new ZCML directive was added named ``notfound``. This ZCML
directive can be used to name a view that should be invoked when the
request can't otherwise be resolved to a view callable. For example::
<notfound
view="helloworld.views.notfound_view"/>
- A new ZCML directive was added named ``forbidden``. This ZCML
directive can be used to name a view that should be invoked when a
view callable for a request is found, but cannot be invoked due to
an authorization failure. For example::
<forbidden
view="helloworld.views.forbidden_view"/>
| -rw-r--r-- | CHANGES.txt | 31 | ||||
| -rw-r--r-- | repoze/bfg/includes/meta.zcml | 12 | ||||
| -rw-r--r-- | repoze/bfg/router.py | 6 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_router.py | 2 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 60 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 30 |
6 files changed, 135 insertions, 6 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 476048dc5..43ea07b32 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,21 @@ Next release Features -------- +- A new ZCML directive was added named ``notfound``. This ZCML + directive can be used to name a view that should be invoked when the + request can't otherwise be resolved to a view callable. For example:: + + <notfound + view="helloworld.views.notfound_view"/> + +- A new ZCML directive was added named ``forbidden``. This ZCML + directive can be used to name a view that should be invoked when a + view callable for a request is found, but cannot be invoked due to + an authorization failure. For example:: + + <forbidden + view="helloworld.views.forbidden_view"/> + - Allow views to be *optionally* defined as callables that accept only a request object, instead of both a context and a request (which still works, and always will). The following types work as views in @@ -81,6 +96,19 @@ Features contain a key ``bfg.routes.route`` (the Route object which matched), and a key ``bfg.routes.matchdict`` (the result of calling route.match). +Deprecations +------------ + +- Utility registrations against + ``repoze.bfg.interfaces.INotFoundView`` and + ``repoze.bfg.interfaces.IForbiddenView`` are now deprecated. Use + the ``notfound`` and ``forbidden`` ZCML directives instead (see the + "Hooks" chapter for more information). Such registrations will + continue to work, but the notfound and forbidden directives do + "extra work" to ensure that the callable named by the directive can + be called by the router even if it's a class or + request-argument-only view. + Removals -------- @@ -136,6 +164,9 @@ Bug Fixes Documentation ------------- +- A "router" chapter explaining the request/response lifecycle at a + high level was added. + - Replaced all mentions and explanations of a routes "context factory" with equivalent explanations of a "root factory" (context factories have been disused). diff --git a/repoze/bfg/includes/meta.zcml b/repoze/bfg/includes/meta.zcml index 83fb48e87..36129c5e3 100644 --- a/repoze/bfg/includes/meta.zcml +++ b/repoze/bfg/includes/meta.zcml @@ -16,6 +16,18 @@ handler="repoze.bfg.zcml.scan" /> + <meta:directive + name="notfound" + schema="repoze.bfg.zcml.INotFoundViewDirective" + handler="repoze.bfg.zcml.notfound" + /> + + <meta:directive + name="forbidden" + schema="repoze.bfg.zcml.IForbiddenViewDirective" + handler="repoze.bfg.zcml.forbidden" + /> + </meta:directives> <meta:groupingDirective diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py index d85f599b2..145e0ad7c 100644 --- a/repoze/bfg/router.py +++ b/repoze/bfg/router.py @@ -99,13 +99,13 @@ class Router(object): warning = ( 'Instead of registering a utility against the ' 'repoze.bfg.interfaces.INotFoundAppFactory interface ' - 'to return a custom notfound response, you should register ' - 'a repoze.bfg.interfaces.INotFoundView. The ' + 'to return a custom notfound response, you should use the ' + '"notfound_view" ZCML directive. The ' 'INotFoundAppFactory interface was deprecated in' 'repoze.bfg 0.9 and will be removed in a subsequent version ' 'of repoze.bfg. See the "Hooks" chapter of the repoze.bfg ' 'documentation for more information about ' - 'INotFoundView.') + 'the "notfound_view" directive.') self.logger and self.logger.warn(warning) def notfound(context, request): app = notfound_app_factory() diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py index 195d1bba7..1049fa752 100644 --- a/repoze/bfg/tests/test_router.py +++ b/repoze/bfg/tests/test_router.py @@ -184,7 +184,7 @@ class RouterTests(unittest.TestCase): self.registry.registerUtility(factory, INotFoundAppFactory) router = self._makeOne() self.assertEqual(len(logger.messages), 1) - self.failUnless('INotFoundView' in logger.messages[0]) + self.failUnless('notfound_view' in logger.messages[0]) class DummyRequest: def get_response(self, app): return app diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index d537cf581..7fe33e56b 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -520,6 +520,62 @@ class TestViewDirective(unittest.TestCase): self.assertRaises(ConfigurationError, self._callFUT, context, 'repoze.view', None, view, '', None, 'foo') +class TestNotFoundDirective(unittest.TestCase): + def _callFUT(self, context, view): + from repoze.bfg.zcml import notfound + return notfound(context, view) + + def test_it(self): + context = DummyContext() + def view(request): + return 'OK' + self._callFUT(context, view) + actions = context.actions + from repoze.bfg.interfaces import INotFoundView + from repoze.bfg.zcml import handler + + self.assertEqual(len(actions), 1) + + regadapt = actions[0] + regadapt_discriminator = ('notfound_view',) + self.assertEqual(regadapt['discriminator'], regadapt_discriminator) + self.assertEqual(regadapt['callable'], handler) + self.assertEqual(regadapt['args'][0], 'registerUtility') + derived_view = regadapt['args'][1] + self.assertEqual(derived_view(None, None), 'OK') + self.assertEqual(derived_view.__name__, view.__name__) + self.assertEqual(regadapt['args'][2], INotFoundView) + self.assertEqual(regadapt['args'][3], '') + self.assertEqual(regadapt['args'][4], None) + +class TestForbiddenDirective(unittest.TestCase): + def _callFUT(self, context, view): + from repoze.bfg.zcml import forbidden + return forbidden(context, view) + + def test_it(self): + context = DummyContext() + def view(request): + return 'OK' + self._callFUT(context, view) + actions = context.actions + from repoze.bfg.interfaces import IForbiddenView + from repoze.bfg.zcml import handler + + self.assertEqual(len(actions), 1) + + regadapt = actions[0] + regadapt_discriminator = ('notfound_view',) + self.assertEqual(regadapt['discriminator'], regadapt_discriminator) + self.assertEqual(regadapt['callable'], handler) + self.assertEqual(regadapt['args'][0], 'registerUtility') + derived_view = regadapt['args'][1] + self.assertEqual(derived_view(None, None), 'OK') + self.assertEqual(derived_view.__name__, view.__name__) + self.assertEqual(regadapt['args'][2], IForbiddenView) + self.assertEqual(regadapt['args'][3], '') + self.assertEqual(regadapt['args'][4], None) + class TestDeriveView(unittest.TestCase): def _callFUT(self, view): from repoze.bfg.zcml import derive_view @@ -787,7 +843,7 @@ class TestConnectRouteFunction(unittest.TestCase): 'conditions':{'sub_domain':['a', 'b']} }) -class TestRoute(unittest.TestCase): +class TestRouteDirective(unittest.TestCase): def setUp(self): cleanUp() @@ -978,7 +1034,7 @@ class TestBFGViewFunctionGrokker(unittest.TestCase): actions = context.actions self.assertEqual(len(actions), 0) -class TestZCMLScanFunction(unittest.TestCase): +class TestZCMLScanDirective(unittest.TestCase): def setUp(self): cleanUp() diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index e53a359da..1b66839d6 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -20,6 +20,8 @@ from zope.schema import TextLine from repoze.bfg.interfaces import IRoutesMapper from repoze.bfg.interfaces import IViewPermission +from repoze.bfg.interfaces import INotFoundView +from repoze.bfg.interfaces import IForbiddenView from repoze.bfg.interfaces import IView from repoze.bfg.request import DEFAULT_REQUEST_FACTORIES @@ -84,6 +86,20 @@ def view( derived_view, (for_, request_type), IView, name, _context.info), ) +def view_utility(_context, view, iface): + derived_view = derive_view(view) + _context.action( + discriminator = ('notfound_view',), + callable = handler, + args = ('registerUtility', derived_view, iface, '', _context.info), + ) + +def notfound(_context, view): + view_utility(_context, view, INotFoundView) + +def forbidden(_context, view): + view_utility(_context, view, IForbiddenView) + def derive_view(view): derived_view = view if inspect.isclass(view): @@ -316,6 +332,20 @@ class IViewDirective(Interface): title = u'The route that must match for this view to be used', required = False) +class INotFoundViewDirective(Interface): + view = GlobalObject( + title=u"", + description=u"The notfound view callable", + required=True, + ) + +class IForbiddenViewDirective(Interface): + view = GlobalObject( + title=u"", + description=u"The forbidden view callable", + required=True, + ) + class IRouteRequirementDirective(Interface): """ The interface for the ``requirement`` route subdirective """ attr = TextLine(title=u'attr', required=True) |
