[Checkins] SVN: hurry.workflow/trunk/ Merged r108441 from multiple-workflows branch

Vincent Fretin vincent.fretin at gmail.com
Wed Jan 27 02:08:23 EST 2010


Log message for revision 108551:
  Merged r108441 from multiple-workflows branch

Changed:
  U   hurry.workflow/trunk/CHANGES.txt
  U   hurry.workflow/trunk/src/hurry/workflow/workflow.py
  U   hurry.workflow/trunk/src/hurry/workflow/workflow.txt

-=-
Modified: hurry.workflow/trunk/CHANGES.txt
===================================================================
--- hurry.workflow/trunk/CHANGES.txt	2010-01-27 06:27:32 UTC (rev 108550)
+++ hurry.workflow/trunk/CHANGES.txt	2010-01-27 07:08:22 UTC (rev 108551)
@@ -4,9 +4,13 @@
 0.11 (unreleased)
 =================
 
-- Nothing changed yet.
+* Do IAnnotations(self.context) only once in WorkflowState.
 
+* An IWorkflowVersions implementation is now optional.
 
+* Added multiple workflows support.
+
+
 0.10 (2009-11-19)
 =================
 

Modified: hurry.workflow/trunk/src/hurry/workflow/workflow.py
===================================================================
--- hurry.workflow/trunk/src/hurry/workflow/workflow.py	2010-01-27 06:27:32 UTC (rev 108550)
+++ hurry.workflow/trunk/src/hurry/workflow/workflow.py	2010-01-27 07:08:22 UTC (rev 108551)
@@ -92,50 +92,54 @@
 
 class WorkflowState(object):
     implements(IWorkflowState)
+    state_key = "hurry.workflow.state"
+    id_key  = "hurry.workflow.id"
 
     def __init__(self, context):
         # XXX okay, I'm tired of it not being able to set annotations, so
         # we'll do this. Ugh.
         from zope.security.proxy import removeSecurityProxy
         self.context = removeSecurityProxy(context)
+        self._annotations = IAnnotations(self.context)
 
     def initialize(self):
-        wf_versions = component.getUtility(IWorkflowVersions)
-        self.setId(wf_versions.createVersionId())
+        wf_versions = component.queryUtility(IWorkflowVersions)
+        if wf_versions is not None:
+            self.setId(wf_versions.createVersionId())
         
     def setState(self, state):
         if state != self.getState():
-            IAnnotations(self.context)[
-                'hurry.workflow.state'] = state
+            self._annotations[self.state_key] = state
             
     def setId(self, id):
         # XXX catalog should be informed (or should it?)
-        IAnnotations(self.context)['hurry.workflow.id'] = id
+        self._annotations[self.id_key] = id
         
     def getState(self):
-        try:
-            return IAnnotations(self.context)['hurry.workflow.state']
-        except KeyError:
-            return None
+        return self._annotations.get(self.state_key, None)
 
     def getId(self):
-        try:
-            return IAnnotations(self.context)['hurry.workflow.id']
-        except KeyError:
-            return None
+        return self._annotations.get(self.id_key, None)
             
 class WorkflowInfo(object):
     implements(IWorkflowInfo)
+    name = u''
     
     def __init__(self, context):
         self.context = context
-                
+        self.wf = component.getUtility(IWorkflow, name=self.name)
+
+    def info(self, obj):
+        return component.getAdapter(obj, IWorkflowInfo, name=self.name)
+
+    def state(self, obj):
+        return component.getAdapter(obj, IWorkflowState, name=self.name)
+
     def fireTransition(self, transition_id, comment=None, side_effect=None,
                        check_security=True):
-        state = IWorkflowState(self.context)
-        wf = component.getUtility(IWorkflow)
+        state = self.state(self.context)
         # this raises InvalidTransitionError if id is invalid for current state
-        transition = wf.getTransition(state.getState(), transition_id)
+        transition = self.wf.getTransition(state.getState(), transition_id)
         # check whether we may execute this workflow transition
         try:
             interaction = getInteraction()
@@ -158,10 +162,10 @@
         result = transition.action(self, self.context)
         if result is not None:
             if transition.source is None:
-                IWorkflowState(result).initialize()
+                self.state(result).initialize()
             # stamp it with version
-            state = IWorkflowState(result)
-            state.setId(IWorkflowState(self.context).getId())
+            state = self.state(result)
+            state.setId(self.state(self.context).getId())
             # execute any side effect:
             if side_effect is not None:
                 side_effect(result)
@@ -171,7 +175,7 @@
                                                    transition, comment)
         else:
             if transition.source is None:
-                IWorkflowState(self.context).initialize()
+                self.state(self.context).initialize()
             # execute any side effect
             if side_effect is not None:
                 side_effect(self.context)
@@ -200,12 +204,12 @@
                                    comment, side_effect, check_security)
         
     def fireTransitionForVersions(self, state, transition_id):
-        id = IWorkflowState(self.context).getId()
+        id = self.state(self.context).getId()
         wf_versions = component.getUtility(IWorkflowVersions)
         for version in wf_versions.getVersions(state, id):
             if version is self.context:
                 continue
-            IWorkflowInfo(version).fireTransition(transition_id)
+            self.info(version).fireTransition(transition_id)
 
     def fireAutomatic(self):
         for transition_id in self.getAutomaticTransitionIds():
@@ -222,7 +226,7 @@
             
     def hasVersion(self, state):
         wf_versions = component.getUtility(IWorkflowVersions)
-        id = IWorkflowState(self.context).getId()
+        id = self.state(self.context).getId()
         return wf_versions.hasVersion(state, id)
     
     def getManualTransitionIds(self):
@@ -245,10 +249,9 @@
         return self.getManualTransitionIds() + self.getSystemTransitionIds()
     
     def getFireableTransitionIdsToward(self, state):
-        wf = component.getUtility(IWorkflow)
         result = []
         for transition_id in self.getFireableTransitionIds():
-            transition = wf.getTransitionById(transition_id)
+            transition = self.wf.getTransitionById(transition_id)
             if transition.destination == state:
                 result.append(transition_id)
         return result
@@ -263,9 +266,8 @@
 
     def _getTransitions(self, trigger):
         # retrieve all possible transitions from workflow utility
-        wf = component.getUtility(IWorkflow)
-        transitions = wf.getTransitions(
-            IWorkflowState(self.context).getState())
+        transitions = self.wf.getTransitions(
+            self.state(self.context).getState())
         # now filter these transitions to retrieve all possible
         # transitions in this context, and return their ids
         return [transition for transition in transitions if

Modified: hurry.workflow/trunk/src/hurry/workflow/workflow.txt
===================================================================
--- hurry.workflow/trunk/src/hurry/workflow/workflow.txt	2010-01-27 06:27:32 UTC (rev 108550)
+++ hurry.workflow/trunk/src/hurry/workflow/workflow.txt	2010-01-27 07:08:22 UTC (rev 108551)
@@ -189,6 +189,85 @@
     'b'
     >>> events[-1].destination is None
     True
+
+
+Multiple workflows
+------------------
+
+We have previously registered a workflow as a unnamed utility.
+You can also register a workflow as a named utility to provide
+several workflows for a site.
+
+Let's create a, invoice document::
+
+   >>> class IInvoiceDocument(IDocument):
+   ...    title = Attribute('Title')
+
+   >>> class InvoiceDocument(object):
+   ...    implements(IInvoiceDocument)
+   ...    def __init__(self, title, amount):
+   ...        self.title = title
+   ...        self.amount = amount
+
+We define our workflow::
+
+    >>> invoice_init = workflow.Transition(
+    ...     transition_id='init_invoice',
+    ...     title='Invoice Received',
+    ...     source=None,
+    ...     destination='received')
+    >>>
+    >>> invoice_paid = workflow.Transition(
+    ...     transition_id='invoice_paid',
+    ...     title='Invoice Paid',
+    ...     source='received',
+    ...     destination='paid')
+
+    >>> invoice_wf = workflow.Workflow( [ invoice_init, invoice_paid ] )
+
+We register a new workflow utility, WorkflowState and WorkflowInfo adapters, all
+named "invoice"::
+
+    >>> from hurry.workflow import workflow
+    >>> from zope.annotation import interfaces as annotation_interfaces
+    >>> component.provideUtility(invoice_wf, interfaces.IWorkflow, name="invoice")
+    >>> class InvoiceWorkflowInfo(workflow.WorkflowInfo):
+    ...     name="invoice"
+    >>> component.provideAdapter(
+    ...     InvoiceWorkflowInfo,
+    ...     (annotation_interfaces.IAnnotatable,),
+    ...     interfaces.IWorkflowInfo,
+    ...     name="invoice")
+    >>> class InvoiceWorkflowState(workflow.WorkflowState):
+    ...     state_key = "invoice.state"
+    ...     id_key  = "invoice.id"
+    >>> component.provideAdapter(
+    ...     InvoiceWorkflowState,
+    ...     (annotation_interfaces.IAnnotatable,),
+    ...     interfaces.IWorkflowState,
+    ...     name="invoice")
+
+Now we can utilize the workflow::
+
+    >>> invoice = InvoiceDocument('abc', 22)
+
+    >>> info = component.getAdapter(invoice, interfaces.IWorkflowInfo, name="invoice")
+    >>> info.fireTransition('init_invoice')
+    >>> state = component.getAdapter(invoice, interfaces.IWorkflowState, name="invoice")
+    >>> state.getState()
+    'received'
+    >>> info.fireTransition('invoice_paid')
+    >>> state.getState()
+    'paid'
+
+Of course, this document always have the default unnamed workflow::
+
+    >>> info = interfaces.IWorkflowInfo(invoice)
+    >>> info.fireTransition('to_a')
+    >>> state = interfaces.IWorkflowState(invoice)
+    >>> state.getState()
+    'a'
+
    
 Multi-version workflow
 ----------------------



More information about the checkins mailing list