================
Stateful Objects
================

  >>> from cybertools.stateful.definition import StatesDefinition
  >>> from cybertools.stateful.definition import State, Transition
  >>> from cybertools.stateful.definition import registerStatesDefinition
  >>> from cybertools.stateful.base import Stateful

We start with a simple demonstration class that provides stateful
behaviour directly.

  >>> class Demo(Stateful):
  ...     currentActors = None
  ...     def getActors(self):
  ...         return self.currentActors

  >>> demo = Demo()

The default states definition has the `started` state as its initial
state.

  >>> demo.getState()
  'started'
  >>> demo.getStateObject().title
  'Started'

We can now execute the `finish` Transition.

  >>> demo.doTransition('finish')
  >>> demo.getState()
  'finished'

More complex states definitions
-------------------------------

We'll use a predefined simple publishing workflow that.

  >>> from cybertools.stateful.publishing import simplePublishing
  >>> registerStatesDefinition(simplePublishing())

  >>> demo = Demo()
  >>> demo.statesDefinition = 'simple_publishing'
  >>> demo.getState()
  'draft'

  >>> [t.title for t in demo.getAvailableTransitions()]
  ['publish', 'hide', 'archive', 'remove']

If we try to execute a transition that is not an outgoing transition
of the current state we get an error.

  >>> demo.doTransition('retract')
  Traceback (most recent call last):
  ...
  ValueError: Transition 'retract' is not reachable from state 'draft'.
  >>> demo.getState()
  'draft'

Check condition
---------------

  >>> def checkIfEmpty(obj):
  ...     return getattr(obj, 'empty', True)

  >>> removeAction = demo.getStatesDefinition().transitions.remove
  >>> removeAction.condition = checkIfEmpty
  >>> removeAction in demo.getAvailableTransitions()
  True

  >>> demo.empty = False
  >>> removeAction in demo.getAvailableTransitions()
  False

Check actors
------------

  >>> removeAction.actors = ['master']

  >>> demo.getActors()
  >>> demo.checkActors(['master'])
  True

  >>> demo.empty = True
  >>> removeAction in demo.getAvailableTransitionsForUser()
  True

  >>> demo.currentActors = ['dummy']
  >>> demo.getActors()
  ['dummy']

  >>> removeAction in demo.getAvailableTransitionsForUser()
  False

  >>> demo.currentActors = ['master']
  >>> removeAction in demo.getAvailableTransitionsForUser()
  True


Stateful Adapters
=================

Objects that show stateful behaviour need not be derived from the Stateful
class, for persistent objects one can also provide a stateful adapter.

  >>> from persistent import Persistent
  >>> class Demo(Persistent):
  ...     pass

  >>> demo = Demo()

  >>> from zope import component
  >>> from cybertools.stateful.base import StatefulAdapter
  >>> component.provideAdapter(StatefulAdapter)

We can now retrieve a stateful adapter using the IStateful interface.

  >>> from cybertools.stateful.interfaces import IStateful

  >>> statefulDemo = IStateful(demo)
  >>> statefulDemo.getState()
  'started'
  >>> statefulDemo.getStateObject().title
  'Started'

  >>> statefulDemo.doTransition('finish')
  >>> statefulDemo.getState()
  'finished'

If we make a new adapter for the same persistent object we get
back the state that is stored with the object.

  >>> statefulDemo = IStateful(demo)
  >>> statefulDemo.getState()
  'finished'

