08 Dec 2008

Spoiler: nasty programming problem ahead.

I am currently developing on a Plone3 website. Plone and its base, Zope, have a lot of concepts from the programming world, layers of abstractions if you will. A layer of abstraction is designed to make your life as a programmer easier, but only if you understand that it's there of course.

Example:

I was developing an aspect of certain types in our project, namely that they can be bookmarked. With Zope(3), you can write an adapter. This class does nothing but add specific behaviour to another object. Now I can have a lot of different bookmarkable types, without changing all their classes in the same way (and no, inheritance is not an answer for everything). All I need to do is adapt them via an interface when I want to bookmark them, like this:

bookmarkable = IBookmarkable(context)

My adapter object will make sure that the adapted object has an 'bookmarked' attribute, a boolean:

def __init__(self, context):
     self.context.bookmarked = False

But oh! Everytime I'd adapt some object via IBookmarkable to work on its bookmarked attribute, a new adapter is created (via __init__()). So each time I adapt, I set the bookmarked attribute on False. So let's change that to:

def __init__(self, context):
     if not hasattr(self.context, 'bookmarked'):
         self.context.bookmarked = False

Now I was happy. But later, my unit tests indicated to me that whenever I bookmarked a folder object, the bookmarked attribute would in the same way be triggered for all bookmarkable objects contained in the folder!

After a while, I found that the code that checked for the bookmarked attribute triggered Zopes implicit acquisition. The acquisition mechanism uses attributes from objects higher up in the containment hierarchy when the object itself doesn't have it. So in the code above, I check for the attribute, thereby (through implicit acquisition) setting it to be the one of the containing folder. When I then later boomark the folder, I also bookmark the contained object, since they use the same boolean object. To prevent this, here is the solution:

def __init__(self, context):
     if not hasattr(self.context.aq_explicit, 'bookmarked'):
        self.context.bookmarked = False

And I was so close to bother the Zope mailing list - Now I am a bit proud to have figured the abstraction layers out myself :)

 

# lastedited 08 Dec 2008
You are seeing a selection of all entries on this page. See all there are.