[predicates] fix 'adaptable' implementation, used to return 0 on a rset with individualy adaptable entities of different types. Closes #2709663 oldstable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 04 Feb 2013 14:39:55 +0100
brancholdstable
changeset 8858 51fdbbbd07b2
parent 8857 5d08086c3e6d
child 8859 6ed22ac7257c
[predicates] fix 'adaptable' implementation, used to return 0 on a rset with individualy adaptable entities of different types. Closes #2709663
predicates.py
test/unittest_predicates.py
--- a/predicates.py	Wed Apr 03 14:33:20 2013 +0200
+++ b/predicates.py	Mon Feb 04 14:39:55 2013 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -501,11 +501,30 @@
 
     def __call__(self, cls, req, **kwargs):
         kwargs.setdefault('accept_none', False)
-        # being adaptable to an interface should takes precedence other is_instance('Any'),
-        # but not other explicit is_instance('SomeEntityType'), and:
+        score = super(adaptable, self).__call__(cls, req, **kwargs)
+        if score == 0 and kwargs.get('rset') and len(kwargs['rset']) > 1 and not 'row' in kwargs:
+            # on rset containing several entity types, each row may be
+            # individually adaptable, while the whole rset won't be if the
+            # same adapter can't be used for each type
+            for row in xrange(len(kwargs['rset'])):
+                kwargs.setdefault('col', 0)
+                _score = super(adaptable, self).__call__(cls, req, row=row, **kwargs)
+                if not _score:
+                    return 0
+                # adjust score per row as expected by default adjust_score
+                # implementation
+                score += self.adjust_score(_score)
+        else:
+            score = self.adjust_score(score)
+        return score
+
+    @staticmethod
+    def adjust_score(score):
+        # being adaptable to an interface should takes precedence other
+        # is_instance('Any'), but not other explicit
+        # is_instance('SomeEntityType'), and, for **a single entity**:
         # * is_instance('Any') score is 1
         # * is_instance('SomeEntityType') score is at least 2
-        score = super(adaptable, self).__call__(cls, req, **kwargs)
         if score >= 2:
             return score - 0.5
         if score == 1:
--- a/test/unittest_predicates.py	Wed Apr 03 14:33:20 2013 +0200
+++ b/test/unittest_predicates.py	Mon Feb 04 14:39:55 2013 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -27,6 +27,7 @@
                                 multi_lines_rset, score_entity, is_in_state,
                                 rql_condition, relation_possible)
 from cubicweb.selectors import on_transition # XXX on_transition is deprecated
+from cubicweb.view import EntityAdapter
 from cubicweb.web import action
 
 
@@ -337,6 +338,22 @@
         selector = rql_condition('U login "toto"', user_condition=True)
         self.assertEqual(selector(None, req), 0)
 
+
+class AdaptablePredicateTC(CubicWebTC):
+
+    def test_multiple_entity_types_rset(self):
+        class CWUserIWhatever(EntityAdapter):
+            __regid__ = 'IWhatever'
+            __select__ = is_instance('CWUser')
+        class CWGroupIWhatever(EntityAdapter):
+            __regid__ = 'IWhatever'
+            __select__ = is_instance('CWGroup')
+        with self.temporary_appobjects(CWUserIWhatever, CWGroupIWhatever):
+            req = self.request()
+            selector = adaptable('IWhatever')
+            rset = req.execute('Any X WHERE X is IN(CWGroup, CWUser)')
+            self.assertTrue(selector(None, req, rset=rset))
+
 if __name__ == '__main__':
     unittest_main()