summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt11
-rw-r--r--docs/api/events.rst39
-rw-r--r--docs/index.rst1
-rw-r--r--docs/narr/events.rst119
-rw-r--r--docs/narr/views.rst172
-rw-r--r--docs/notes.txt16
-rw-r--r--repoze/bfg/tests/fixtureapp/configure.zcml7
-rw-r--r--repoze/bfg/tests/fixtureapp/views.py5
-rw-r--r--repoze/bfg/tests/test_zcml.py108
-rw-r--r--repoze/bfg/zcml.py61
10 files changed, 334 insertions, 205 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 111be6294..a76a91bc0 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,14 @@
+After 0.2.6
+
+ - Add a ``request_type`` attribute to the available attributes of a
+ ``bfg:view`` configure.zcml element. This attribute will have a
+ value which is a dotted Python path, pointing at an interface. If
+ the request object implements this interface when the view lookup
+ is performed, the appropriate view will be called.
+
+ - Remove "template only" views. These were just confusing and were
+ never documented.
+
0.2.6
- Add event sends for INewRequest and INewResponse. See the
diff --git a/docs/api/events.rst b/docs/api/events.rst
index 25bb9841b..b2cd4a100 100644
--- a/docs/api/events.rst
+++ b/docs/api/events.rst
@@ -9,41 +9,6 @@
.. autoclass:: NewResponse
-You can write *listeners* for these event types and subsequently
-register the listeners to be called when the events occur. For
-example, if you create event listener functions in a ``listeners.py``
-file in your application like so:
-
-.. code-block:: python
- :linenos:
-
- def handle_new_request(event):
- print 'request', event.request
-
- def handle_new_response(event):
- print 'response', event.response
-
-You may configure these functions to be called at the appropriate
-times by adding the following to your application's ``configure.zcml``
-file:
-
-.. code-block:: xml
- :linenos:
-
- <subscriber
- for="repoze.bfg.interfaces.INewRequest"
- handler=".listeners.handle_new_request"
- />
-
- <subscriber
- for="repoze.bfg.interfaces.INewResponse"
- handler=".listeners.handle_new_response"
- />
-
-This causes the functions as to be registered as event listeners
-within the :term:`application registry` . Under this configuration,
-when the application is run, every new request and every response will
-be printed to the console.
-
-The return value of a listener function is ignored.
+See :ref:`events_chapter` for more information about how to register
+code which subscribes to these events.
diff --git a/docs/index.rst b/docs/index.rst
index 3f20d0797..a11ebfe45 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -32,6 +32,7 @@ Narrative documentation in chapter form explaining how to use
narr/templates
narr/models
narr/security
+ narr/events
glossary
Tutorials
diff --git a/docs/narr/events.rst b/docs/narr/events.rst
new file mode 100644
index 000000000..1e69a6f7f
--- /dev/null
+++ b/docs/narr/events.rst
@@ -0,0 +1,119 @@
+.. _events_chapter:
+
+Using Events
+=============
+
+An *event* is an object broadcast by the :mod:`repoze.bfg` framework
+at particularly interesting points during the lifetime of your
+application. You don't need to use, know about, or care about events
+in order to create most :mod:`repoze.bfg` applications, but they can
+be useful when you want to do slightly advanced operations, such as
+"skinning" a site slightly differently based on, for example, the
+hostname used to reach the site.
+
+Events in :mod:`repoze.bfg` are always broadcast by the framework.
+They only become useful when you register a *subscriber*. A
+subscriber is a function that accepts a single argument named `event`:
+
+.. code-block:: python
+ :linenos:
+
+ def mysubscriber(event):
+ print event
+
+The above is a subscriber that simply prints the event to the console
+when it's called.
+
+The mere existence of a subscriber function, however, is not
+sufficient to arrange for it to be called. To arrange for the
+subscriber to be called, you'll need to change your :term:`application
+registry` by modifying your application's ``configure.zcml``. Here's
+an example of a bit of XML you can add to the ``configure.zcml`` file
+which registers the above ``mysubscriber`` function, which we assume
+lives in a ``subscribers.py`` module within your application:
+
+.. code-block:: xml
+ :linenos:
+
+ <subscriber
+ for="repoze.bfg.interfaces.INewRequest"
+ handler=".subscribers.mysubscriber"
+ />
+
+The above example means "every time the :mod:`repoze.bfg` framework
+emits an event object that supplies an ``INewRequest`` interface, call
+the ``mysubscriber`` function with the event object. As you can see,
+subscriptions are made to *interfaces*. The event object sent to a
+subscriber will always have an interface. You can use the interface
+itself to determine what attributes of the event are available.
+
+For example, if you create event listener functions in a
+``subscribers.py`` file in your application like so:
+
+.. code-block:: python
+ :linenos:
+
+ def handle_new_request(event):
+ print 'request', event.request
+
+ def handle_new_response(event):
+ print 'response', event.response
+
+You may configure these functions to be called at the appropriate
+times by adding the following to your application's ``configure.zcml``
+file:
+
+.. code-block:: xml
+ :linenos:
+
+ <subscriber
+ for="repoze.bfg.interfaces.INewRequest"
+ handler=".subscribers.handle_new_request"
+ />
+
+ <subscriber
+ for="repoze.bfg.interfaces.INewResponse"
+ handler=".subscribers.handle_new_response"
+ />
+
+This causes the functions as to be registered as event subscribers
+within the :term:`application registry` . Under this configuration,
+when the application is run, every new request and every response will
+be printed to the console. We know that ``INewRequest`` events have a
+``request`` attribute, which is a :term:`WebOb` request, because the
+interface defined at ``repoze.bfg.interfaces.INewRequest`` says it
+must. Likewise, we know that ``INewResponse`` events have a
+``response`` attribute, which is a response object constructed by your
+application, because the interface defined at
+``repoze.bfg.interfaces.INewResponse`` says it must. These particular
+interfaces are documented in the :ref:`events_module` API chapter.
+
+The *subscriber* ZCML element takes two values: ``for``, which is the
+interface the subscriber is registered for (which limits the events
+that the subscriber will receive to those specified by the interface),
+and ``handler`` which is a Python dotted-name path to the subscriber
+function.
+
+The return value of a subscriber function is ignored.
+
+Uses For Events
+---------------
+
+Here are some things that events are useful for:
+
+- Attaching different interfaces to the request to be able to
+ differentiate e.g. requests from a browser against requests from an
+ XML-RPC client within view code. To do this, you'd subscribe a
+ function to ```INewRequest``, and use the
+ ``zope.interface.alsoProvides`` function to add one or more
+ interfaces to the request object.
+
+- Post-processing all response output by subscribing to
+ ``INewResponse``, for example, modifying headers.
+
+ .. note::
+
+ Usually postprocessing requests is better handled in middleware
+ components. The ``INewResponse`` event exists purely for
+ symmetry with ``INewRequest``, really.
+
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index 33e80caf7..36273de9d 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -1,3 +1,5 @@
+.. _views_chapter:
+
Views
=====
@@ -11,11 +13,14 @@ Defining a View as a Function
The easiest way to define a view is to create a function that accepts
two arguments: :term:`context`, and :term:`request`. For example,
-this is a hello world view implemented as a function::
+this is a hello world view implemented as a function:
+
+.. code-block:: python
+ :linenos:
- def hello_world(context, request):
- from webob import Response
- return Response('Hello world!')
+ def hello_world(context, request):
+ from webob import Response
+ return Response('Hello world!')
The :term:`context` and :term:`request` arguments can be defined as
follows:
@@ -63,13 +68,14 @@ You must associate a view with a URL by adding information to your
:term:`application registry` via :term:`ZCML` in your
``configure.zcml`` file using a ``bfg:view`` declaration.
-.. sourcecode:: xml
+.. code-block:: xml
+ :linenos:
- <bfg:view
- for=".models.IHello"
- view=".views.hello_world"
- name="hello.html"
- />
+ <bfg:view
+ for=".models.IHello"
+ view=".views.hello_world"
+ name="hello.html"
+ />
The above maps the ``.views.hello_world`` view function to
:term:`context` objects which implement the ``.models.IHello``
@@ -89,12 +95,13 @@ changes. It's also shorter to type.
You can also declare a *default view* for a model type:
-.. sourcecode:: xml
+.. code-block:: xml
+ :linenos:
- <bfg:view
- for=".models.IHello"
- view=".views.hello_world"
- />
+ <bfg:view
+ for=".models.IHello"
+ view=".views.hello_world"
+ />
A *default view* has no ``name`` attribute. When a :term:`context` is
traversed and there is no *view name* in the request, the *default
@@ -103,18 +110,115 @@ view* is the view that is used.
You can also declare that a view is good for any model type by using
the special ``*`` character in the ``for`` attribute:
-.. sourcecode:: xml
+.. code-block:: xml
+ :linenos:
- <bfg:view
- for="*"
- view=".views.hello_world"
- name="hello.html"
- />
+ <bfg:view
+ for="*"
+ view=".views.hello_world"
+ name="hello.html"
+ />
This indicates that when :mod:`repoze.bfg` identifies that the *view
name* is ``hello.html`` against *any* :term:`context`, this view will
be called.
+The ``bfg:view`` ZCML Element
+-----------------------------
+
+The ``bfg:view`` ZCML element has these possible attributes:
+
+view
+
+ The Python dotted-path name to the view callable.
+
+for
+
+ A Python dotted-path name representing the :term:`interface` that
+ the :term:`context` must have in order for this view to be found and
+ called.
+
+name
+
+ The *view name*. Read and understand :ref:`traversal_chapter` to
+ understand the concept of a view name.
+
+permission
+
+ The name of a *permission* that the user must possess in order to
+ call the view. See :ref:`view_security_section` for more
+ information about view security and permissions.
+
+request_type
+
+ A Python dotted-path name representing the :term:`interface` that
+ the :term:`request` must have in order for this view to be found and
+ called. See :ref:`view_request_types_section` for more
+ information about view security and permissions.
+
+.. _view_request_types_section:
+
+View Request Types
+------------------
+
+You can optionally add a *request_type* attribute to your ``bfg:view``
+declaration, which indicates what "kind" of request the view should be
+used for. For example:
+
+.. code-block:: xml
+ :linenos:
+
+ <bfg:view
+ for=".models.IHello"
+ view=".views.hello_json"
+ name="hello.json"
+ request_type=".interfaces.IJSONRequest"
+ />
+
+Where the code behind ``.interfaces.IJSONRequest`` might look like:
+
+.. code-block:: python
+ :linenos:
+
+ from repoze.bfg.interfaces import IRequest
+
+ class IJSONRequest(IRequest):
+ """ An marker interface for representing a JSON request """
+
+This is an example of simple "content negotiation", using JSON as an
+example. To make sure that this view will be called when the request
+comes from a JSON client, you can use an ``INewRequest`` event
+subscriber to attach the ``IJSONRequest`` interface to the request if
+and only if the request headers indicate that the request has come
+from a JSON client. Since we've indicated that the ``request_type``
+in our ZCML for this particular view is ``.interfaces.IJSONRequest``,
+the view will only be called if the request provides this interface.
+
+You can also use this facility for "skinning" a by using request
+parameters to vary the interface(s) that a request provides. By
+attaching to the request an arbitrary interface after examining the
+hostname or any other information available in the request within an
+``INewRequest`` event subscriber, you can control view lookup
+precisely. For example, if you wanted to have two slightly different
+views for requests to two different hostnames, you might register one
+view with a ``request_type`` of ``.interfaces.IHostnameFoo`` and
+another with a ``request_type`` of ``.interfaces.IHostnameBar`` and
+then arrange for an event subscriber to attach
+``.interfaces.IHostnameFoo`` to the request when the HTTP_HOST is
+``foo`` and ``.interfaces.IHostnameBar`` to the request when the
+HTTP_HOST is ``bar``. The appropriate view will be called.
+
+You can also form an inheritance hierarchy out of ``request_type``
+interfaces. When :mod:`repoze.bfg` looks up a view, the most specific
+view for the interface(s) found on the request based on standard
+Python method resolution order through the interface class hierarchy
+will be called.
+
+See :ref:`events_chapter` for more information about event
+subscribers.
+
+.. _view_security_section:
+
View Security
-------------
@@ -125,16 +229,24 @@ against the context before the view function is actually called.
Here's an example of specifying a permission in a ``bfg:view``
declaration:
-.. sourcecode:: xml
+.. code-block:: xml
+ :linenos:
- <bfg:view
- for=".models.IBlog"
- view=".views.add_entry"
- name="add.html"
- permission="add"
- />
+ <bfg:view
+ for=".models.IBlog"
+ view=".views.add_entry"
+ name="add.html"
+ permission="add"
+ />
When a security policy is enabled, this view will be protected with
-the ``add`` permission. See the :ref:`security_chapter` chapter to
-find out how to turn on a security policy.
+the ``add`` permission. The view will not be called if the user does
+not possess the ``add`` permission relative to the current
+:term:`context`. Instead an HTTP ``Unauthorized`` status will be
+returned to the client.
+
+.. note::
+
+ See the :ref:`security_chapter` chapter to find out how to turn on
+ a security policy.
diff --git a/docs/notes.txt b/docs/notes.txt
new file mode 100644
index 000000000..e8d679237
--- /dev/null
+++ b/docs/notes.txt
@@ -0,0 +1,16 @@
+- Document z3c.pt
+
+- Spaces in project names (allow for separate project / package names?)
+
+- Subpath and view name in request
+
+- WebOb Request basics
+
+- "push" style templating
+
+- .001 case where there is a template without a view.
+
+- Warn if permissions are defined but no security policy is in place.
+
+- Change port num due to conflict with Postgres.
+
diff --git a/repoze/bfg/tests/fixtureapp/configure.zcml b/repoze/bfg/tests/fixtureapp/configure.zcml
index 265d69511..dfedda7bb 100644
--- a/repoze/bfg/tests/fixtureapp/configure.zcml
+++ b/repoze/bfg/tests/fixtureapp/configure.zcml
@@ -5,16 +5,17 @@
<include package="repoze.bfg" />
<bfg:view
- for=".models.IFixture"
view=".views.fixture_view"
+ for=".models.IFixture"
permission="repoze.view"
/>
<bfg:view
+ view=".views.fixture_view"
for=".models.IFixture"
- template="templates/fixture.pt"
+ name="dummyskin.html"
permission="repoze.view"
- name="fixture.html"
+ request_type=".views.IDummy"
/>
</configure>
diff --git a/repoze/bfg/tests/fixtureapp/views.py b/repoze/bfg/tests/fixtureapp/views.py
index 2babbc59c..ccf0e4811 100644
--- a/repoze/bfg/tests/fixtureapp/views.py
+++ b/repoze/bfg/tests/fixtureapp/views.py
@@ -1,3 +1,8 @@
+from zope.interface import Interface
+
def fixture_view(context, request):
return None
+class IDummy(Interface):
+ pass
+
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index f8ef78ff8..663022796 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -13,54 +13,37 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup):
from repoze.bfg.zcml import view
return view
- def test_no_class_or_template(self):
+ def test_no_view(self):
f = self._getFUT()
from zope.configuration.exceptions import ConfigurationError
context = DummyContext()
self.assertRaises(ConfigurationError, f, context, 'repoze.view', None)
- def test_no_such_file(self):
- f = self._getFUT()
- from zope.configuration.exceptions import ConfigurationError
- context = DummyContext()
- self.assertRaises(ConfigurationError, f, context, 'repoze.view', None,
- template='notthere.pt')
-
- def test_only_template(self):
+ def test_only_view(self):
f = self._getFUT()
context = DummyContext()
class IFoo:
pass
- f(context, 'repoze.view', IFoo, template='minimal.pt')
+ def view(context, request):
+ pass
+ f(context, 'repoze.view', IFoo, view=view)
actions = context.actions
- from repoze.bfg.interfaces import ITemplate
- from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.security import ViewPermissionFactory
from zope.component.zcml import handler
from zope.component.interface import provideInterface
- self.assertEqual(len(actions), 4)
-
- regutil_discriminator = ('utility', ITemplate,
- context.path('minimal.pt'))
- regutil = actions[0]
- self.assertEqual(regutil['discriminator'], regutil_discriminator)
- self.assertEqual(regutil['callable'], handler)
- self.assertEqual(regutil['args'][0], 'registerUtility')
- self.assertEqual(regutil['args'][1].template.filename,
- context.path('minimal.pt'))
- self.assertEqual(regutil['args'][2], ITemplate)
- self.assertEqual(regutil['args'][3], context.path('minimal.pt'))
+ self.assertEqual(len(actions), 3)
- provide = actions[1]
+ provide = actions[0]
self.assertEqual(provide['discriminator'], None)
self.assertEqual(provide['callable'], provideInterface)
self.assertEqual(provide['args'][0], '')
self.assertEqual(provide['args'][1], IFoo)
-
- permission = actions[2]
+
+ permission = actions[1]
permission_discriminator = ('permission', IFoo, '', IRequest,
IViewPermission)
self.assertEqual(permission['discriminator'], permission_discriminator)
@@ -72,29 +55,27 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup):
self.assertEqual(permission['args'][3], IViewPermission)
self.assertEqual(permission['args'][4], '')
self.assertEqual(permission['args'][5], None)
-
- regadapt = actions[3]
+
+ regadapt = actions[2]
regadapt_discriminator = ('view', IFoo, '', IRequest, IView)
self.assertEqual(regadapt['discriminator'], regadapt_discriminator)
self.assertEqual(regadapt['callable'], handler)
self.assertEqual(regadapt['args'][0], 'registerAdapter')
- self.assertEqual(regadapt['args'][1].template,
- context.path('minimal.pt'))
+ self.assertEqual(regadapt['args'][1], view)
self.assertEqual(regadapt['args'][2], (IFoo, IRequest))
self.assertEqual(regadapt['args'][3], IView)
self.assertEqual(regadapt['args'][4], '')
self.assertEqual(regadapt['args'][5], None)
- def test_only_factory(self):
+ def test_request_type(self):
f = self._getFUT()
context = DummyContext()
class IFoo:
pass
def view(context, request):
pass
- f(context, 'repoze.view', IFoo, view=view)
+ f(context, 'repoze.view', IFoo, view=view, request_type=IDummy)
actions = context.actions
- from repoze.bfg.interfaces import IRequest
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.security import ViewPermissionFactory
@@ -110,77 +91,29 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup):
self.assertEqual(provide['args'][1], IFoo)
permission = actions[1]
- permission_discriminator = ('permission', IFoo, '', IRequest,
+ permission_discriminator = ('permission', IFoo, '', IDummy,
IViewPermission)
self.assertEqual(permission['discriminator'], permission_discriminator)
self.assertEqual(permission['callable'], handler)
self.assertEqual(permission['args'][0], 'registerAdapter')
self.failUnless(isinstance(permission['args'][1],ViewPermissionFactory))
self.assertEqual(permission['args'][1].permission_name, 'repoze.view')
- self.assertEqual(permission['args'][2], (IFoo, IRequest))
+ self.assertEqual(permission['args'][2], (IFoo, IDummy))
self.assertEqual(permission['args'][3], IViewPermission)
self.assertEqual(permission['args'][4], '')
self.assertEqual(permission['args'][5], None)
regadapt = actions[2]
- regadapt_discriminator = ('view', IFoo, '', IRequest, IView)
+ regadapt_discriminator = ('view', IFoo, '', IDummy, IView)
self.assertEqual(regadapt['discriminator'], regadapt_discriminator)
self.assertEqual(regadapt['callable'], handler)
self.assertEqual(regadapt['args'][0], 'registerAdapter')
self.assertEqual(regadapt['args'][1], view)
- self.assertEqual(regadapt['args'][2], (IFoo, IRequest))
+ self.assertEqual(regadapt['args'][2], (IFoo, IDummy))
self.assertEqual(regadapt['args'][3], IView)
self.assertEqual(regadapt['args'][4], '')
self.assertEqual(regadapt['args'][5], None)
- def test_template_and_factory(self):
- f = self._getFUT()
- context = DummyContext()
- from zope.configuration.exceptions import ConfigurationError
- self.assertRaises(ConfigurationError, f, context, 'repoze.view',
- None, view=object, template='minimal.pt')
-
-class TemplateOnlyViewFactoryTests(unittest.TestCase, PlacelessSetup):
- def setUp(self):
- PlacelessSetup.setUp(self)
-
- def tearDown(self):
- PlacelessSetup.tearDown(self)
-
- def _getTargetClass(self):
- from repoze.bfg.zcml import TemplateOnlyViewFactory
- return TemplateOnlyViewFactory
-
- def _zcmlConfigure(self):
- import repoze.bfg
- import zope.configuration.xmlconfig
- zope.configuration.xmlconfig.file('configure.zcml', package=repoze.bfg)
-
- def _getTemplatePath(self, name):
- import os
- here = os.path.abspath(os.path.dirname(__file__))
- return os.path.join(here, 'fixtures', name)
-
- def _makeOne(self, *arg, **kw):
- klass = self._getTargetClass()
- return klass(*arg, **kw)
-
- def test_call(self):
- self._zcmlConfigure()
- path = self._getTemplatePath('minimal.pt')
- view = self._makeOne(path)
- result = view(None, None)
- from webob import Response
- self.failUnless(isinstance(result, Response))
- self.assertEqual(result.app_iter, ['<div>\n</div>'])
- self.assertEqual(result.status, '200 OK')
- self.assertEqual(len(result.headerlist), 2)
-
- def test_call_no_template(self):
- self._zcmlConfigure()
- view = self._makeOne('nosuch')
- self.assertRaises(ValueError, view, None, None)
-
class TestSampleApp(unittest.TestCase, PlacelessSetup):
def setUp(self):
PlacelessSetup.setUp(self)
@@ -224,5 +157,8 @@ class DummyContext:
'args':args}
)
+from zope.interface import Interface
+class IDummy(Interface):
+ pass
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index 7d81527d2..343abc1d7 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -1,68 +1,28 @@
-import os
-
from zope.component.zcml import handler
from zope.component.interface import provideInterface
from zope.configuration.exceptions import ConfigurationError
from zope.configuration.fields import GlobalObject
-from zope.configuration.fields import Path
from zope.interface import Interface
-from zope.interface import implements
-from zope.interface import classProvides
from zope.schema import TextLine
from repoze.bfg.interfaces import IRequest
-from repoze.bfg.interfaces import ITemplateFactory
-from repoze.bfg.interfaces import ITemplate
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.interfaces import IView
-from repoze.bfg.template import Z3CPTTemplateFactory
-from repoze.bfg.template import render_template_to_response
-
from repoze.bfg.security import ViewPermissionFactory
-class TemplateOnlyViewFactory(object):
- """ Pickleable template-only view factory """
- classProvides(ITemplateFactory)
- implements(IView)
-
- def __init__(self, template):
- self.template = template
-
- def __call__(self, context, request):
- kw = dict(view=self, context=context, request=request)
- return render_template_to_response(self.template, **kw)
-
def view(_context,
permission=None,
for_=None,
view=None,
name="",
- template=None,
+ request_type=IRequest,
):
- if (template and view):
- raise ConfigurationError(
- 'One of template or view must be specified, not both')
-
- if template:
- template_abs = os.path.abspath(str(_context.path(template)))
- if not os.path.exists(template_abs):
- raise ConfigurationError('No template file named %s' % template_abs)
- utility = Z3CPTTemplateFactory(template_abs)
- _context.action(
- discriminator = ('utility', ITemplate, template_abs),
- callable = handler,
- args = ('registerUtility', utility, ITemplate, template_abs),
- )
- view = TemplateOnlyViewFactory(template_abs)
-
if not view:
- raise ConfigurationError(
- 'Neither template nor factory was specified, though one must be '
- 'specified.')
+ raise ConfigurationError('"view" attribute was not specified')
if for_ is not None:
_context.action(
@@ -74,18 +34,19 @@ def view(_context,
if permission:
pfactory = ViewPermissionFactory(permission)
_context.action(
- discriminator = ('permission', for_,name, IRequest,IViewPermission),
+ discriminator = ('permission', for_,name, request_type,
+ IViewPermission),
callable = handler,
args = ('registerAdapter',
- pfactory, (for_, IRequest), IViewPermission, name,
+ pfactory, (for_, request_type), IViewPermission, name,
_context.info),
)
_context.action(
- discriminator = ('view', for_, name, IRequest, IView),
+ discriminator = ('view', for_, name, request_type, IView),
callable = handler,
args = ('registerAdapter',
- view, (for_, IRequest), IView, name,
+ view, (for_, request_type), IView, name,
_context.info),
)
@@ -115,9 +76,11 @@ class IViewDirective(Interface):
required=False,
)
- template = Path(
- title=u"The name of a template that implements the view.",
- description=u"""Refers to a file containing a z3c.pt page template""",
+ request_type = GlobalObject(
+ title=u"""The request type interface for the view""",
+ description=(u"The view will be called if the interface represented by "
+ u"'request_type' is implemented by the request. The "
+ u"default request type is repoze.bfg.interfaces.IRequest"),
required=False
)