Writing your own validators
==================================

After all, using only built-in validators won't help you much: You'll need 
custom validation rules which means that you need to write your own validators.

pycerberus comes with two classes that can serve as a good base when you start
writing a custom validator: The ``BaseValidator`` only provides the absolutely
required set of API so you have maximum freedom. The ``Validator`` class itself
is inherited from the ``BaseValidator`` and defines a more sophisticated API 
and i18n support. Usually you should use the ``Validator`` class.


BaseValidator
----------------------------------

.. autoclass:: pycerberus.api.BaseValidator
   :members:


Validator
----------------------------------

.. autoclass:: pycerberus.api.Validator
   :members:
   :exclude-members: keys, message_for_key


Miscellaneous
----------------------------------

pycerberus uses `simple_super <http://www.häcker.net/trac/browser/open-source/python-simple-super/trunk/simple_super.py>`_
so you can just say 'self.super()' in your custom validator classes. This will
call the super implementation with just the same parameters as your method was
called.

Validators need to be thread-safe as one instance might be used several times.
Therefore you must not add additional attributes to your validator instance
after you called Validator's constructor. To prevent unexperienced programmers
falling in that trap, a ''Validator'' will raise an exception if you try to set
an attribute. If you don't like this behavior and you really know what you are 
doing, you can issue ``validator.set_internal_state_freeze(False)`` to disable
that protection.


Putting all together - A simple validator
-----------------------------------------

Now it's time to put it all together. This validator demonstrates most of the 
API as explained so far:

.. literalinclude:: ./unicode_validator.py
    :pyobject: UnicodeValidator

The validator will convert all input to unicode strings (using the UTF-8 
encoding). It also checks for a maximum length of the string.

You can see that all the conversion is done in ``convert()`` while additional
validation is encapsulated in ``validate()``. This can help you keeping your
methods small.

In case there is an error the ``error()`` method will raise an ``InvalidDataError``.
You select the error message to show by passing a string constant ``key`` which
identifies the message. The key can be used later to adapt the user interface
without relying the message itself (e.g. show an additional help box in the user
interface if the user typed in the wrong password). 

The error messages are declared in the ``messages()``. You'll notice that the 
message strings can also contain variable parts. You can use these variable
parts to give the user some additional hints about what was wrong with the data.

