[workflow selectors] introduced new on_fire_transition hook selector, deprecated on_transition stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 02 May 2011 17:24:51 +0200
branchstable
changeset 7297 117dbb11a42e
parent 7295 b6fd14ee491e
child 7298 a448e470c150
[workflow selectors] introduced new on_fire_transition hook selector, deprecated on_transition + minor docstring cleanup
doc/book/en/devrepo/vreg.rst
selectors.py
--- a/doc/book/en/devrepo/vreg.rst	Mon May 02 15:46:03 2011 +0200
+++ b/doc/book/en/devrepo/vreg.rst	Mon May 02 17:24:51 2011 +0200
@@ -79,7 +79,7 @@
 .. autoclass:: cubicweb.selectors.has_add_permission
 .. autoclass:: cubicweb.selectors.has_mimetype
 .. autoclass:: cubicweb.selectors.is_in_state
-.. autoclass:: cubicweb.selectors.on_transition
+.. autoclass:: cubicweb.selectors.on_fire_transition
 .. autoclass:: cubicweb.selectors.implements
 
 
--- a/selectors.py	Mon May 02 15:46:03 2011 +0200
+++ b/selectors.py	Mon May 02 17:24:51 2011 +0200
@@ -196,7 +196,7 @@
 from warnings import warn
 from operator import eq
 
-from logilab.common.deprecation import class_renamed
+from logilab.common.deprecation import class_renamed, deprecated
 from logilab.common.compat import all, any
 from logilab.common.interface import implements as implements_iface
 
@@ -1178,6 +1178,7 @@
         except Unauthorized:
             return 0
 
+# workflow selectors ###########################################################
 
 class is_in_state(score_entity):
     """Return 1 if entity is in one of the states given as argument list
@@ -1189,9 +1190,8 @@
     * you must use the latest tr info thru the workflow adapter for repository
       side checking of the current state
 
-    In debug mode, this selector can raise:
-    :raises: :exc:`ValueError` for unknown states names
-        (etype workflow only not checked in custom workflow)
+    In debug mode, this selector can raise :exc:`ValueError` for unknown states names
+    (only checked on entities without a custom workflow)
 
     :rtype: int
     """
@@ -1231,40 +1231,45 @@
                            ','.join(str(s) for s in self.expected))
 
 
-class on_transition(is_in_state):
-    """Return 1 if entity is in one of the transitions given as argument list
-
-    Especially useful to match passed transition to enable notifications when
-    your workflow allows several transition to the same states.
+def on_fire_transition(etype, tr_name, from_state_name=None):
+    """Return 1 when entity of the type `etype` is going through transition of
+    the name `tr_name`. If `from_state_name` is specified, this selector will
+    also check the incoming state.
 
-    Note that if workflow `change_state` adapter method is used, this selector
-    will not be triggered.
+    You should use this selector on 'after_add_entity' hook, since it's actually
+    looking for addition of `TrInfo` entities. Hence in the hook, `self.entity`
+    will reference the matching `TrInfo` entity, allowing to get all the
+    transition details (including the entity to which is applied the transition
+    but also its original state, transition, destination state, user...).  See
+    :class:`cubicweb.entities.wfobjs.TrInfo` for more information.
+    """
+    def match_etype_and_transition(trinfo):
+        # is_instance() first two arguments are 'cls' (unused, so giving None is
+        # fine) and the request/session
+        return (trinfo.transition.name == tr_name
+                and is_instance(etype)(None, trinfo._cw, entity=trinfo.for_entity))
 
-    You should use this instead of your own :class:`score_entity` selector to
-    avoid some gotchas:
-
-    * possible views gives a fake entity with no state
-    * you must use the latest tr info thru the workflow adapter for repository
-      side checking of the current state
+    return is_instance('TrInfo') & score_entity(match_etype_and_transition)
 
-    In debug mode, this selector can raise:
-    :raises: :exc:`ValueError` for unknown transition names
-        (etype workflow only not checked in custom workflow)
 
-    :rtype: int
+class match_transition(ExpectedValueSelector):
+    """Return 1 if `transition` argument is found in the input context which has
+    a `.name` attribute matching one of the expected names given to the
+    initializer.
+
+    This selector is expected to be used to customise the status change form in
+    the web ui.
     """
-    def _score(self, adapted):
-        trinfo = adapted.latest_trinfo()
-        if trinfo and trinfo.by_transition:
-            return trinfo.by_transition[0].name in self.expected
-
-    def _validate(self, adapted):
-        wf = adapted.current_workflow
-        valid = [n.name for n in wf.reverse_transition_of]
-        unknown = sorted(self.expected.difference(valid))
-        if unknown:
-            raise ValueError("%s: unknown transition(s): %s"
-                             % (wf.name, ",".join(unknown)))
+    @lltrace
+    def __call__(self, cls, req, transition=None, **kwargs):
+        # XXX check this is a transition that apply to the object?
+        if transition is None:
+            treid = req.form.get('treid', None)
+            if treid:
+                transition = req.entity_from_eid(treid)
+        if transition is not None and getattr(transition, 'name', None) in self.expected:
+            return 1
+        return 0
 
 
 # logged user selectors ########################################################
@@ -1504,23 +1509,6 @@
 
 # Other selectors ##############################################################
 
-# XXX deprecated ? maybe use on_transition selector instead ?
-class match_transition(ExpectedValueSelector):
-    """Return 1 if `transition` argument is found in the input context which has
-    a `.name` attribute matching one of the expected names given to the
-    initializer.
-    """
-    @lltrace
-    def __call__(self, cls, req, transition=None, **kwargs):
-        # XXX check this is a transition that apply to the object?
-        if transition is None:
-            treid = req.form.get('treid', None)
-            if treid:
-                transition = req.entity_from_eid(treid)
-        if transition is not None and getattr(transition, 'name', None) in self.expected:
-            return 1
-        return 0
-
 
 class match_exception(ExpectedValueSelector):
     """Return 1 if a view is specified an as its registry id is in one of the
@@ -1544,6 +1532,47 @@
 
 ## deprecated stuff ############################################################
 
+
+class on_transition(is_in_state):
+    """Return 1 if entity is in one of the transitions given as argument list
+
+    Especially useful to match passed transition to enable notifications when
+    your workflow allows several transition to the same states.
+
+    Note that if workflow `change_state` adapter method is used, this selector
+    will not be triggered.
+
+    You should use this instead of your own :class:`score_entity` selector to
+    avoid some gotchas:
+
+    * possible views gives a fake entity with no state
+    * you must use the latest tr info thru the workflow adapter for repository
+      side checking of the current state
+
+    In debug mode, this selector can raise:
+    :raises: :exc:`ValueError` for unknown transition names
+        (etype workflow only not checked in custom workflow)
+
+    :rtype: int
+    """
+    @deprecated('[3.12] on_transition is deprecated, you should rather use '
+                'on_fire_transition(etype, trname)')
+    def __init__(self, *expected):
+        super(on_transition, self).__init__(*expected)
+
+    def _score(self, adapted):
+        trinfo = adapted.latest_trinfo()
+        if trinfo and trinfo.by_transition:
+            return trinfo.by_transition[0].name in self.expected
+
+    def _validate(self, adapted):
+        wf = adapted.current_workflow
+        valid = [n.name for n in wf.reverse_transition_of]
+        unknown = sorted(self.expected.difference(valid))
+        if unknown:
+            raise ValueError("%s: unknown transition(s): %s"
+                             % (wf.name, ",".join(unknown)))
+
 entity_implements = class_renamed('entity_implements', is_instance)
 
 class _but_etype(EntitySelector):