summaryrefslogtreecommitdiff
path: root/docs/narr/events.rst
blob: 1d012b99d17b73f30f68c9d0a1c33610bb785f4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
.. index::
   single: event
   single: events
   single: subscriber
   single: INewRequest
   single: INewResponse
   pair: subscriber; ZCML directive

.. _events_chapter:

Using Events
=============

An *event* is an object broadcast by the :mod:`repoze.bfg` framework
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
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 either of the following methods:

.. topic:: Configuring an Event Listener Imperatively

   You can imperatively configure a subscriber function to be called
   for some event type via the
   :meth:`repoze.bfg.configuration.Configurator.add_subscriber`
   method (see also :term:`Configurator`):

   .. code-block:: python
      :linenos:

      from repoze.bfg.interfaces import INewRequest

      from subscribers import mysubscriber

      config.add_subscriber(mysubscriber, INewRequest)

   The first argument to
   :meth:`repoze.bfg.configuration.Configurator.add_subscriber` is the
   subscriber function; the second argument is the event type.

.. topic:: Configuring an Event Listener Through ZCML

   You can configure an event listener 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"
       />

Each of the above examples implies that every time the
:mod:`repoze.bfg` framework emits an event object that supplies an
:class:`repoze.bfg.interfaces.INewRequest` interface, the
``mysubscriber`` function will be called with an *event* object.

As you can see, a subscription is made in terms of an
:term:`interface`.  The event object sent to a subscriber will always
be an object that possesses an interface.  The interface itself
provides documentation of 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"
    />

See also :ref:`subscriber_directive`.

The :meth:`repoze.bfg.configuration.Configurator.add_subscriber`
method can be used to perform the same job:

.. ignore-next-block
.. code-block:: python
   :linenos:

   from repoze.bfg.interfaces import INewRequest
   from repoze.bfg.interfaces import INewResponse

   from subscribers import handle_new_request
   from subscribers import handle_new_response

   config.add_subscriber(handle_new_request, INewRequest)
   config.add_subscriber(handle_new_response, INewResponse)

This causes the functions as to be registered as event subscribers
within the :term:`application registry` .  Under this configuration,
when the application is run, each time a new request or response is
detected, a message will be printed to the console.

.. sidebar:: The ``INewResponse`` Event vs. Middleware

   Postprocessing a response is usually better handled in a WSGI
   :term:`middleware` component than in subscriber code that is called
   by a :class:`repoze.bfg.interfaces.INewResponse` event.  The
   :class:`repoze.bfg.interfaces.INewResponse` event exists almost
   purely for symmetry with the
   :class:`repoze.bfg.interfaces.INewRequest` event.

We know that :class:`repoze.bfg.interfaces.INewRequest` events have a
``request`` attribute, which is a :term:`WebOb` request, because the
interface defined at :class:`repoze.bfg.interfaces.INewRequest` says
it must.  Likewise, we know that
:class:`repoze.bfg.interfaces.INewResponse` events have a ``response``
attribute, which is a response object constructed by your application,
because the interface defined at
:class:`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.

All other concrete event types are documented in the
:ref:`events_module` API documentation.