zeam.form.base
==============

Actions represent code which is executed when the user submit a form.

Action
------

An action represent a single action of a form:

   >>> from zeam.form.base.actions import Action

   >>> action1 = Action("Apply")
   >>> action1
   <Action Apply>

An action is callable with a submission to be execuded:

   >>> from zope.publisher.browser import TestRequest
   >>> request = TestRequest()

   >>> from zeam.form.base.form import FormData
   >>> submission1 = FormData(object(), request)

   >>> action1(submission1)
   Traceback (most recent call last):
     ...
   NotImplementedError

Of course you need to implement your own action:

   >>> from zeam.form.base.markers import SUCCESS

   >>> class MyAction(Action):
   ...    def __call__(self, submission):
   ...        print u"Execute '%s'" % self.title
   ...        return SUCCESS

   >>> myaction1 = MyAction("I like it")
   >>> myaction1(submission1)
   Execute 'I like it'
   <Marker SUCCESS>

Action provide a validate method as well, which is called before the
action is. If the validate method does return True, the action will
not be processed:

   >>> myaction1.validate(submission1)
   True


Submissions
~~~~~~~~~~~

This object contains information about the submitted form for the
actions to be processed and the widgets to be rendered:

   >>> from zope.interface.verify import verifyObject
   >>> from zeam.form.base import interfaces
   >>> verifyObject(interfaces.IFormData, submission1)
   True

You have an helper function to clone them:

   >>> from zeam.form.base.form import cloneFormData
   >>> clone1 = cloneFormData(submission1)
   >>> clone1 is submission1
   False
   >>> verifyObject(interfaces.IFormData, clone1)
   True

Settings are cloned as well:

   >>> clone1.ignoreRequest = True
   >>> clone1.ignoreContent = False
   >>> clone1.mode = 'carambar'
   >>> clone1.prefix = 'form.advanced'
   >>> clone2 = cloneFormData(clone1)
   >>> clone2 is clone1
   False
   >>> clone2.ignoreRequest
   True
   >>> clone2.ignoreContent
   False
   >>> clone2.mode
   'carambar'
   >>> clone2.prefix
   'form.advanced'
   >>> verifyObject(interfaces.IFormData, clone2)
   True

Interface
~~~~~~~~~

Action implements IAction, and IComponent:

   >>> verifyObject(interfaces.IAction, action1)
   True
   >>> verifyObject(interfaces.IComponent, action1)
   True


Actions
-------

Action are added to an Actions collection:

   >>> from zeam.form.base.actions import Actions
   >>> as1 = Actions(action1, Action('Cancel'))
   >>> as1
   <Actions>
   >>> list(as1)
   [<Action Apply>, <Action Cancel>]

That Actions is a collection, so all collection operation are
available.


Processing of actions
~~~~~~~~~~~~~~~~~~~~~

It add a ``process`` method, which takes a form and a request and
execute actions that need to be. Let's create a set of action with
real actions and try it:

   >>> myaction2 = MyAction("Make it nicer")
   >>> as2 = Actions(myaction1, myaction2)
   >>> list(as2)
   [<MyAction I like it>, <MyAction Make it nicer>]

   >>> as2.process(submission1, request)
   <Marker NOTHING_DONE>

   >>> r2 = TestRequest(form={'form.action.%s' %
   ...      myaction2.identifier: myaction2.title})
   >>> submission2 = FormData(object(), r2)

   >>> as2.process(submission2, r2)
   Execute 'Make it nicer'
   <Marker SUCCESS>


Validation
~~~~~~~~~~

Like we said, an action have a validate method. Let's define an action
which doesn't validate unless there is a key 'valid' in the request:

   >>> class MyNeverHappyAction(MyAction):
   ...     def validate(self, submission):
   ...         return submission.request.has_key('valid')

   >>> m3 = MyNeverHappyAction("I am unhappy")
   >>> as3 = Actions(m3)

And a test submission for it, which validate the action:

   >>> r3 = TestRequest(form={'form.action.%s' % m3.identifier: m3.title,
   ...                        'valid': True})
   >>> submission3 = FormData(object(), r3)

The action works:

   >>> m3(submission3)
   Execute 'I am unhappy'
   <Marker SUCCESS>
   >>> as3.process(submission3, r3)
   Execute 'I am unhappy'
   <Marker SUCCESS>

However if the validation fail, it is not:

   >>> r31 = TestRequest(form={'form.action.%s' % m3.identifier: m3.title})
   >>> s31 = FormData(object(), r31)

   >>> m3(s31)
   Execute 'I am unhappy'
   <Marker SUCCESS>
   >>> as3.process(s31, r31)
   <Marker NOTHING_DONE>


Errors
~~~~~~

Actions can trigger errors, by raising ActionError. Let's define an
action like that:

   >>> class MyFailingAction(Action):
   ...     def __call__(self, submission):
   ...         raise interfaces.ActionError, "I can't take it anymore"

   >>> m4 = MyFailingAction("Do it")
   >>> as4 = Actions(m4)

Errors can be read from the submission:

   >>> r4 = TestRequest(form={'form.action.%s' % m4.identifier: m4.title})
   >>> s4 = FormData(object(), r4)

   >>> list(s4.errors)
   []
   >>> s4.formError

So the action doesn't work, and the error will be reported in the
submission:

   >>> m4(s4)
   Traceback (most recent call last):
     ...
   ActionError: I can't take it anymore
   >>> as4.process(s4, r4)
   <Marker FAILURE>

   >>> list(s4.errors)
   [<Error I can't take it anymore>]
   >>> s4.formError
   <Error I can't take it anymore>

Errors implement an interface as well, and are components contained
in a collection:

   >>> verifyObject(interfaces.IError, s4.formError)
   True
   >>> interfaces.IError.extends(interfaces.IComponent)
   True

   >>> verifyObject(interfaces.IErrors, s4.errors)
   True
   >>> interfaces.IErrors.extends(interfaces.ICollection)
   True


Interfaces
~~~~~~~~~~

Those Actions implement IActions and ICollection:

   >>> verifyObject(interfaces.IActions, as1)
   True
   >>> interfaces.IActions.extends(interfaces.ICollection)
   True


Defining actions on a form
--------------------------

You can define form methods as Action, using the action decortor. This
will create an action that will executed the method, and register in
the Actions list located on the form class:

   >>> from zeam.form.base.actions import action
   >>> from zeam.form.base.form import Form

   >>> class MyForm(Form):
   ...   @action(u"Change")
   ...   def change(self):
   ...       self.status = u"Just kidding"

   >>> MyForm.actions
   <Actions>
   >>> list(MyForm.actions)
   [<DecoratedAction Change>]

This is like a regular action, but run the method when you call it:

   >>> d1 = list(MyForm.actions)[0]
   >>> d1
   <DecoratedAction Change>

   >>> verifyObject(interfaces.IAction, d1)
   True

   >>> f3 = MyForm(object(), request)
   >>> d1.validate(f3)
   True
   >>> d1(f3)
   >>> f3.status
   u'Just kidding'

