Monday, August 21, 2006

Local adapters in Zope 2.10

Further to Hanno's excitement about local components, I thought I'd add something of my own. As Hanno points out, systems like Plone where more than one site (with possibly different configurations) can be in the same Zope instance, and where much of the configuration needs to be done through-the-web, there is often a need for persistent, local components. In CMF, we have tools, which are persistent (in-ZODB) singletons (per site). Their analogue in the Zope 3 world are persistent (local) utilities.

In Zope 2.10 (and 3.3), Jim has refactored the component architecture to make it much, much easier to work with local components. Components are registered with a site manager, of which there is a global one (the deafult one use when you register components in ZCML) and any number of nested local ones attached to one or more sites. Hanno has made the Plone site root a site on the 3.0 bundle, which means that we can attach local components to it.

Imagine you have an object 'obj' that you want to register as a local utility providing an interface 'iface' with the given name 'name'. Here's how you'd register that as a utility on a particular site:
sm = getSiteManager(site)
sm.registerUtility(obj, iface, name)
Hanno has bundled this functionality into a GenericSetup export/import handler here, which means that setting up local utilities should be as simple as an XML file.

After this registration has been made, you can do this:
util = getUtility(iface, name=name)
and you should get back 'obj'.

That is - if you are in the right context. Upon traversal, Zope (and Five, in our case) will know the context and look up the containment hierarchy to find out where the nearest site manager is. If a site manager does not contain a given component registration, it will fall through to its parent, all the way up to the global site manager. Again, Hanno has taken care of this in Plone. In a test, you may need to do something like:
To simulate what Zope may otherwise do during acquistion.

Now, in plone.portlets I have been doing something similar with local adapters, with no small amount of help from Philipp von Weitershausen. Now, to register a local adapter, you only need to call registerAdapter() on a site manager:
sm.registerAdapter(required=(iface1, iface2,),
provided=iface, name=name, factory=callable)
Here, 'callable' could be a class or a function that produces the adapter providing 'iface'. It will take two parameters, being passed the objects providing iface1 and iface2.

In plone.portlets, however, the pattern is a bit more interesting. The reason for needing a local adapter in the first place is to satisfy zope.contentprovider. This comes with a TALES expression type handler for the provider: expression type. If you write a page template:
tal:replace="structure provider:plone.leftcolumn"
zope.contentprovider will look up a multi-adapter providing IContentProvider from (context, request, view) and call its update() and render() methods.

In plone.portlets, we want to be able to register a content provider to render the left portlet column in place of this expression, which means that an appropriate adapter lookup must be found. Furthermore, the assignment of portlets to a context is a persistent, site-local concept. This implies that not only must the correct IContentProvider local adapter be located, it must also know which "portlet manager" to query for the list of portlets to render.

The solution to this problem is to let the adapter factory (the callable that produces the actual registration) be a persistent object. The implementation of PortletManager has a __call__() method which can produce the appropriate implementation of IContentProvider in the form of a PortletManagerRenderer. When instantiated, this is told which portlet manager it is rendering.

Thus, a portlet manager in /.portlets/left, for example, is registered (using a GenericSetup handler) as the adapter factory for the plone.leftcolumn adapter:
required=(Interface, IBrowserRequest, IBrowserView,),
provided=IContentProvider, name='plone.leftcolumn',
In effect, this achieves a link between the (persistent) adapter registration for the plone.leftcolumn content provider adapter and the (persistent) portlet manager storage.

Of course, users of the library shouldn't have to do any more than this in a GenericSetup profile:


Hanno Schlichting said...

Finally I can read optilude-esk blog posts in addition to your mails, I've been waiting for it for soooo long :)

Just a short update, the exportimport handler is now part of its own package for easier re-use and can be found here:

Martin Aspeli said...

Great, Hanno :)

But why such a Zope2-ish name? Upper-mixed-case? brrrrr :)


sexy said...



A片,色情,成人,做愛,情色文學,A片下載,色情遊戲,色情影片,色情聊天室,情色電影,免費視訊,免費視訊聊天,免費視訊聊天室,一葉情貼圖片區,情色,情色視訊,免費成人影片,視訊交友,視訊聊天,視訊聊天室,言情小說,愛情小說,AIO,AV片,A漫,av dvd,聊天室,自拍,情色論壇,視訊美女,AV成人網,色情A片,SEX





will said...







milf said...

dessicant air dryer pediatric asthma asthma specialist
carpet cleaning dallas tx carpet cleaners dallas carpet cleaning dallas
beach vacations your beach vacations
bob hairstyle
bob haircuts bob layered pob hairstyle
bobbed classic bob Care for Curly Hair
Tips for Curly Hair curly hair 12r 22.5 best price
tires truck bus tires 12r 22.5 washington new house
new house Houston new house san Antonio new house ventura
sealy air beds portable portables air beds
antique doorknobs drying desiccant
air drying desiccantlipitor allergic reactionsApple prodam iphone prahaflorida headache clinic
cheap beach vacationsnight vision binoculars bargains

milf said...

new houston house houston house tx stains removal dye
stains removal clothes stains removal teeth whitening
teeth whiteningbright teeth jennifer grey nose
jennifer nose jobs calebrities nose jobs Women with Big Noses
Women hairstyles Big Nose Women, hairstylesdvd player troubleshootingtroubleshooting with the dvd player