July 9, 2014

Plone - the broken parts - a non-pythonic programming model

This is a loose series of blog posts about parts of Plone that I consider as broken from the prospective of a programmer. The blog entries are based on personal experiences with Plone over the last few months collected in new Plone 4.3 projects and some legacy projects but they also reflect experienced learned from other non-core Plone developers involved in these projects (developers on the customer side).

Archetypes served for than ten years as framework for building content-types in Plone. Dexterity had been designed as successor of Archetypes with the goal making the content-types development within Plone more easy with less boilerplate. Content-type development should become be more pythonic. Creating a new of a content-type in Plone in a "pythonic way" would look like this:

"obj = plone.api.content.create(some_type)
obj.id = 'foo'
obj.title = u'hello world'
obj.start_time = datetime(...)
"

This approach is in general working as long as all data are stored directly as attributes on the object itself. However there is a problem with the introduction of Dexterity behaviors. Behaviors can implement different storage strategies other than the standard attribute storage. So if a content-type is composed of several behaviors it is not legitimate to read and write object attributes directly that are introduced and managed by a behavior. With many behaviors you need to know which field comes from which behavior exactly. So your code may look like this:

obj = plone.api.content.create(some_type)
adapter1 = IBehavior1(obj)
adapter1.field1 = ...
adapter1.field2 = ...
adapter2 = IBehavior2(obj)
adapter2.field3 = ... 

You see the problem that this code is no longer pythonic. It exposes the Zope Component Architecture for simple object modifications to a degree that appears wild to non-core Plone developers. It is getting more scary when you need to call a content-type functionality for performing some internal processing like it is needed in plone.app.event:

from plone.app.event.dx.behaviors import data_postprocessing
from plone.app.event.dx.behaviors import IEventBasic
event = plone.api.content.create('Event')
adapter = IEventBasic(event)
adapter.start = datetime(..)
adapter.end = datetime(..)
data_postprocessing(obj, None)

This is no the point where we have a communication problem teaching Plone and Dexterity development to average developers. Such a programming model can not be taught to non-Plone people. Most people scratched their head and called what-the-fuck!?

Related topic: hooks and event subscribers. Archetypes offered a bunch of hooks (good or bad) in order to provide additional functionality inside the content-types implementation without needing to hack for example the auto-generated forms for view + edit. E.g. at_post_edit_script() could be used to perform arbitrary actions after submitting the edit form. The ZCA-ish approach are event subscribers where you can register arbitrary methods and handler for various life cycle events. This concept is in general a nice solution if you want to add external behavior to existing (3rd party code) without modifying the actual implementation. But with Dexterity this is also the only solution for hooking your own additional functionality into your own content-type.

Example: in zopyx.existdb we need to push some information from the Dexterity content-type after saving the data in Plone to a different system. This is a core functionality of the content-type we are implementing - it is not business logic, it is core functionality and therefore it should be able to implement this directly inside the content-type. Not possible with Dexterity right now. Instead the related code must be moved into an event subscriber outside the scope of the content-type implementation although it belongs to the core implementation. The result is scattered code that actually belongs together into one class. From the prospective of reading code and understanding the programming logic this is not desirable. So in the current programming model you end of with code that belongs to a particular content-type spread over the content-type class, event subscribers and browser views. So there is really a need for having a way to implement hooks in a reasonable way in the scope of the content-type class without explicit event subscribers.