--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unittest_selectors.py Mon Feb 16 16:24:24 2009 +0100
@@ -0,0 +1,80 @@
+"""unit tests for selectors mechanism
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb.vregistry import Selector, AndSelector, OrSelector
+
+class _1_(Selector):
+ def __call__(self, *args, **kwargs):
+ return 1
+
+class _0_(Selector):
+ def __call__(self, *args, **kwargs):
+ return 0
+
+def _2_(*args, **kwargs):
+ return 2
+
+
+class SelectorsTC(TestCase):
+ def test_basic_and(self):
+ selector = _1_() & _1_()
+ self.assertEquals(selector(None), 2)
+ selector = _1_() & _0_()
+ self.assertEquals(selector(None), 0)
+ selector = _0_() & _1_()
+ self.assertEquals(selector(None), 0)
+
+ def test_basic_or(self):
+ selector = _1_() | _1_()
+ self.assertEquals(selector(None), 1)
+ selector = _1_() | _0_()
+ self.assertEquals(selector(None), 1)
+ selector = _0_() | _1_()
+ self.assertEquals(selector(None), 1)
+ selector = _0_() | _0_()
+ self.assertEquals(selector(None), 0)
+
+ def test_selector_and_function(self):
+ selector = _1_() & _2_
+ self.assertEquals(selector(None), 3)
+ selector = _2_ & _1_()
+ self.assertEquals(selector(None), 3)
+
+ def test_three_and(self):
+ selector = _1_() & _1_() & _1_()
+ self.assertEquals(selector(None), 3)
+ selector = _1_() & _0_() & _1_()
+ self.assertEquals(selector(None), 0)
+ selector = _0_() & _1_() & _1_()
+ self.assertEquals(selector(None), 0)
+
+ def test_three_or(self):
+ selector = _1_() | _1_() | _1_()
+ self.assertEquals(selector(None), 1)
+ selector = _1_() | _0_() | _1_()
+ self.assertEquals(selector(None), 1)
+ selector = _0_() | _1_() | _1_()
+ self.assertEquals(selector(None), 1)
+ selector = _0_() | _0_() | _0_()
+ self.assertEquals(selector(None), 0)
+
+ def test_composition(self):
+ selector = (_1_() & _1_()) & (_1_() & _1_())
+ self.failUnless(isinstance(selector, AndSelector))
+ self.assertEquals(len(selector.selectors), 4)
+ self.assertEquals(selector(None), 4)
+ selector = (_1_() & _0_()) | (_1_() & _1_())
+ self.failUnless(isinstance(selector, OrSelector))
+ self.assertEquals(len(selector.selectors), 2)
+ self.assertEquals(selector(None), 2)
+
+
+if __name__ == '__main__':
+ unittest_main()
+
--- a/vregistry.py Mon Feb 16 14:18:13 2009 +0100
+++ b/vregistry.py Mon Feb 16 16:24:24 2009 +0100
@@ -543,3 +543,65 @@
return 0
return selector
+
+# selector base classes and operations ########################################
+class Selector(object):
+ """base class for selector classes providing implementation
+ for operators ``&`` and ``|``
+
+ This class is only here to give access to binary operators, the
+ selector logic itself should be implemented in the __call__ method
+ """
+ def __init__(self, *selectors):
+ self.selectors = self.merge_selectors(selectors)
+
+ @classmethod
+ def merge_selectors(cls, selectors):
+ """merge selectors when possible :
+
+ AndSelector(AndSelector(sel1, sel2), AndSelector(sel3, sel4))
+ ==> AndSelector(sel1, sel2, sel3, sel4)
+ """
+ merged_selectors = []
+ for selector in selectors:
+ if isinstance(selector, cls):
+ merged_selectors += selector.selectors
+ else:
+ merged_selectors.append(selector)
+ return merged_selectors
+
+ def __and__(self, other):
+ return AndSelector(self, other)
+
+ def __or__(self, other):
+ return OrSelector(self, other)
+
+ __ror__ = __or__ # for cases like (function | selector)
+ __rand__ = __and__ # for cases like (function & selector)
+ # XXX (function | function) or (function & function) not managed yet
+
+ def __call__(self, cls, *args, **kwargs):
+ return NotImplementedError("selector %s must implement its logic "
+ "in its __call__ method" % self.__class__.__name__)
+
+
+class AndSelector(Selector):
+ """and-chained selectors (formerly known as chainall)"""
+ def __call__(self, cls, *args, **kwargs):
+ score = 0
+ for selector in self.selectors:
+ partscore = selector(cls, *args, **kwargs)
+ if not partscore:
+ return 0
+ score += partscore
+ return score
+
+
+class OrSelector(Selector):
+ """or-chained selectors (formerly known as chainfirst)"""
+ def __call__(self, cls, *args, **kwargs):
+ for selector in self.selectors:
+ partscore = selector(cls, *args, **kwargs)
+ if partscore:
+ return partscore
+ return 0