136 lines
3.3 KiB
Text
136 lines
3.3 KiB
Text
================
|
|
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'
|
|
|