=======
Sources
=======

Concepts
--------

Sources are designed with three concepts:

- The source itself - an iterable

  This can return any kind of object it wants. It doesn't have to care
  for browser representation, encoding, ...

- A way to map a value from the iterable to something that can be used
  for form *values* - this is called a token. A token is commonly a
  (unique) 7bit representation of the value.

- A way to map a value to something that can be displayed to the user -
  this is called a title

The last two elements are dispatched using a so called `term`. The
ITitledTokenizedTerm interface contains a triple of (value, token, title).

Additionally there are some lookup functions to perform the mapping
between values and terms and tokens and terms.

Sources that require context use a special factory: a context source
binder that is called with the context and instanciates the source when
it is actually used.

Sources in Fields
-----------------

A choice field can be constructed with a source or source name.  When a source
is used, it will be used as the source for valid values.

Create a source for all odd numbers.

.. doctest::

   >>> from zope import interface
   >>> from zope.schema.interfaces import ISource, IContextSourceBinder
   >>> @interface.implementer(ISource)
   ... class MySource(object):
   ...     divisor = 2
   ...     def __contains__(self, value):
   ...         return bool(value % self.divisor)
   >>> my_source = MySource()
   >>> 1 in my_source
   True
   >>> 2 in my_source
   False

   >>> from zope.schema import Choice
   >>> choice = Choice(__name__='number', source=my_source)
   >>> bound = choice.bind(object())
   >>> bound.vocabulary
   <...MySource...>

If a IContextSourceBinder is passed as the `source` argument to Choice, it's
`bind` method will be called with the context as its only argument.   The
result must implement ISource and will be used as the source.

.. doctest::

   >>> _my_binder_called = []
   >>> def my_binder(context):
   ...     _my_binder_called.append(context)   
   ...     source = MySource()
   ...     source.divisor = context.divisor
   ...     return source
   >>> interface.directlyProvides(my_binder, IContextSourceBinder)

   >>> class Context(object):
   ...     divisor = 3

   >>> choice = Choice(__name__='number', source=my_binder)
   >>> bound = choice.bind(Context())
   >>> len(_my_binder_called)
   1
   >>> bound.vocabulary
   <...MySource...>
   >>> bound.vocabulary.divisor
   3

When using IContextSourceBinder together with default value, it's
impossible to validate it on field initialization. Let's check if
initalization doesn't fail in that case.

.. doctest::

   >>> choice = Choice(__name__='number', source=my_binder, default=2)

   >>> del _my_binder_called[:]
   >>> bound = choice.bind(Context())
   >>> len(_my_binder_called)
   1

   >>> bound.validate(bound.default)
   >>> bound.validate(3)
   Traceback (most recent call last):
   ...
   ConstraintNotSatisfied: 3

It's developer's responsibility to provide a default value that fits the
constraints when using context-based sources.
