You are here: Home Articles Aggregated aspect customisation
OpenID Log in


Aggregated aspect customisation

by Martin Aspeli last modified May 16, 2009 12:50 PM

A pattern to consider?

I little while ago, I wrote some plugins for Trac 0.11. It used a pattern not dissimilar to way we use adapters in Zope for customisation/plugins, but with a slightly different twist. Whilst not as architecturally clean, it did make some things easier. I'm wondering if we should consider using this type of pattern in Zope/Plone. At least it'd be interesting to think about.

Basically, the pattern works on the principle that the platform provides a number of interfaces that you can use to provide different types of behaviour. Each typically defines one or two methods that you have to implement. Examples - all fictional - could be:

  • An INavigationElement interface with a method getNavigationElement() that returns a list of items to add to the navigation.
  • An INameChooser with a method chooseName() is called to choose a name for a new content object.
  • An IRoleProvider with a method getRoles() that returns the roles for the current user in context.

There would be a well documented list of such interfaces. The framework will look up all components (e.g. using named adapters, allowing you override by name, but generally just being additive) applicable to the current context that provide a particular interface, call the relevant method and use the result.

The idea here is that you build a class that describes the behaviour you want, e.g. for a particular type of content object, or for your particular Plone site (for non-contextual aspects). You build this class up, adding more interfaces and implementing the corresponding method as necessary. There's nothing to stop you from splitting your code up into multiple classes, but keeping things together normally makes things easier, at least for small sites. You stop thinking about these so much as separate adapters and more as a description of the aspects or behaviours you'd like to implement for your type.

In pseudo-code, this could look like:

class MyAspects(ContentAspects):
    implements(INavigationElement, INameChooser, IRoleProvider)

    def getNavigationElement(self):
        return ...
    def chooseName(self):
        return ...
    def getRoles(self):
        return ...

The ContentAspects base class would make sure that this class acted as an adapter, with self.context being the adapter context. The type of context is listed with adapts(), as normal, and the provided aspects are listed with implements(). The name of the adapter could default to the dotted name of the class, though you could obviously specify a different name in the registration (e.g. to override an existing set of aspects). The ContentAspects base class could also be used by a grokker to make this class auto-register the aspects without the need for ZCML. It'd need to register one (named) adapter per provided interface, all using this class as the factory.

The framework code exposing this pattern could look something like:

# build navigation
for name, nav in getAdapters(context, INavigationElement):
    element = nav.getNavigationElement()

Note that to the framework code, this is an adapter like any other.

There is something a bit dirty about this. The separation of concerns in the MyAspects class is fuzzy. To get proper separation of concerns, you'd have one class/adapter per aspect, which is possibly better (certainly more granular), although it's more code to manage.

Even if we don't want to do amalgamated aspects in one class, I think the idea of thinking about some adapters as providing "aspects" holds some merit. We do this already, but we don't always think of it like that. There's a relatively manageable number of interfaces that make sense for people to create adapters for. If we listed and grouped those, we'd have a pretty good starting point for an "API" that people could browse and use to affect the behaviour of content objects and sites in Plone.

Document Actions

A good idea (if I got it right ;))

Posted by at May 18, 2009 02:16 PM
I'm not completely sure of having got it right, but if the idea is to have "big aggregated adapters" that cover an entire aspect, instead of a moltitude of specialized micro-adapters that do a single thing, the idea might actually be good. I think this is mostly finding the right balance between granularity and complexity: simplifying a bit, increasing granularity also adds complexity (for examples, ACLs can be more complex to manage than simple UNIX-style permissions, despite offering more functionality).
A few weeks ago I was working on some Java project (ok, Groovy) and I was a bit confused by Java wanting me to "adapt" a Date instance with a Calendar in order to print it out as string. It's an approach that offers a better flexibility but, for most use cases, it ends up adding complexity without serious benefits.
The approach you propose seems to offer a reasonable compromise, and will probably not cut out the ability to exploit the underlying granularity if needed.
(btw, I then realized Groovy's Date allowed me to format the string without passing through the Calendar)
Plone Book
Professional Plone 4 Development

I am the author of a book called Professional Plone Development. You can read more about it here.

About this site

This Plone site is kindly hosted by: 

Six Feet Up