Not Logged In

plone.behavior 1.0.2

plone.behavior

This package provides optional support for “behaviors”. A behavior is
a re-usable aspect of an object that can be enabled or disabled without
changing the component registry.

A behavior is described by an interface, and has metadata such as a title and
a description. The dotted name of the interface is the unique name for the
behavior, from which the metadata can be looked up. When the behavior is
enabled for an object, you will be able to adapt the object to the interface.
In some cases, the interface can be used as a marker interface as well.

As an example, let’s say that your application needs to support object-level
locking, and that this can be modeled via an adapter, but you want to leave
it until runtime to determine whether locking is enabled for a particular
object. You could then register locking as a behavior.

Requirements

This package comes with support for registering behaviors and factories. It
does not, however, implement the policy for determining what behaviors are
enabled on a particular object at a particular time. That decision is deferred
to an IBehaviorAssignable adapter, which you must implement.

This package also does not directly support the adding of marker interfaces to
instances. To do that, you can either use an event handler to mark an object
when it is created, or a dynamic __providedBy__ descriptor that does the
lookup on the fly (but you probably want some caching).

The intention is that behavior assignment is generic across an application,
used for multiple, optional behaviors. It probably doesn’t make much sense to
use plone.behavior for a single type of object. The means to keep track
of which behaviors are enabled for what types of objects will be application
specific.

Usage

A behavior is written much like an adapter, except that you don’t specify
the type of context being adapted directly. For example:

After this is done - and presuming an appropriate IBehaviorAssignable adapter
exists for the context - you can adapt a context to ILockingSupport as
normal:

locking = ILockingSupport(context, None)
if locking is not None:
locking.lock()

You’ll get an instance of LockingSupport if context can be adapted to
IBehaviorAssignable (which, recall, is application specific), and if the
implementation of IBehaviorAssignable says that this context supports this
particular behavior.

It is also possible to let the provided interface act as a marker interface
that is to be provided directly by the instance. To achieve this, omit the
‘factory’ argument. This is useful if you need to register other adapters
(including views and viewlets) for instances providing a particular behavior.

Like the IBehaviorAssignable plumbing, marker interface support needs to be
enabled on a per-application basis. It can be done with a custom
__providedBy__ decorator or an IObjectCreatedEvent handler for applying the
marker. A sample event handler is provided with this package, but is not
registered by default

Please see behavior.txt, directives.txt and annotation.txt for more details.

plone.behavior: Behaviors

Please see README.txt at the root of this egg for more details on what
behaviors are and how to use them.

See directives.txt in this directory for details on how to register new
types of behaviors using ZCML.

Usage

To use this package, you must first provide a suitable IBehaviorAssignable
adapter. This is normally done by the framework. plone.dexterity, for example,
will provide a suitable adapter.

Then, for each behavior:

Write an interface describing the behavior.

Write a factory (much like an adapter factory) that contains the logic of
the behavior. This is optional if your interface is a marker interface that
can be directly provided by the object.

Register the behavior. This consists of a utility providing IBehavior and
an adapter factory based on IBehaviorAdapterFactory. The <plone:behavior />
ZCML directive makes this easy. See directives.txt.

Once the behavior has been registered, you can use standard adaptation idioms
to attempt to use it, e.g.:

locking = ILocking(context, None)
if locking is not None:
locking.lock()

Here, ILocking is a registered behavior interface. The adaptation will only
succeed if the context support behaviors (i.e. it can be adapted to
IBehaviorAssignable), and if the ILocking behavior is currently enabled for
this type of context.

Example

As an example, let’s create a basic behavior that’s described by the
interface ILockingSupport:

NOTE: By convention, the behavior name should be the same as the identifier
of its interface. This convention is maintained by the <plone:behavior />
ZCML directive.

We also need to register an adapter factory that can create an instance of
an ILockingSupport for any context. This is a bit different to a standard
adapter factory (which is normally just a class with a constructor that
takes the context as an argument), because we want this factory to be
able to adapt almost anything, but return None (and thus fail to adapt) if
the behavior isn’t currently enabled for the context.

To get these semantics, we can use the BehaviorAdapterFactory helper
class.

For the behavior to work, we need to define an IBehaviorAssignable adapter.
For the purposes of this test, we’ll maintain a simple, global registry that
maps classes to a list of enabled behavior interfaces.

Marker interfaces

Behaviors work without the aid of marker interfaces. However, it may sometimes
be desirable to apply a marker interface to newly created objects that support
a particular behavior, for example if you need to register specific views or
viewlets that should only be available when this behavior is supported.

Note that there is no need to use marker interfaces if the desired behavior
can be achieved using adapters only. For this, the standard plone.behavior
adapter pattern is better, because there is no dependency on per-instance
markers.

Marker interface support again requires some framework support not configured
by this package. One of two possible configurations is possible:

A custom __providedBy__ descriptor that includes the markeres of all
enabled behaviors can be added to behavior-aware classes.

An event handler can be installed that marks newly created instances with
the markers of all enabled behaviors.

The first approach is better in many ways, because it can be made more robust
in case a marker interface is removed or renamed, and because it is possible
to turn off behavior markers without finding all objects providing the
subtype and calling noLongerProvides() on them. However, it is also pretty
difficult to get this right, and it cannot be generalised (you can’t make
any adapter lookups in the descriptor, since you’d get infinite recursion).
There’s an implementation of such a descriptor in the plone.dexterity package,
which also uses some heavy caching.

An event handler is easier, and this package provides a simple one that you
can use. It is not registered by default, since it may not be desirable to
enable an event handler for every type of object.

For the purposes of this test, we will simulate the event handler by calling
it directly.

We will register this behavior as above, this time specifying the marker
interface explicitly. In real life, of course, we’d be more likely to use the
<plone:behavior /> ZCML directive with the ‘marker’ attribute. See
directives.txt for more details.

Note that since this is applied per-instance, old instances do not get the
marker interface automatically:

>>> ITaggable.providedBy(context1)
False

It may be useful to mark the content with the behavior interface directly for
cases where the marker is all that’s needed for the behavior to work. In
these cases no factory is needed, because the object already provides the
behavior directly as indicated by the marker. Note that the same interface
is used as the ‘interface’ and ‘marker’:

plone.behavior: Annotation storage

plone.behavior comes with a standard behavior factory that can be used to
store the data of a schema interface in annotations. This means that it is
possible to create a simple “data only” behavior with just an interface.

We have created such an interface in plone.behavior.tests, called
IAnnotationStored. It has a single field, ‘some_field’.

1.0b4 - 2009-06-07

Allow a marker-interface-only behavior to be set by using the ‘provides’
attribute (previously ‘interface’) in the <plone:behavior /> directive
without a ‘factory’ attribute. The ‘marker’ attribute (previously known as
‘subtype’) is now only required if there is a marker used in addition to
a behavior adapter with a separate interface (‘provides’) and factory.
[optilude]

Rename the ‘interface’ attribute of <plone:behavior /> to ‘provides’ to
be more consistent with the <adapter /> directive. This is a backwards
incompatible change!
[optilude]

Rename the ‘subtype’ attribute of <plone:behavior /> to ‘marker’ to
be more explicit about its purpose. This is a backwards
incompatible change!
[optilude]