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

Forms are class, registered like views:

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

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

   >>> class Content(object):
   ...     def __repr__(self):
   ...         return '<%s object>' % self.__class__.__name__
   >>> context = Content()

   >>> form1 = Form(context, request)
   >>> form1
   <zeam.form.base.form.Form object at ...>

They implements `IForm` and `ISimpleForm`, `ISimpleForm` extending
`IForm` and adding some form processing abilities::

   >>> from zope.interface.verify import verifyObject
   >>> from zeam.form.base import interfaces

   >>> verifyObject(interfaces.IForm, form1)
   True

   >>> verifyObject(interfaces.ISimpleForm, form1)
   True

   >>> interfaces.ISimpleForm.isOrExtends(interfaces.IForm)
   True

The `IForm` extends the clearly defined Grok view component interface,
`IGrokViewSupport`:

   >>> for attr in list(interfaces.IGrokViewSupport): print attr
   redirect
   i18nLanguage
   render
   url
   update
   response

The ISimpleForm interface extends also two main components interfaces.

The `IFormCanvas` interface defines the very core of a form : its
label and description, its fields and actions and the related methods::

   >>> interfaces.ISimpleForm.isOrExtends(interfaces.IFormCanvas)
   True
   
   >>> print interfaces.IFormCanvas.__doc__
   Definition of a form structure.
      Form presentation : label, description
      Form contents and actions : fields, actions and their related methods.

The `IFormData` interface defines the elements involved in the form
data processing::

   >>> interfaces.ISimpleForm.isOrExtends(interfaces.IFormData)
   True

   >>> print interfaces.IFormData.__doc__
   Form data processing facilities.

   >>> for name, attr in interfaces.IFormData.namesAndDescriptions():
   ...     print "%s: %s" % (name, attr.getDoc())
    status: Form status message.
    extractData: Returns the form data and errors for the given fields.
    errors: Iterable of the errors that occured during the form processing.
    formError: Main error that occurred during the form processing.
    getContentData: Returns the content that will be used for the form processing.
    dataManager: Data manager class used to access content.
    setContentData: Sets the content that will be used as the form processing context.
    validateData: Validates the form in a global way and returns a collection of errors (if any occured) or None.

Data manager
------------

A form (more precisely, the widgets) can access the content data via a data
manager. A data manager will transparently allow you to access different
kind of contents, such as dictionaries, data structures or directly
attributes.


Object data manager
~~~~~~~~~~~~~~~~~~~

   >>> from zeam.form.base.datamanager import ObjectDataManager

   >>> class MyContent(Content):
   ...    title = u'Content'
   ...    value = 42
   >>> mycontent = MyContent()

   >>> manager1 = ObjectDataManager(mycontent)
   >>> manager1
   <ObjectDataManager used for <MyContent object>>

It correctly implements its interface:

   >>> verifyObject(interfaces.IDataManager, manager1)
   True

And you can use it to access content value:

   >>> manager1.get('title')
   u'Content'
   >>> manager1.get('value')
   42

Inexisting content value raises KeyError

   >>> manager1.get('foobar')
   Traceback (most recent call last):
     ...
   KeyError: 'foobar'

You can set values as well:

   >>> manager1.set('ready', True)
   >>> mycontent.ready
   True


Dictionary data manager
~~~~~~~~~~~~~~~~~~~~~~~

There is a data manager which is able to work on dictionaries as well:

   >>> from zeam.form.base.datamanager import DictDataManager

   >>> data = {'title': u'Content', 'value': 42}
   >>> manager2 = DictDataManager(data)
   >>> manager2
   <DictDataManager used for {'value': 42, 'title': u'Content'}>

It correctly implements its interface:

   >>> verifyObject(interfaces.IDataManager, manager2)
   True

And you can use it to access content value:

   >>> manager2.get('title')
   u'Content'
   >>> manager2.get('value')
   42

Inexisting content value raises KeyError

   >>> manager2.get('foobar')
   Traceback (most recent call last):
     ...
   KeyError: 'foobar'

You can set values as well:

   >>> manager2.set('ready', True)
   >>> data.get('ready')
   True


Using a data manager on a form
------------------------------

You can use a data manager on every FormData:

   >>> from zeam.form.base.form import cloneFormData
   >>> formdata1 = cloneFormData(form1)

By default you will get a data manager for the context:

   >>> manager3 = formdata1.getContentData()
   >>> manager3
   <ObjectDataManager used for <Content object>>
   >>> manager3.content is context
   True

However you modify it and give directly a content:

   >>> formdata1.setContentData(mycontent)
   >>> formdata1.getContentData()
   <ObjectDataManager used for <MyContent object>>

Or directly a data manager:

   >>> formdata1.setContentData(manager2)
   >>> formdata1.getContentData() is manager2
   True


Modes & extraction
------------------

Mode Markers
~~~~~~~~~~~~

Forms and Fields have a mode. This mode decides what widget you get
and the behavior of the form extractor. Logically, some modes should
not allow value extraction.

Let's have a closer look at a ModeMarker that defines a component
mode::

  >>> from zeam.form.base.interfaces import IModeMarker
  >>> list(IModeMarker)
  ['extractable']

The `extractable` attribute defines the ability of a mode to provide a
valid value extraction::

  >>> print IModeMarker['extractable'].__doc__
  Boolean allowing or not the extraction of the data, for components in that mode.

``zeam.form.base`` provides 4 base mode markers. Letr's review them::

  >>> from zeam.form.base import markers

The `input` mode is, logically, extractable, as the form submission
are based on it::

  >>> IModeMarker.providedBy(markers.INPUT)
  True
  >>> markers.INPUT.extractable
  True
  >>> print markers.INPUT
  input

Accordingly, the `hidden` mode matches the `input` behavior::

  >>> IModeMarker.providedBy(markers.HIDDEN)
  True
  >>> markers.HIDDEN.extractable
  True
  >>> print markers.HIDDEN
  hidden

At the contrary, the `display` mode will not be extractable, as it's
used for presentation purpose only::

  >>> IModeMarker.providedBy(markers.DISPLAY)
  True
  >>> markers.DISPLAY.extractable
  False
  >>> print markers.DISPLAY
  display


Extraction
~~~~~~~~~~

The extraction process will rely on the fields' mode or, if not defined
locally, on the form's mode. Let's define a form with 2 fields::

  >>> from zeam.form.base.fields import Field, Fields
  >>> id = Field('identifier')
  >>> name = Field('name')

  >>> class MyForm(Form):
  ...    fields = Fields(id, name)

Now, we create a request with some data, for the `name` field. We
consider the `id` field immutable.

  >>> request = TestRequest(form={"form.field.name": "myname"})
  >>> context = Content()

While extracting the data, the form will retrieve the values of all
the "extractable" fields::

  >>> form = MyForm(context, request)
  >>> form.update()
  >>> data, errors = form.extractData()

  >>> print data
  {'identifier': <Marker NO_VALUE>, 'name': 'myname'}

The `identifier` value is set to NO_VALUE, as the request was not
containing any. As we consider the field immutable, we'll apply a
simple display mode on it, allowing us to display the current value,
without providing a way to edit it and without considering the field
in the extraction process::

  >>> form = MyForm(context, request)
  >>> form.fields['identifier'].mode = markers.DISPLAY
  >>> form.update()
  >>> data, errors = form.extractData()
  >>> print data
  {'name': 'myname'}

The `identifier` field is not extracted, as planned.
