===============
Action Managers
===============

Action managers are components that manage all actions that can be taken
within a view, usually a form. They are also responsible for executing actions
when asked to do so.

Creating an action manager
--------------------------

An action manager is a form-related adapter that has the following
discriminator: form, request, and content. While there is a base
implementation for an action manager, the ``action`` module does not provide a
full implementation.

So we first have to build a simple implementation based on the ``Actions``
manager base class which allows us to add actions. Note that the following
implementation is for deomnstration purposes. If you want to see a real action
manager implementation, then have a look at ``ButtonActions``. Let's now
implement our simple action manager:

  >>> from z3c.form import action
  >>> class SimpleActions(action.Actions):
  ...     """Simple sample."""
  ...
  ...     def append(self, name, action):
  ...         """See z3c.form.interfaces.IActions."""
  ...         if not name in self:
  ...             self._data_keys.append(name)
  ...         self._data_values.append(action)
  ...         self._data[name] = action

Before we can initialise the action manager, we have to create instances for
our three discriminators, just enough to get it working:

  >>> import zope.interface
  >>> from z3c.form import interfaces
  >>> class Form(object):
  ...     zope.interface.implements(interfaces.IForm)
  >>> form = Form()

  >>> class Content(object):
  ...     zope.interface.implements(zope.interface.Interface)
  >>> content = Content()

  >>> from z3c.form.testing import TestRequest
  >>> request = TestRequest()

We are now ready to create the action manager, which is a simple
triple-adapter:

  >>> manager = SimpleActions(form, request, content)
  >>> manager
  <SimpleActions None>

As we can see in the manager representation above, the name of the manager is
``None``, since we have not specified one:

  >>> manager.__name__ = 'example'
  >>> manager
  <SimpleActions 'example'>


Managing and Accessing Actions
------------------------------

Initially there are no actions in the manager:

  >>> manager.keys()
  []

Our simple implementation of has an additional ``append()`` method, which we
will use to add actions:

  >>> apply = action.Action(request, u'Apply')
  >>> manager.append(apply.name, apply)

The action is added immediately:

  >>> manager.keys()
  ['apply']

However, you should not rely on it being added, and always update the manager
once all actions were defined:

  >>> manager.update()

Note: If the title of the action is a more complex unicode string and no name
is specified for the action, then a hexadecimal name is created from the
title:

  >>> action.Action(request, u'Apply Now!').name
  '4170706c79204e6f7721'

Since the action manager is an enumerable mapping, ...

  >>> from zope.interface.common.mapping import IEnumerableMapping
  >>> IEnumerableMapping.providedBy(manager)
  True

there are several API methods available:

  >>> manager['apply']
  <Action 'apply' u'Apply'>
  >>> manager['foo']
  Traceback (most recent call last):
  ...
  KeyError: 'foo'

  >>> manager.get('apply')
  <Action 'apply' u'Apply'>
  >>> manager.get('foo', 'default')
  'default'

  >>> 'apply' in manager
  True
  >>> 'foo' in manager
  False

  >>> manager.values()
  [<Action 'apply' u'Apply'>]

  >>> manager.items()
  [('apply', <Action 'apply' u'Apply'>)]

  >>> len(manager)
  1


Executing actions
-----------------

When an action is executed, an execution adapter is looked up. If there is no
adapter, nothing happens. So let's create a request that submits the apply
button:

  >>> request = TestRequest(form={'apply': 'Apply'})
  >>> manager = SimpleActions(form, request, content)

We also want to have two buttons in this case, so that we can ensure that only
one is executed:

  >>> apply = action.Action(request, u'Apply')
  >>> manager.append(apply.name, apply)

  >>> cancel = action.Action(request, u'Cancel')
  >>> manager.append(cancel.name, cancel)
  >>> manager.update()

Now that the manager is updated, we can ask it for the "executed" actions:

  >>> manager.executedActions
  [<Action 'apply' u'Apply'>]

Executing the actions does nothing, because there are no handlers yet:

  >>> manager.execute()


Let's now register an action handler that listens to the "Apply" action. An
action handler has four discriminators: form, request, content, and
action. All those objects are available to the handler under those names. When
using the base action handler from the ``action`` module, ``__call__()`` is
the only method that needs to be implemented:

  >>> from z3c.form import util

  >>> class SimpleActionHandler(action.ActionHandlerBase):
  ...     zope.component.adapts(
  ...         None, TestRequest, None, util.getSpecification(apply))
  ...     def __call__(self):
  ...         print 'successfully applied'

  >>> zope.component.provideAdapter(SimpleActionHandler)

As you can see, we registered the action specifically for the apply
action. Now, executing the actions calls this handler:

  >>> manager.execute()
  successfully applied

Of course it only works for the "Apply" action and not ""Cancel":

  >>> request = TestRequest(form={'cancel': 'Cancel'})
  >>> manager.request = apply.request = cancel.request = request
  >>> manager.execute()
