Zope 3: A New Hope

by Maurits van Rees last modified Feb 04, 2009 03:05 AM
One general strategy and one Quills specific strategy

When we extend a product in Zope 3 style, we adapt an existing content
type without making a new content type. In our case this means that
we only need to adapt WeblogEntry, without the need for changing the
Weblog class. Also, we can arrange that our adaptations work for the
VideoWeblogEntry class as well, as long as that class plays nice and
implements the IWeblogEntry interface that Quills defines.

So already we can see that the Zope 3 way indeed brings new hope, in
that our changes can be much less intrusive and much more compatible
with other changes.

We will be using and introducing the following Zope 3 technologies:

* events

* utilities

* adapters

* annotations


General Strategy
----------------

We are going to make two products: one general product for extending
an object when it fulfils some condition, and one specific product for
extending a Quills WeblogEntry when it has audio content. The
strategy for the general product is this:

* An **event** is handled by an event handler, which uses a...

* **utility** to decide if an...

* **adapter** is necessary, in which case...

* **annotations** are added to the original object.

So: when an event takes place, we ask a utility if this event warrants
an adaptation of an object. The adaptation is implemented by adding
annotations to that object.

This is implemented by the **keywordannotator** product. And this
strategy probably has most people blinking their eyes and rereading
that list. :) Don't panic. You probably *do* want to reread it, but
if you do not fully understand it, you can just go to the next
section, which will translate this general idea into a specific
strategy for our use case. It should be much clearer then.


Quills Strategy
---------------

Do you remember the four Zope 3 technologies that we planned on using?
Events, utilities, adapters and annotations. This is how our specific
strategy uses those:

event
Someone adds a keyword `audio` to a WeblogEntry. When
this happens, Zope fires a so-called IObjectModified event. We will
register an event handler that springs into action when a
WeblogEntry (or actually any object that claims to implement the
IWeblogEntry interface) is modified or added.

utility
The code that handles this event, calls a utility. This utility
looks at the WeblogEntry and decides that the addition of this
keyword means that this entry now also implements the marker
interface IMaudio that we will define. If an object implements or
provides a 'normal' interface, then this object has all the
functions and attributes that are defined by that interface. A
marker interface has no functions or attributes. So it is basically
just a label: the object is marked as being IMaudio.

We also make sure that we can later add annotations to this entry by
letting it provide the standard IAttributeAnnotatable interface
defined by Zope 3.

adapter
We can now actually adapt (extend) this object that
implements IMaudio and add annotations to it. Adapting basically
does the same that inheritance (the Zope 2 way) does, but with a
different technique. You temporarily put a `wrapper` around
an object, do something to it (in our case add annotations) and then
remove the wrapper or just forget about it. Any other code (for
example the code in Quills itself) does not know or care that the
WeblogEntry object has been adapted or wrapped and now has something
extra. To that code, the WeblogEntry is still a normal WeblogEntry,
even though it now has annotations.

annotations
Annotations can be added in several ways, but most
used is the method associated with the IAttributeAnnotatable
interface that I mentioned above. In our case, two links are added
in a new hidden attribute of that WeblogEntry.

This is implemented by the **quadapter** (Quills Adapter) product,
which uses the keywordannotator product. In fact, this quadapter
product is added in the examples directory of keywordannotator. By the
way, both products have tests, so you can just run them and see for
yourself that all this actually works.

This section is rather important. If you understand what our goal and
our strategy is, you stand a good chance of understanding the next
sections which present the actual code. On the other hand, if you
prefer a *bottom up* approach, then reading the next sections may help
you in understanding this strategy.

Each of the next few sections first present the code from the general
keywordannotator product and then the code from the specific quadapter
product. Sometimes I find it hard myself to follow what
keywordannotator is actually doing, but reading the corresponding
quadapter code usually helps. :)