=================================
 Schevo Transaction Hook Methods
=================================


Overview
========

Transaction hooks make it easy to change specific behavior of default
transaction classes.  You can customize the following behaviors:

* Setup of a transaction instance.

* What happens just before standard transaction execution.

* What happens just after standard transaction execution.


Doctest Setup
=============

This document also functions as a doctest::

    >>> from schevo.test import DocTest


Subclassing in general
======================

Define each subclass as an inner class of the entity class that you are
overriding.  Begin the class name with an underscore, then the name of
the transaction you are overriding.  Inherit from the name of the transaction
in the ``T`` namespace.  For example::

    class Foo(E.Entity):

        bar = f.string()
        count = f.integer()

        class _Create(T.Create):
            ...

        class _Delete(T.Delete):
            ...

        class _Update(T.Update):
            ...

You can also create custom transactions that have names that differ
from the standard ones shown above.  Remember to set up a `t` method
so you can create instances of your new transaction::

    class Foo(E.Entity):

        bar = f.string()
        count = f.integer()

        def t_custom_update(self, **kw):
            return E.Foo._CustomUpdate(self, **kw)

        class _CustomUpdate(T.Update):
            ...


Overriding __init__ and _execute not allowed
--------------------------------------------

Overriding `__init__` or `_execute` in most `Transaction` subclasses
is not allowed.  If you attempt to do so, Schevo will raise a
`schevo.error.TransactionExecuteRedefinitionRestricted` exception::

    >>> t = DocTest("""
    ...     class Foo(E.Entity):
    ...
    ...         bar = f.string()
    ...
    ...         class _Create(T.Create):
    ...
    ...             def _execute(self, db):
    ...                 pass
    ... """) #doctest: +ELLIPSIS
    Traceback (most recent call last):
        ...
    TransactionExecuteRedefinitionRestricted: ...

Classes that have a `_restrict_subclasses` attribute set to `True`
enforce the above restriction.  They include the following:

* Combination
* Create
* Delete
* Update
* Inverse
* Initialize
* Populate
* CallableWrapper


Overriding hook methods
-----------------------

When implementing the hooks listed below, remember that you can raise
an exception at any point to veto an operation.  Any changes to the database
made by the transaction will be reversed, and control will return to the
parent transaction execution, if one exists.


Adding and overriding fields
----------------------------

Subclasses of Create, Delete, and Update transactions that are defined as
inner classes of an entity class inherit the field definitions from the
entity class.

When a field with a new name is defined in a transaction subclass, it is
added to the end of the existing field definitions.  For example, the
Update class here has three fields, ``bar``, ``count``, and ``baz``::

    class Foo(E.Entity):

        bar = f.string()
        count = f.integer()

        class _Update(T.Update):

            baz = f.entity('Baz')

When a field with an existing name is defined, it replaces the old field
definition and is added to the end.  For example, the Update class here has
two fields, ``count`` and ``bar``::

    class Foo(E.Entity):

        bar = f.string()
        count = f.integer()

        class _Update(T.Update):

            bar = f.entity('Bar')

To change the properties of a field without changing the order of it,
make those changes in the ``_setup`` method by using the ``f`` namespace::

    class Foo(E.Entity):

        bar = f.string()
        count = f.integer()

        class _Update(T.Update):

            def _setup(self):
                self.f.count.hidden = True


Available hook methods
======================


Create subclasses
-----------------

``_setup(self)``
    Called during ``__init__`` after setting fields based on keyword
    arguments, before setting default values for fields that weren't assigned
    a value.

``_before_execute(self, db)``
    Called during transaction execution, before the create operation takes
    place.

``_after_execute(self, db, entity)``
    Called during transaction execution, after the create operation takes
    place.  The newly-created entity is passed in.


Delete subclasses
-----------------

``_setup(self)``
    Called at end of ``__init__``.

``_before_execute(self, db, entity)``
    Called during transaction execution, before the delete operation takes
    place.  The entity about to be deleted is passed in.

``_after_execute(self, db)``
    Called during transaction execution, after the delete operation has
    taken place.


Update subclasses
-----------------

``_setup(self)``
    Called at end of ``__init__``.

``_before_execute(self, db, entity)``
    Called during transaction execution, before the update operation takes
    place.  The entity about to be updated is passed in.

``_after_execute(self, db, entity)``
    Called during transaction execution, after the update operation has
    taken place.  The entity that was just updated is passed in.
