Adapter
zcml and code for the adapters
zcml registration
^^^^^^^^^^^^^^^^^
Remember what we want to do: we want to adapt objects implementing a
certain interface by giving them annotations. In keywordannotator we
register an adapter for objects implementing the IKeywordMatch
interface. This adapter should provide such an object the new
IKeywordBasedAnnotations interface, which is just a marker interface
(or label). This adapter can be created by calling the
KeywordBasedAnnotations class in events.py. We register that adapter
in the configure.zcml file::
<adapter
for=".interfaces.IKeywordMatch"
provides=".interfaces.IKeywordBasedAnnotations"
factory=".events.KeywordBasedAnnotations"
/>
In quadapter we do something similar::
<adapter
for=".interfaces.IMaudio"
provides=".interfaces.IAudioAnnotations"
factory=".events.AudioAnnotations"
/>
This means that the adapter can be created by calling the
AudioAnnotations class in the events.py file of quadapter. This
adapter provides the IAudioAnnotations interface for objects that
already implement the IMaudio interface.
IAudioAnnotations is not a marker interface but a 'normal' interface,
if you want to make that distinction. In the interfaces.py file of
quadapter we state that any object that claims to implement the
IAudioAnnotations interface should have the attributes completeURL and
partURL::
class IAudioAnnotations(Interface):
"""Provide access to the audio annotations of an IMaudio object.
"""
completeURL = schema.URI(title=u'URL to complete audio content')
partURL = schema.URI(title=u'URL to a part of the audio content')
At this point it may be good to say that Zope 3 tends to use a lot of
interfaces. But you have probably already noticed that. :)
Adapter Code
^^^^^^^^^^^^
We have registered two adapters. Now how does the code look? In
keywordannotator::
class KeywordBasedAnnotations(object):
...
def __init__(self, context):
self.context = context
annotations = IAnnotations(self.context)
self._metadata = annotations.get(self._anno_key, None)
if self._metadata is None:
self._metadata = PersistentDict()
annotations[self._anno_key] = self._metadata
The __init__ function is the factory; in other words, it is the
function that creates a KeywordBasedAnnotations object based on
another object that is passed in via the 'context' parameter. This is
the spot where we first really see annotations. It is this line::
annotations = IAnnotations(self.context)
Here the context object is adapted to the IAnnotations interface and
the resulting wrapped or adapted object is stored in the annotations
variable. That variable is now basically a python dictionary which at
this point probably does not contain any values. The other lines make
sure that a basic structure for storing annotations is now available
in that object.
Now on to the adapter code in quadapter::
class AudioAnnotations(KeywordBasedAnnotations):
...
def __get_completeURL(self):
return self._metadata.get(COMP_ANNO)
def __set_completeURL(self, url):
self._metadata[COMP_ANNO] = url
completeURL = property(__get_completeURL, __set_completeURL)
This class subclasses the KeywordBasedAnnotations class from
keywordannotator, so it inherits the init function we saw above. But
the lines in this code do the stuff that we actually started this
product for: they add the completeURL property, where we can store the
link to the complete service of my church. This link is stored in the
self._metadata property, which is an annotation to this object.
The same is done (in some code that is not shown here as it is
basically the same) for the partURL property, where we can now store a
link to the partial service or more sane terms: the sermon.
Previous:
Current Status
