[Zope3-checkins] SVN: Zope3/trunk/src/zope/ Added pluggable workflow-integration component. Now, when specifying a

Jim Fulton jim at zope.com
Mon May 9 13:07:09 EDT 2005


Log message for revision 30314:
  Added pluggable workflow-integration component. Now, when specifying a
  workflow definition, you must also specify an object that the
  definition calls to get application-defined participant and work-item
  components.
  

Changed:
  U   Zope3/trunk/src/zope/app/wfmc/meta.zcml
  U   Zope3/trunk/src/zope/app/wfmc/metaconfigure.py
  D   Zope3/trunk/src/zope/app/wfmc/metadirectives.py
  U   Zope3/trunk/src/zope/app/wfmc/zcml.txt
  U   Zope3/trunk/src/zope/wfmc/README.txt
  A   Zope3/trunk/src/zope/wfmc/attributeintegration.py
  A   Zope3/trunk/src/zope/wfmc/component/
  A   Zope3/trunk/src/zope/wfmc/component/__init__.py
  A   Zope3/trunk/src/zope/wfmc/component/integration.py
  A   Zope3/trunk/src/zope/wfmc/component/integration.txt
  A   Zope3/trunk/src/zope/wfmc/component/tests.py
  U   Zope3/trunk/src/zope/wfmc/interfaces.py
  U   Zope3/trunk/src/zope/wfmc/process.py
  U   Zope3/trunk/src/zope/wfmc/tests.py
  U   Zope3/trunk/src/zope/wfmc/xpdl.txt

-=-
Modified: Zope3/trunk/src/zope/app/wfmc/meta.zcml
===================================================================
--- Zope3/trunk/src/zope/app/wfmc/meta.zcml	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/app/wfmc/meta.zcml	2005-05-09 17:07:09 UTC (rev 30314)
@@ -3,7 +3,7 @@
 
   <meta:directive namespace="http://namespaces.zope.org/wfmc"
                   name="xpdl"
-                  schema=".metadirectives.IdefineXpdl"
+                  schema=".metaconfigure.IdefineXpdl"
                   handler=".metaconfigure.defineXpdl" />
 
 </configure>

Modified: Zope3/trunk/src/zope/app/wfmc/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/wfmc/metaconfigure.py	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/app/wfmc/metaconfigure.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -11,18 +11,53 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""WFMC metaconfigure
+"""ZCML directives for loading XPDL workflow definitions.
 
 $Id: $
 """
 __docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+import zope.configuration.fields
 from zope import wfmc
 from zope.wfmc import xpdl
 from zope.app.component.metaconfigure import utility
+import zope.wfmc.interfaces
 
-def defineXpdl(_context, file, process, id):
+class IdefineXpdl(zope.interface.Interface):
+
+    file = zope.configuration.fields.Path(
+        title=u"File Name",
+        description=u"The name of the xpdl file to read.",
+        )
+
+    process = zope.schema.TextLine(
+        title=u"Process Name",
+        description=u"The name of the process to read.",
+        )
+
+    id = zope.schema.Id(
+        title=u"ID",
+        description=(u"The identifier to use for the process.  "
+                     u"Defaults to the process name."),
+        )
+
+    integration = zope.configuration.fields.GlobalObject(
+        title=u"Integration component",
+        description=(u"Python name of the integration object.  This"
+                      " must identify an object in a module using the"
+                      " full dotted name."),
+        )
+
+def defineXpdl(_context, file, process, id, integration):
     package = xpdl.read(open(file))
     definition = package[process]
     definition.id = id
 
+    if not zope.wfmc.interfaces.IIntegration.providedBy(integration):
+        raise TypeError("Not an IIntegration", integration)
+    
+    definition.integration = integration
+
     utility(_context, wfmc.interfaces.IProcessDefinition, definition, name=id)

Deleted: Zope3/trunk/src/zope/app/wfmc/metadirectives.py
===================================================================
--- Zope3/trunk/src/zope/app/wfmc/metadirectives.py	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/app/wfmc/metadirectives.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -1,39 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2005 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""ZCML directives for defining privileges.
-
-$Id: $
-"""
-__docformat__ = "reStructuredText"
-import zope.interface
-import zope.schema
-import zope.configuration.fields
-
-class IdefineXpdl(zope.interface.Interface):
-
-    file = zope.configuration.fields.Path(
-        title=u"File Name",
-        description=u"The name of the xpdl file to read.",
-        )
-
-    process = zope.schema.TextLine(
-        title=u"Process Name",
-        description=u"The name of the process to read.",
-        )
-
-    id = zope.schema.Id(
-        title=u"ID",
-        description=(u"The identifier to use for the process.  "
-                     u"Defaults to the process name."),
-        )

Modified: Zope3/trunk/src/zope/app/wfmc/zcml.txt
===================================================================
--- Zope3/trunk/src/zope/app/wfmc/zcml.txt	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/app/wfmc/zcml.txt	2005-05-09 17:07:09 UTC (rev 30314)
@@ -16,6 +16,7 @@
     ...             file="%(file_name)s" 
     ...             process="Publication"
     ...             id="example.publication"
+    ...             integration="zope.wfmc.component.integration"
     ...             />
     ...
     ...     </configure>
@@ -25,5 +26,10 @@
 
     >>> from zope.wfmc.interfaces import IProcessDefinition
     >>> import zope.component
-    >>> zope.component.getUtility(IProcessDefinition, 'example.publication')
+    >>> pd = zope.component.getUtility(IProcessDefinition, 
+    ...                                'example.publication')
+    >>> pd
     ProcessDefinition('example.publication')
+
+    >>> import zope.wfmc.component
+    >>> pd.integration = zope.wfmc.component.integration

Modified: Zope3/trunk/src/zope/wfmc/README.txt
===================================================================
--- Zope3/trunk/src/zope/wfmc/README.txt	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/wfmc/README.txt	2005-05-09 17:07:09 UTC (rev 30314)
@@ -162,6 +162,9 @@
 parameter, will be used to add a `publish` attribute to the workflow
 relevant data.
 
+Participants
+------------
+
 We've declared some applications, and we've wired them up to
 activities, but we still haven't specified any application code. Before
 we can specify application code, we need to consider who will be
@@ -183,36 +186,37 @@
     >>> pd.activities['author'].definePerformer('author')
     >>> pd.activities['review'].definePerformer('reviewer')
 
-For each of the participants provided, we need to provide a named
-adapter from an activity instance
-(`zope.wfmc.interfaces.IActivity`) to
-`zope.wfmc.interfaces.IParticipant`.  When a process needs to get a
-participant, it adapts the activity instance to `IParticipant` using a
-qualified name, consisting of the process-definition identifier, a
-dot, and the performer's participant identifier. If an adapter can't
-be found with that name, it tries again using just a dot followed by
-the participant identifier.  This way, participant adapters can be
-shared across multiple process definitions, or provided for a
-specific definition.  If an activity doesn't have a performer, then
-procedure above is used with an empty participant id.  We first look
-for an adapter with a name consisting of the process id followed by a
-dot, and then we look for an adapter with a single dot as it's name.
+Application Integration
+-----------------------
 
-Application implementations are provided as named adapters from
-participants to `zope.wfmc.interfaces.IWorkItem`.  As when looking up
-applications, we first look for an adapter with a name consisting of
-the process-definition id, a dot, and the application id.  If that
-fails, we look for an adapter consisting of a dot followed by the
-application id.  Workflow items provide a `start` method, which is
-used to start the work and pass input arguments.  It is the
-responsibility of the work item, at some later time, to call the
-`workitemFinished` method on the activity, to notify the activity that
-the work item was completed. Output parameters are passed to the
-`workitemFinished` method.
+To use a process definition to control application logic, we need to
+associate it with an "integration" object. 
 
-Let's implement participants for our process. We'll start with a
-generic participant:
+When a process needs to get a participant, it calls createParticipant
+on it's integration attribute, passing the process id and the
+performer id. If an activity doesn't have a
+performer, then procedure above is used with an empty performer id.
 
+Similarly, when a process needs a work item, it calls createWorkItem
+on it's integration attribute, passing the process id and the
+application id.
+
+Work items provide a `start` method, which is used to start the work
+and pass input arguments.  It is the responsibility of the work item,
+at some later time, to call the `workitemFinished` method on the
+activity, to notify the activity that the work item was
+completed. Output parameters are passed to the `workitemFinished`
+method.
+
+A simple way to create integration objects is with
+`zope.wfmc.attributeintegration.AttributeIntegration`.
+
+    >>> from zope.wfmc.attributeintegration import AttributeIntegration
+    >>> integration = AttributeIntegration()
+    >>> pd.integration = integration
+
+We'll start by defining a simple Participant class:
+
     >>> import zope.interface
     >>> from zope.wfmc import interfaces
 
@@ -223,12 +227,18 @@
     ...     def __init__(self, activity):
     ...         self.activity = activity
 
-    >>> zope.component.provideAdapter(Participant, name=".author")
-    >>> zope.component.provideAdapter(Participant, name=".reviewer")
-    >>> zope.component.provideAdapter(Participant, name=".")
+We set attributes on the integration for each participant:
 
-And finally, we can define adapters that implement our application:
+    >>> integration.authorParticipant   = Participant
+    >>> integration.reviewerParticipant = Participant
 
+We also define an attribute for participants for activities that don't
+have performers:
+
+    >>> integration.Participant = Participant
+
+Now we'll define our work-items. First we'll define some classes:
+
     >>> work_list = []
 
     >>> class ApplicationBase:
@@ -245,33 +255,33 @@
     ...     def finish(self):
     ...         self.participant.activity.workItemFinished(self)
 
-    >>> class Author(ApplicationBase):
-    ...     pass
-
-    >>> zope.component.provideAdapter(Author, name=".author")
-
     >>> class Review(ApplicationBase):
     ...     def finish(self, publish):
     ...         self.participant.activity.workItemFinished(self, publish)
 
-    >>> zope.component.provideAdapter(Review, name=".review")
-
     >>> class Publish(ApplicationBase):
     ...     def start(self):
     ...         print "Published"
     ...         self.finish()
 
-    >>> zope.component.provideAdapter(Publish, name=".publish")
-
     >>> class Reject(ApplicationBase):
     ...     def start(self):
     ...         print "Rejected"
     ...         self.finish()
 
-    >>> zope.component.provideAdapter(Reject, name=".reject")
+and then we'll hook them up with the integration object:
 
-Now, when we instantiate and start our workflow:
+    >>> integration.authorWorkItem  = ApplicationBase
+    >>> integration.reviewWorkItem  = Review
+    >>> integration.publishWorkItem = Publish
+    >>> integration.rejectWorkItem  = Reject
 
+Using workflow processes
+------------------------
+
+To use a process definition, instantiate it and call it's start method
+to start execution:
+
     >>> proc = pd()
     >>> proc.start()
     ... # doctest: +NORMALIZE_WHITESPACE
@@ -321,9 +331,10 @@
 If transitions are defined in an inconvenient order, then the workflow
 might not work as expected.  For example, let's modify the above
 process by switching the order of definition of some of the
-transitions:
+transitions.  We'll reuse our integration object from the previous
+example by passing it to the definition constructor:
 
-    >>> pd = process.ProcessDefinition('sample')
+    >>> pd = process.ProcessDefinition('sample', integration)
     >>> zope.component.provideUtility(pd, name=pd.id)
     >>> pd.defineActivities(
     ...     author = process.ActivityDefinition(),
@@ -395,7 +406,7 @@
 redefine the process:
 
 
-    >>> pd = process.ProcessDefinition('sample')
+    >>> pd = process.ProcessDefinition('sample', integration)
     >>> zope.component.provideUtility(pd, name=pd.id)
     >>> pd.defineActivities(
     ...     author = process.ActivityDefinition(),
@@ -549,9 +560,11 @@
 flows simultaneously to the two technical review activities.  The rest
 of the splits in this example are "xor" splits.
 
-Lets create our new workflow process:
+Lets create our new workflow process. We'll reuse our existing
+integration object:
 
     >>> Publication = process.ProcessDefinition('Publication')
+    >>> Publication.integration = integration
     >>> zope.component.provideUtility(Publication, name=Publication.id)
 
     >>> Publication.defineActivities(
@@ -698,24 +711,31 @@
     ...         author_name = activity.process.workflowRelevantData.author
     ...         self.user = authors[author_name]
 
-    >>> zope.component.provideAdapter(Author, name="Publication.author")
+In this example, we need to define a separate attribute for each participant:
 
+    >>> integration.authorParticipant = Author
+
 When the process is created, the author name will be passed in and
 assigned to the workflow-relevant data.  Our author class uses this
 information to select the named user.
 
     >>> class Reviewer(Participant):
     ...     user = reviewer
-    >>> zope.component.provideAdapter(Reviewer, name="Publication.reviewer")
+    >>> integration.reviewerParticipant = Reviewer
 
     >>> class Tech1(Participant):
     ...     user = tech1
-    >>> zope.component.provideAdapter(Tech1, name="Publication.tech1")
+    >>> integration.tech1Participant = Tech1
 
     >>> class Tech2(Participant):
     ...     user = tech2
-    >>> zope.component.provideAdapter(Tech2, name="Publication.tech2")
+    >>> integration.tech2Participant = Tech2
 
+We'll use our orginal participation class for activities without
+performers:
+
+    >>> integration.Participant = Participant
+
 Now we'll create our applications. Let's start with our author:
 
     >>> class ApplicationBase(object):
@@ -751,7 +771,7 @@
     ...         self.activity.process.applicationRelevantData.doc = doc
     ...         super(Prepare, self).finish()
 
-    >>> zope.component.provideAdapter(Prepare, name="Publication.prepare")
+    >>> integration.prepareWorkItem = Prepare
 
 Since we used the prepare application for revisions as well as initial
 preparation, we provide a summary method to show us what we have to do.
@@ -772,8 +792,7 @@
     ...     def finish(self, decision, changes):
     ...         self.activity.workItemFinished(self, decision, changes)
 
-    >>> zope.component.provideAdapter(TechReview,
-    ...                               name="Publication.tech_review")
+    >>> integration.tech_reviewWorkItem = TechReview
 
 Here, we provided a method to access the original document.
 
@@ -793,7 +812,7 @@
     ...     def finish(self, ed_changes):
     ...         self.activity.workItemFinished(self, True, (), ed_changes)
 
-    >>> zope.component.provideAdapter(Review, name="Publication.ed_review")
+    >>> integration.ed_reviewWorkItem = Review
 
 In this implementation, we decided to reject outright if either
 technical editor recommended rejection and to send work back to
@@ -818,7 +837,7 @@
     ...         self.activity.process.applicationRelevantData.doc = doc
     ...         super(Final, self).finish()
 
-    >>> zope.component.provideAdapter(Final, name="Publication.final")
+    >>> integration.finalWorkItem = Final
 
 In our this application, we simply update the document to reflect
 changes.
@@ -828,7 +847,7 @@
     ...     def finish(self, ed_changes):
     ...         self.activity.workItemFinished(self, ed_changes)
 
-    >>> zope.component.provideAdapter(ReviewFinal, name="Publication.rfinal")
+    >>> integration.rfinalWorkItem = ReviewFinal
 
 Our process now returns data.  When we create a process, we need to
 supply an object that it can call back to:

Added: Zope3/trunk/src/zope/wfmc/attributeintegration.py
===================================================================
--- Zope3/trunk/src/zope/wfmc/attributeintegration.py	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/wfmc/attributeintegration.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Attribute-based integration components
+
+
+$Id$
+"""
+
+from zope.wfmc import interfaces
+from zope import interface
+
+class AttributeIntegration:
+    """Integration component that uses simple attributes
+
+    Subclasses provide attributes with suffices Participant or Application to
+    provide participant and application factories of a given name.
+    """
+
+    interface.implements(interfaces.IIntegration)
+    
+    def createParticipant(self, activity,
+                          process_definition_identifier, performer):
+        return getattr(self, performer+'Participant')(activity)
+        
+
+    def createWorkItem(self, participant,
+                       process_definition_identifier, application):
+        return getattr(self, application+'WorkItem')(participant)


Property changes on: Zope3/trunk/src/zope/wfmc/attributeintegration.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Copied: Zope3/trunk/src/zope/wfmc/component/__init__.py (from rev 30239, Zope3/trunk/src/zope/wfmc/__init__.py)
===================================================================
--- Zope3/trunk/src/zope/wfmc/__init__.py	2005-05-04 14:12:06 UTC (rev 30239)
+++ Zope3/trunk/src/zope/wfmc/component/__init__.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -0,0 +1,19 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Adapter-based workflow integration
+
+$Id$
+"""
+
+from zope.wfmc.component import integration

Copied: Zope3/trunk/src/zope/wfmc/component/integration.py (from rev 30239, Zope3/trunk/src/zope/wfmc/process.py)
===================================================================
--- Zope3/trunk/src/zope/wfmc/process.py	2005-05-04 14:12:06 UTC (rev 30239)
+++ Zope3/trunk/src/zope/wfmc/component/integration.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Processes
+
+$Id$
+"""
+
+from zope import component, interface
+from zope.wfmc import interfaces
+
+interface.moduleProvides(interfaces.IIntegration)
+    
+def createParticipant(activity, process_definition_identifier, performer):
+    participant = component.queryAdapter(
+        activity, interfaces.IParticipant,
+        process_definition_identifier + '.' + performer)
+
+    if participant is None:
+        participant = component.getAdapter(
+            activity, interfaces.IParticipant, '.' + performer)
+
+    return participant
+
+def createWorkItem(participant,
+                   process_definition_identifier, application):
+
+    workitem = component.queryAdapter(
+        participant, interfaces.IWorkItem,
+        process_definition_identifier + '.' + application)
+    if workitem is None:
+        workitem = component.getAdapter(
+            participant, interfaces.IWorkItem, '.' + application)
+
+    return workitem

Copied: Zope3/trunk/src/zope/wfmc/component/integration.txt (from rev 30239, Zope3/trunk/src/zope/wfmc/README.txt)
===================================================================
--- Zope3/trunk/src/zope/wfmc/README.txt	2005-05-04 14:12:06 UTC (rev 30239)
+++ Zope3/trunk/src/zope/wfmc/component/integration.txt	2005-05-09 17:07:09 UTC (rev 30314)
@@ -0,0 +1,186 @@
+Adapter-based workflow application integration
+==============================================
+
+This package provides an adapter-based workflow integration
+component. The component is implemented by the integration module:
+
+    >>> from zope.wfmc import process, component
+    >>> import zope.component
+
+To demonstrate how this works, we'll use the simple wfmc example
+process:
+
+    >>> pd = process.ProcessDefinition('sample', component.integration)
+    >>> zope.component.provideUtility(pd, name=pd.id)
+    >>> pd.defineActivities(
+    ...     author = process.ActivityDefinition(),
+    ...     review = process.ActivityDefinition(),
+    ...     publish = process.ActivityDefinition(),
+    ...     reject = process.ActivityDefinition(),
+    ...     )
+    >>> pd.defineTransitions(
+    ...     process.TransitionDefinition('author', 'review'),
+    ...     process.TransitionDefinition(
+    ...         'review', 'publish', condition=lambda data: data.publish),
+    ...     process.TransitionDefinition('review', 'reject'),
+    ...     )
+
+
+    >>> pd.defineApplications(
+    ...     author = process.Application(),
+    ...     review = process.Application(
+    ...         process.OutputParameter('publish')),
+    ...     publish = process.Application(),
+    ...     reject = process.Application(),
+    ...     )
+
+    >>> pd.activities['author'].addApplication('author')
+    >>> pd.activities['review'].addApplication('review', ['publish'])
+    >>> pd.activities['publish'].addApplication('publish')
+    >>> pd.activities['reject'].addApplication('reject')
+
+    >>> pd.defineParticipants(
+    ...     author   = process.Participant(),
+    ...     reviewer = process.Participant(),
+    ...     )
+
+    >>> pd.activities['author'].definePerformer('author')
+    >>> pd.activities['review'].definePerformer('reviewer')
+
+For each of the participants provided, we need to provide a named
+adapter from an activity instance
+(`zope.wfmc.interfaces.IActivity`) to
+`zope.wfmc.interfaces.IParticipant`.  When a process needs to get a
+participant, it adapts the activity instance to `IParticipant` using a
+qualified name, consisting of the process-definition identifier, a
+dot, and the performer's participant identifier. If an adapter can't
+be found with that name, it tries again using just a dot followed by
+the participant identifier.  This way, participant adapters can be
+shared across multiple process definitions, or provided for a
+specific definition.  If an activity doesn't have a performer, then
+procedure above is used with an empty participant id.  We first look
+for an adapter with a name consisting of the process id followed by a
+dot, and then we look for an adapter with a single dot as it's name.
+
+Application implementations are provided as named adapters from
+participants to `zope.wfmc.interfaces.IWorkItem`.  As when looking up
+applications, we first look for an adapter with a name consisting of
+the process-definition id, a dot, and the application id.  If that
+fails, we look for an adapter consisting of a dot followed by the
+application id.  Workflow items provide a `start` method, which is
+used to start the work and pass input arguments.  It is the
+responsibility of the work item, at some later time, to call the
+`workitemFinished` method on the activity, to notify the activity that
+the work item was completed. Output parameters are passed to the
+`workitemFinished` method.
+
+Let's implement participants for our process. We'll start with a
+generic participant:
+
+    >>> import zope.interface
+    >>> from zope.wfmc import interfaces
+
+    >>> class Participant(object):
+    ...     zope.component.adapts(interfaces.IActivity)
+    ...     zope.interface.implements(interfaces.IParticipant)
+    ...
+    ...     def __init__(self, activity):
+    ...         self.activity = activity
+
+    >>> zope.component.provideAdapter(Participant, name=".author")
+    >>> zope.component.provideAdapter(Participant, name=".reviewer")
+    >>> zope.component.provideAdapter(Participant, name=".")
+
+And finally, we can define adapters that implement our application:
+
+    >>> work_list = []
+
+    >>> class ApplicationBase:
+    ...     zope.component.adapts(interfaces.IParticipant)
+    ...     zope.interface.implements(interfaces.IWorkItem)
+    ...
+    ...     def __init__(self, participant):
+    ...         self.participant = participant
+    ...         work_list.append(self)
+    ...
+    ...     def start(self):
+    ...         pass
+    ...
+    ...     def finish(self):
+    ...         self.participant.activity.workItemFinished(self)
+
+    >>> class Author(ApplicationBase):
+    ...     pass
+
+    >>> zope.component.provideAdapter(Author, name=".author")
+
+    >>> class Review(ApplicationBase):
+    ...     def finish(self, publish):
+    ...         self.participant.activity.workItemFinished(self, publish)
+
+    >>> zope.component.provideAdapter(Review, name=".review")
+
+    >>> class Publish(ApplicationBase):
+    ...     def start(self):
+    ...         print "Published"
+    ...         self.finish()
+
+    >>> zope.component.provideAdapter(Publish, name=".publish")
+
+    >>> class Reject(ApplicationBase):
+    ...     def start(self):
+    ...         print "Rejected"
+    ...         self.finish()
+
+    >>> zope.component.provideAdapter(Reject, name=".reject")
+
+We'll see the workflow executing by registering a subscriber that logs
+workflow events:
+
+    >>> def log_workflow(event):
+    ...     print event
+
+    >>> import zope.event
+    >>> zope.event.subscribers.append(log_workflow)
+
+Now, when we instantiate and start our workflow:
+
+    >>> proc = pd()
+    >>> proc.start()
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ProcessStarted(Process('sample'))
+    Transition(None, Activity('sample.author'))
+    ActivityStarted(Activity('sample.author'))
+
+We transition into the author activity and wait for work to get done.
+To move forward, we need to get at the authoring work item, so we can
+finish it.  Our work items add themselves to a work list, so we can
+get the item from the list.
+
+    >>> item = work_list.pop()
+
+Now we can finish the work item, by calling it's finish method:
+
+    >>> item.finish()
+    WorkItemFinished('author')
+    ActivityFinished(Activity('sample.author'))
+    Transition(Activity('sample.author'), Activity('sample.review'))
+    ActivityStarted(Activity('sample.review'))
+
+We see that we transitioned to the review activity.  Note that the
+`finish` method isn't a part of the workflow APIs.  It was defined by
+our sample classes. Other applications could use different mechanisms.
+
+Now, we'll finish the review process by calling the review work item's
+`finish`. We'll pass `False`, indicating that the content should not
+be published:
+
+    >>> work_list.pop().finish(False)
+    WorkItemFinished('review')
+    ActivityFinished(Activity('sample.review'))
+    Transition(Activity('sample.review'), Activity('sample.reject'))
+    ActivityStarted(Activity('sample.reject'))
+    Rejected
+    WorkItemFinished('reject')
+    ActivityFinished(Activity('sample.reject'))
+    ProcessFinished(Process('sample'))

Copied: Zope3/trunk/src/zope/wfmc/component/tests.py (from rev 30239, Zope3/trunk/src/zope/wfmc/tests.py)
===================================================================
--- Zope3/trunk/src/zope/wfmc/tests.py	2005-05-04 14:12:06 UTC (rev 30239)
+++ Zope3/trunk/src/zope/wfmc/component/tests.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -0,0 +1,37 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test hookup
+
+$Id$
+"""
+import os
+import unittest
+import zope.event
+from zope.component import testing
+from zope.testing import doctest
+
+def tearDown(test):
+    testing.tearDown(test)
+    zope.event.subscribers.pop()
+
+def setUp(test):
+    test.globs['this_directory'] = os.path.dirname(__file__)
+    testing.setUp(test)
+
+def test_suite():
+    return doctest.DocFileSuite('integration.txt',
+                                setUp=testing.setUp, tearDown=tearDown)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Modified: Zope3/trunk/src/zope/wfmc/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/wfmc/interfaces.py	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/wfmc/interfaces.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -18,6 +18,31 @@
 
 from zope import interface
 
+class IIntegration(interface.Interface):
+    """Integration of a workflow definition with an application environment
+
+    IIntegration objects provide methods for integratibg workflow
+    process definition with an application environment. 
+    """
+
+    
+    def createParticipant(activity, process_definition_identifier, performer):
+        """Create a participant for an activity
+
+        The process id and especially the perfomer (id) are used to
+        select an appropriate participant type.
+        
+        """
+
+    def createWorkItem(participant,
+                       process_definition_identifier, application):
+        """Create a work for the given participant
+
+        The process id and especially the application (id) are used to
+        select an appropriate work-item type.
+        
+        """
+
 class IProcessDefinition(interface.Interface):
     """Process definition
     """
@@ -28,6 +53,16 @@
 
     description = interface.Attribute("Description")
 
+    integration = interface.Attribute(
+        """Environment-integration component
+
+        The integration component is used to hook up a process
+        definition with an application environment.
+
+        This is an IIntegration.
+        """
+        )
+
     participants = interface.Attribute(
         """Process participants
 

Modified: Zope3/trunk/src/zope/wfmc/process.py
===================================================================
--- Zope3/trunk/src/zope/wfmc/process.py	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/wfmc/process.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -32,8 +32,9 @@
 
     interface.implements(interfaces.IProcessDefinition)
 
-    def __init__(self, id):
+    def __init__(self, id, integration=None):
         self.id = id
+        self.integration = integration
         self.activities = {}
         self.transitions = []
         self.applications = {}
@@ -215,15 +216,19 @@
         zope.event.notify(ProcessStarted(self))
         self.transition(None, (self.startTransition, ))
 
+    def outputs(self):
+        outputs = []
+        for parameter in self.definition.parameters:
+            if parameter.output:
+                outputs.append(
+                    getattr(self.workflowRelevantData,
+                            parameter.__name__))
+        
+        return outputs
+
     def _finish(self):
         if self.context is not None:
-            args = []
-            for parameter in self.definition.parameters:
-                if parameter.output:
-                    args.append(
-                        getattr(self.workflowRelevantData,
-                                parameter.__name__))
-            self.context.processFinished(self, *args)
+            self.context.processFinished(self, *self.outputs())
             
         zope.event.notify(ProcessFinished(self))
         
@@ -293,28 +298,24 @@
         self.process = process
         self.activity_definition_identifier = definition.id
 
+        integration = process.definition.integration
+
         workitems = {}
         if definition.applications:
 
-            performer = component.queryAdapter(
-                self, interfaces.IParticipant,
-                process.process_definition_identifier
-                + '.' + definition.performer)
-
-            if performer is None:
-                performer = component.getAdapter(
-                    self, interfaces.IParticipant,
-                    '.' + definition.performer)
-
+            participant = integration.createParticipant(
+                self, 
+                process.process_definition_identifier,
+                definition.performer,
+                )
+                
             i = 0
             for application, formal, actual in definition.applications:
-                workitem = component.queryAdapter(
-                    performer, interfaces.IWorkItem,
-                    process.process_definition_identifier + '.' + application)
-                if workitem is None:
-                    workitem = component.getAdapter(
-                        performer, interfaces.IWorkItem,
-                        '.' + application)
+                workitem = integration.createWorkItem(
+                    participant, 
+                    process.process_definition_identifier,
+                    application,
+                    )
                 i += 1
                 workitem.id = i
                 workitems[i] = workitem, application, formal, actual

Modified: Zope3/trunk/src/zope/wfmc/tests.py
===================================================================
--- Zope3/trunk/src/zope/wfmc/tests.py	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/wfmc/tests.py	2005-05-09 17:07:09 UTC (rev 30314)
@@ -68,8 +68,13 @@
     ...     def __init__(self, activity):
     ...         self.activity = activity
 
-    >>> component.provideAdapter(Participant, name=".")
+    >>> from zope.wfmc.attributeintegration import AttributeIntegration
+    >>> integration = AttributeIntegration()
+    >>> pd.integration = integration
 
+    >>> integration.Participant = Participant
+
+
     >>> class Eek:
     ...     component.adapts(interfaces.IParticipant)
     ...     interface.implements(interfaces.IWorkItem)
@@ -80,8 +85,9 @@
     ...     def start(self, x, y):
     ...         print x, y
 
-    >>> component.provideAdapter(Eek, name='.eek')
 
+    >>> integration.eekWorkItem = Eek
+
     >>> proc = pd()
     >>> proc.start(99, 42)
     99 42
@@ -124,7 +130,6 @@
 def test_suite():
     from zope.testing import doctest
     suite = unittest.TestSuite()
-    # suite.addTest(doctest.DocTestSuite())
     suite.addTest(doctest.DocFileSuite('README.txt', tearDown=tearDown,
                                        setUp=testing.setUp))
     suite.addTest(doctest.DocFileSuite(

Modified: Zope3/trunk/src/zope/wfmc/xpdl.txt
===================================================================
--- Zope3/trunk/src/zope/wfmc/xpdl.txt	2005-05-09 16:57:03 UTC (rev 30313)
+++ Zope3/trunk/src/zope/wfmc/xpdl.txt	2005-05-09 17:07:09 UTC (rev 30314)
@@ -21,6 +21,9 @@
     {u'Publication': ProcessDefinition(u'Publication')}
 
     >>> pd = package[u'Publication']
+    >>> from zope.wfmc.attributeintegration import AttributeIntegration
+    >>> integration = AttributeIntegration()
+    >>> pd.integration = integration
 
 Now, having read the process definition, we can use it as we did
 before (in "README.txt").  As before, we'll create an event subscriber
@@ -65,21 +68,21 @@
     ...         author_name = activity.process.workflowRelevantData.author
     ...         self.user = authors[author_name]
 
-    >>> zope.component.provideAdapter(Author, name="Publication.author")
+    >>> integration.authorParticipant = Author
 
     >>> class Reviewer(Participant):
     ...     user = reviewer
-    >>> zope.component.provideAdapter(Reviewer, name="Publication.reviewer")
+    >>> integration.reviewerParticipant = Reviewer
 
     >>> class Tech1(Participant):
     ...     user = tech1
-    >>> zope.component.provideAdapter(Tech1, name="Publication.tech1")
+    >>> integration.tech1Participant = Tech1
 
     >>> class Tech2(Participant):
     ...     user = tech2
-    >>> zope.component.provideAdapter(Tech2, name="Publication.tech2")
+    >>> integration.tech2Participant = Tech2
 
-    >>> zope.component.provideAdapter(Participant, name="Publication.System")
+    >>> integration.SystemParticipant = Participant
 
     >>> class ApplicationBase(object):
     ...     zope.component.adapts(interfaces.IParticipant)
@@ -114,7 +117,7 @@
     ...         self.activity.process.applicationRelevantData.doc = doc
     ...         super(Prepare, self).finish()
 
-    >>> zope.component.provideAdapter(Prepare, name="Publication.prepare")
+    >>> integration.prepareWorkItem = Prepare
 
     >>> class TechReview(ApplicationBase):
     ...
@@ -124,8 +127,7 @@
     ...     def finish(self, decision, changes):
     ...         self.activity.workItemFinished(self, decision, changes)
 
-    >>> zope.component.provideAdapter(TechReview, 
-    ...                               name="Publication.tech_review")
+    >>> integration.tech_reviewWorkItem = TechReview
 
     >>> class Review(TechReview):
     ...
@@ -143,7 +145,7 @@
     ...     def finish(self, ed_changes):
     ...         self.activity.workItemFinished(self, True, (), ed_changes)
 
-    >>> zope.component.provideAdapter(Review, name="Publication.ed_review")
+    >>> integration.ed_reviewWorkItem = Review
 
     >>> class Final(ApplicationBase):
     ...
@@ -160,14 +162,14 @@
     ...         self.activity.process.applicationRelevantData.doc = doc
     ...         super(Final, self).finish()
 
-    >>> zope.component.provideAdapter(Final, name="Publication.final")
+    >>> integration.finalWorkItem = Final
 
     >>> class ReviewFinal(TechReview):
     ...
     ...     def finish(self, ed_changes):
     ...         self.activity.workItemFinished(self, ed_changes)
 
-    >>> zope.component.provideAdapter(ReviewFinal, name="Publication.rfinal")
+    >>> integration.rfinalWorkItem = ReviewFinal
 
 
     >>> class Publish:
@@ -185,14 +187,14 @@
     ...         self.participant.activity.workItemFinished(self)
 
 
-    >>> zope.component.provideAdapter(Publish, name="Publication.publish")
+    >>> integration.publishWorkItem = Publish
 
     >>> class Reject(Publish):
     ...     def start(self):
     ...         print "Rejected"
     ...         self.finish()
     
-    >>> zope.component.provideAdapter(Reject, name="Publication.reject")
+    >>> integration.rejectWorkItem = Reject
 
 and a process context, so we can pass parameters:
 



More information about the Zope3-Checkins mailing list