Skip to content

Commit

Permalink
- Remove explanation of changing the request type in a new request
Browse files Browse the repository at this point in the history
  event subscriber, as other predicates are now usually an easier way
  to get this done.
  • Loading branch information
Chris McDonough committed Dec 9, 2009
1 parent 42da8fe commit e40eb20
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 158 deletions.
4 changes: 4 additions & 0 deletions CHANGES.txt
Expand Up @@ -15,6 +15,10 @@ Documentation
configuration into account in more places and uses glossary
references more liberally.

- Remove explanation of changing the request type in a new request
event subscriber, as other predicates are now usually an easier way
to get this done.

1.2a4 (2009-12-07)
==================

Expand Down
10 changes: 4 additions & 6 deletions docs/glossary.rst
Expand Up @@ -377,12 +377,10 @@ Glossary
Request type
An attribute of a :term:`request` that allows for specialization
of view invocation based on arbitrary categorization. The every
:term:`request` object that bfg generates and manipulates has one
or more :term:`interface` objects attached to it. The default
interface attached to a request object is
``repoze.bfg.interfaces.IRequest``. See
:ref:`using_an_event_to_vary_the_request_type` for more
information.
:term:`request` object that :mod:`repoze.bfg` generates and
manipulates has one or more :term:`interface` objects attached to
it. The default interface attached to a request object is
``repoze.bfg.interfaces.IRequest``.
repoze.lemonade
Zope2 CMF-like `data structures and helper facilities
<http://docs.repoze.org/lemonade>`_ for CA-and-ZODB-based
Expand Down
28 changes: 15 additions & 13 deletions docs/narr/configuration.rst
Expand Up @@ -193,25 +193,18 @@ based on a URL. However, our sample application uses only
:term:`traversal`.

In :mod:`repoze.bfg` terms, :term:`traversal` is the act of walking
over a *directed graph* of objects from a :term:`root` object using
the individual path segments of the "path info" portion of a URL (the
data following the hostname and port number, but before any query
string elements or fragments, for example the ``/a/b/c`` portion of
the URL ``http://example.com/a/b/c?foo=1``) in order to find a
:term:`context` object and a :term:`view name`. The combination of
the :term:`context` object and the :term:`view name` (and, in more
complex configurations, other :term:`predicate` values) are used to
find "the right" :term:`view callable`, which will be invoked after
over an object graph starting from a :term:`root` object in order to
find a :term:`context` object and a :term:`view name`. The individual
path segments of the "path info" portion of a URL (the data following
the hostname and port number, but before any query string elements or
fragments, for example the ``/a/b/c`` portion of the URL
``http://example.com/a/b/c?foo=1``) are used as "steps" during
traversal.

.. note:: A useful analogy of how :mod:`repoze.bfg` :term:`traversal`
works is available within the section entitled
:ref:`traversal_behavior`. You should probably go read it now.

The object graph of our hello world application is very simple:
there's exactly one object in our graph; the default :term:`root`
object.

The results of a :term:`traversal` include a :term:`context` and a
:term:`view name`. The :term:`view name` is the *first* URL path
segment in the set of path segments "left over" during
Expand All @@ -231,6 +224,15 @@ exhausting all the path segments implied by the path info of the URL:
no segments are "left over". In this case, because the :term:`view
name` is non-empty, a *non-default* view callable will be invoked.

The combination of the :term:`context` object and the :term:`view
name` (and, in more complex configurations, other :term:`predicate`
values) are used to find "the right" :term:`view callable`, which will
be invoked after traversal.

The object graph of our hello world application is very simple:
there's exactly one object in our graph; the default :term:`root`
object.

Apologies for the digression; on with the tutorial.

Relating Traversal to the Hello World Application
Expand Down
12 changes: 6 additions & 6 deletions docs/narr/environment.rst
@@ -1,7 +1,7 @@
.. _environment_chapter:

Environment and Configuration
=============================
Environment Variables and ``.ini`` File Settings
================================================

:mod:`repoze.bfg` behavior can be configured through a combination of
operating system environment variables and ``.ini`` configuration file
Expand Down Expand Up @@ -47,16 +47,16 @@ application-specific configuration settings.
| ``BFG_RELOAD_ALL`` | ``reload_all`` | Turns all reload_* settings on. |
+---------------------------------+-----------------------------+----------------------------------------+
| ``BFG_CONFIGURE_ZCML`` | ``configure_zcml`` | Use the specified filename to load |
| | | the :term:`application registry` |
| | | the default app :term:`ZCML` file |
| | | instead of the filename implied by |
| | | ``filename`` value passed to |
| | | ``repoze.bfg.router.make_app``. If |
| | | ``repoze.bfg.router.make_app``. If |
| | | this is a relative filename, it will |
| | | be considered relative to the |
| | | ``package`` passed to ``make_app`` |
| | | by the application. It may also |
| | | take the form of a resource |
| | | "specification" which names both the |
| | | take the form of a :term:`resource |
| | | specification` which names both the |
| | | package name and a package-relative |
| | | filename, e.g. |
| | | ``dotted.package.name:path/to.zcml``. |
Expand Down
114 changes: 9 additions & 105 deletions docs/narr/events.rst
Expand Up @@ -4,12 +4,12 @@ Using Events
=============

An *event* is an object broadcast by the :mod:`repoze.bfg` framework
at particularly interesting points during the lifetime of an
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 perform slightly advanced operations. For
example, subscribing to an event can allow you to "skin" a site
slightly differently based on the hostname used to reach the site.
at interesting points during the lifetime of an 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 perform slightly advanced operations. For example,
subscribing to an event can allow you to "skin" a site slightly
differently based on the hostname used to reach the site.

Events in :mod:`repoze.bfg` are always broadcast by the framework.
However, they only become useful when you register a *subscriber*. A
Expand Down Expand Up @@ -145,108 +145,12 @@ 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, along with others, are documented in the
:ref:`events_module` API chapter.
defined at ``repoze.bfg.interfaces.INewResponse`` says it must.

The return value of a subscriber function is ignored. Subscribers to
the same event type are not guaranteed to be called in any particular
order relative to one another.

.. _using_an_event_to_vary_the_request_type:

Using An Event to Vary the Request Type
---------------------------------------

The most common usage of the ``INewRequestEvent`` is to attach an
:term:`interface` to a request after introspecting the request data in
some way. For example, you might want to be able to differentiate a
request issued by a browser from a request issued by a JSON client.
This differentiation makes it possible to register different views
against different ``request_type`` interfaces; for instance, depending
on the presence of a request header, you might return JSON data.

To do this, you should subscribe an function to the ``INewRequest``
event type, and you should use the ``zope.interface.alsoProvides`` API
within the function to add one or more interfaces to the request
object provided by the event. Here's an example.

.. code-block:: python
:linenos:
from zope.interface import alsoProvides
from zope.interface import Interface
class IJSONRequest(Interface):
""" A request from a JSON client that sets and Accept:
application/json header """
def categorize_request(event):
request = event.request
accept = request.headers.get('accept', '')
if 'application/json' in accept:
alsoProvides(request, IJSONRequest)
If you subscribe ``categorize_request`` for the
``repoze.bfg.interfaces.INewRequest`` type, the ``IJSONRequest``
interface will be attached to each request object that has ``accept``
headers which match ``application/json``.

Thereafter, you can use the ``request_type`` attribute of a
term:`view` ZCML statement or a ``@bfg_view`` decorator to refer to
this ``IJSONRequest`` interface. For example:

.. code-block:: xml
:linenos:
<subscriber
for="repoze.bfg.interfaces.INewRequest"
handler=".subscribers.categorize_request"
/>
<!-- html default view -->
<view
for=".models.MyModel"
view=".views.html_view"/>
<!-- JSON default view -->
<view
for=".models.MyModel"
request_type=".interfaces.IJSONRequest"
view=".views.json_view"/>
The interface ``repoze.bfg.interfaces.IRequest`` is automatically
implemented by every :mod:`repoze.bfg` request. Views which do not
supply a ``request_type`` attribute will be considered to be
registered for ``repoze.bfg.interfaces.IRequest`` as a default. But
in the example above, ``.views.json_view`` will be called when a
request supplies our ``IJSONRequest`` interface, because it is a more
specific interface.

Of course, you are not limited to using the ``Accept`` header to
determine which interface to attach to a request within an event
subscriber. For example, you might also choose to introspect the
hostname (e.g. ``request.environ.get('HTTP_HOST',
request.environ['SERVER_NAME'])``) in order to "skin" your application
differently based on whether the user should see the "management"
(e.g. "manage.myapp.com") presentation of the application or the
"retail" presentation (e.g. "www.myapp.com").

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.
All other concrete event types are documented in the
:ref:`events_module` API chapter.

30 changes: 7 additions & 23 deletions docs/narr/views.rst
Expand Up @@ -461,14 +461,13 @@ request_type

This value should be a Python dotted-path string 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 request
types. For backwards compatibility with :mod:`repoze.bfg` version
1.0, this value may also be an HTTP ``REQUEST_METHOD`` string, e.g.
('GET', 'HEAD', 'PUT', 'POST', or 'DELETE'). Passing request method
strings as a ``request_type`` is deprecated. Use the
``request_method`` attribute instead for maximum forward
compatibility.
this view to be found and called. The presence of this attribute is
largely for backwards compatibility with applications written for
:mod:`repoze.bfg` version 1.0. This value may be an HTTP
``REQUEST_METHOD`` string, e.g. ('GET', 'HEAD', 'PUT', 'POST', or
'DELETE'). Passing request method strings as a ``request_type`` is
deprecated. Use the ``request_method`` attribute instead for
maximum forward compatibility.

request_method

Expand Down Expand Up @@ -1554,21 +1553,6 @@ rendered in a request that has a ``;charset=utf-8`` stanza on its
to Unicode objects implicitly in :mod:`repoze.bfg`'s default
configuration. The keys are still strings.

.. _view_request_types_section:

Custom View Request Types
-------------------------

You can make use of *custom* view request types by attaching an
:term:`interface` to the request and specifying this interface in the
``request_type`` parameter as a dotted Python name. For example, you
might want to make use of simple "content negotiation", only invoking
a particular view if the request has a content-type of
'application/json'.

For information about using interface to specify a request type, see
:ref:`using_an_event_to_vary_the_request_type`.

.. _response_request_attrs:

Varying Attributes of Rendered Responses
Expand Down
5 changes: 2 additions & 3 deletions docs/tutorials/cmf/skins.rst
Expand Up @@ -18,9 +18,8 @@ for more information about resource overrides.

While there is no analogue to a skin layer search path for locating
Python code (as opposed to resources), :term:`view` code combined with
differing :term:`request type` attributes can provide a good deal of
the same sort of behavior. See
:ref:`using_an_event_to_vary_the_request_type` for more information.
differing :term:`predicate` arguments can provide a good deal of
the same sort of behavior.

Relatedly, the `repoze.bfg.skins
<http://svn.repoze.org/repoze.bfg.skins/>`_ package is an attempt to
Expand Down
5 changes: 3 additions & 2 deletions repoze/bfg/configuration.py
Expand Up @@ -470,8 +470,9 @@ def add_view(self, view=None, name="", for_=None, permission=None,
This value should be an :term:`interface` that the
:term:`request` must provide in order for this view to be
found and called. See :ref:`view_request_types_section` for
more information about request types.
found and called. This value exists only for backwards
compatibility purposes: it's usually easier to use another
predicate.
request_method
Expand Down

0 comments on commit e40eb20

Please sign in to comment.