==================================
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'.

Let's show how this may be registered in ZCML.

    >>> configuration = """\
    ... <configure
    ...      xmlns="http://namespaces.zope.org/zope"
    ...      xmlns:plone="http://namespaces.plone.org/plone"
    ...      i18n_domain="plone.behavior.tests">
    ...     
    ...     <include package="zope.component" file="meta.zcml" />
    ...     <include package="plone.behavior" file="meta.zcml" />
    ...     <include package="zope.annotation" />
    ...     
    ...     <plone:behavior
    ...         title="Annotation behavior"
    ...         provides="plone.behavior.tests.IAnnotationStored"
    ...         factory="plone.behavior.AnnotationStorage"
    ...         />
    ...         
    ... </configure>
    ... """

    >>> from StringIO import StringIO
    >>> from zope.configuration import xmlconfig
    >>> xmlconfig.xmlconfig(StringIO(configuration))

Let us now test this. First, we'll need an annotatable context and an
IBehaviorAssignable adapter. See behaviors.txt for more details.

    >>> from zope.interface import Interface, implements, alsoProvides
    >>> from zope.component import provideAdapter, adapts, getUtility
    >>> from zope.annotation.interfaces import IAttributeAnnotatable, IAnnotations
    >>> from plone.behavior.interfaces import IBehavior, IBehaviorAssignable
    >>> from plone.behavior.tests import IAnnotationStored

    >>> BEHAVIORS = {}
    >>> class TestingBehaviorAssignable(object):
    ...     implements(IBehaviorAssignable)
    ...     adapts(Interface)
    ...     
    ...     def __init__(self, context):
    ...         self.context = context
    ...     
    ...     def supports(self, behavior_interface):
    ...         global BEHAVIORS
    ...         return behavior_interface in BEHAVIORS.get(self.context.__class__, [])
    ...         
    ...     def enumerateBehaviors(self):
    ...         global BEHAVIORS
    ...         for iface in BEHAVIORS.get(self.context.__class__, []):
    ...             yield getUtility(IBehavior, iface.__identifier__)

    >>> provideAdapter(TestingBehaviorAssignable)
    
    >>> class Context(object):
    ...     implements(IAttributeAnnotatable)
    >>> BEHAVIORS[Context] = [IAnnotationStored]
    
    >>> context = Context()

We can now adapt the context to our new interface.

    >>> adapted = IAnnotationStored(context)

Before we've set anything, we get the field's missing_value

    >>> adapted.some_field is IAnnotationStored['some_field'].missing_value
    True
    
Let's look at the annotations also:

    >>> sorted(IAnnotations(context).items())
    []

If we now set the value, it will be stored in annotations:

    >>> adapted.some_field = u'New value'
    >>> sorted(IAnnotations(context).items())
    [('plone.behavior.tests.IAnnotationStored.some_field', u'New value')]

And of course we can get it back again:

    >>> adapted.some_field
    u'New value'

If we try to get some other field, we get an AttributeError:

    >>> adapted.bogus_field #doctest: +ELLIPSIS
    Traceback (most recent call last):
    ...
    AttributeError: bogus_field

Of course, we can still set and then get some value on the adapter factory
itself, but it won't be persisted.

    >>> adapted.bogus_field = 123
    >>> adapted.bogus_field
    123
