Zope 3 adapters

Les adapters

Les adapters Zope 3 rajoutent des fonctionnalités à un objet. Exemple, nous avons deux classes:

>>> class Gruik(object):
...     attribut21 = u'je suis issue de la classe Gruik'

>>> class Gruik2(object):
...     def titre(self):
...         return u'je suis issue de la classe Gruik2'

On aimerais pouvoir appeler un attribut 'titre' sur ces deux objets de façon transparente. Pour cela, il nous faut une interface:

>>> import zope.interface

>>> class IGruikTitre(zope.interface.Interface):
...     titre = zope.interface.Attribute(u'un titre')

Puis, 2 adapters qui implémentent notre interface et adaptent nos classes:

>>> import zope.component

>>> class GruikTitre(object):
...     zope.interface.implements(IGruikTitre)
...     zope.component.adapts(Gruik)
...     def __init__(self,context):
...         self.context = context
...     def _titre(self):
...         return self.context.attribut21
...     titre = property(_titre,None)

>>> class GruikTitre2(object):
...     zope.interface.implements(IGruikTitre)
...     zope.component.adapts(Gruik2)
...     def __init__(self,context):
...         self.context = context
...     def _titre(self):
...         return self.context.titre()
...     titre = property(_titre,None)

On enregistre ses deux adapters (normalement via des zcml):

>>> zope.component.provideAdapter(GruikTitre)
>>> zope.component.provideAdapter(GruikTitre2)

On est ensuite capable d'appeler l'attribut titre sur nos deux classes en passant par leur adapter:

>>> gruik = Gruik()
>>> IGruikTitre(gruik).titre
u'je suis issue de la classe Gruik'
>>> gruik2 = Gruik2()
>>> IGruikTitre(gruik2).titre
u'je suis issue de la classe Gruik2'

On appelle donc l'interface sans ce soucier de quel adapter est appelé.

Les multi-adapters

Il est possible de définir des adapters pour plusieurs objets:

>>> class Animal(object):
...     description = u'un animal'

>>> class Nourriture(object):
...     description = u'de la nourriture'

>>> class IRepas(zope.interface.Interface):
...     pass

>>> class Repas(object):
...     zope.interface.implements(IRepas)
...     zope.component.adapts(Animal,Nourriture)
...     def __init__(self,animal,nourriture):
...         self.animal = animal
...         self.nourriture = nourriture
...     def ilmangequoi(self):
...         return u'au repas, %s mange %s' % (self.animal.description,
...                                            self.nourriture.description)

>>> zope.component.provideAdapter(Repas)

Ce qui nous permet en suite d'adapter l'animal à sa nourriture via le repas:

>>> animal = Animal()
>>> nourriture = Nourriture()
>>> repas = zope.component.queryMultiAdapter((animal,nourriture))
>>> repas.ilmangequoi()
u'au repas, un animal mange de la nourriture'

Les adapters nommés

Il est possible de nommer des adapters. En surclassant notre précédent Repas, nous pouvons en créer de nouveau qui porterons un nom:

>>> class PetitDej(Repas):
...     def ilmangequoi(self):
...         return u"au petit dej, %s mange %s abondamment pour prendre des forces !" % (self.animal.description,
...                                                                                   self.nourriture.description)
>>> class Diner(Repas):
...     def ilmangequoi(self):
...         return u"au diner, %s fais la diete pour ne pas etre ballonne la nuit" % self.animal.description

>>> zope.component.provideAdapter(PetitDej,name='ptitdej')
>>> zope.component.provideAdapter(Diner,name='diner')

Puis les utiliser explicitement:

>>> animal = Animal()
>>> nourriture = Nourriture()
>>> repas = zope.component.queryMultiAdapter((animal,nourriture),name='ptitdej')
>>> repas.ilmangequoi()
u'au petit dej, un animal mange de la nourriture abondamment pour prendre des forces !'
>>> repas = zope.component.queryMultiAdapter((animal,nourriture),name='diner')
>>> repas.ilmangequoi()
u'au diner, un animal fais la diete pour ne pas etre ballonne la nuit'

Conclusion

Toute l'architecture de Zope 3 est basé sur les interfaces et les adapters (je vous conseil la lecture des README de zope.interface et zope.component). La vue d'un objet est, par exemple, un multi-adapter nommé qui adapte un context et une request et dont le nom est, la plupart du temps, le .html que l'on appelle.

Il est possible, grâce à ce système de redéfinir tout ou partie du comportement de votre application. La façon dont sont générés l'id de vos objets (voir INameChooser) ou la façon dont il sont traversé (voir ITraverse) par exemple.