changeset 967:afd230972167

data: Improve pagination filters code, add `defined` and `not_empty`.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 08 Oct 2017 09:30:59 -0700
parents cc55740860de
children 20f49786937c
files piecrust/data/filters.py
diffstat 1 files changed, 106 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/data/filters.py	Sat Oct 07 12:32:48 2017 -0700
+++ b/piecrust/data/filters.py	Sun Oct 08 09:30:59 2017 -0700
@@ -4,79 +4,6 @@
 logger = logging.getLogger(__name__)
 
 
-class PaginationFilter(object):
-    def __init__(self):
-        self.root_clause = None
-
-    @property
-    def is_empty(self):
-        return self.root_clause is None
-
-    def addClause(self, clause):
-        self._ensureRootClause()
-        self.root_clause.addClause(clause)
-
-    def addClausesFromConfig(self, config):
-        self._ensureRootClause()
-        self._addClausesFromConfigRecursive(config, self.root_clause)
-
-    def pageMatches(self, page):
-        if self.root_clause is None:
-            return True
-        return self.root_clause.pageMatches(self, page)
-
-    def _ensureRootClause(self):
-        if self.root_clause is None:
-            self.root_clause = AndBooleanClause()
-
-    def _addClausesFromConfigRecursive(self, config, parent_clause):
-        for key, val in config.items():
-            if key == 'and':
-                if not isinstance(val, list) or len(val) == 0:
-                    raise Exception("The given boolean 'AND' filter clause "
-                                    "doesn't have an array of child clauses.")
-                subcl = AndBooleanClause()
-                parent_clause.addClause(subcl)
-                for c in val:
-                    self._addClausesFromConfigRecursive(c, subcl)
-
-            elif key == 'or':
-                if not isinstance(val, list) or len(val) == 0:
-                    raise Exception("The given boolean 'OR' filter clause "
-                                    "doesn't have an array of child clauses.")
-                subcl = OrBooleanClause()
-                parent_clause.addClause(subcl)
-                for c in val:
-                    self._addClausesFromConfigRecursive(c, subcl)
-
-            elif key == 'not':
-                if isinstance(val, list):
-                    if len(val) != 1:
-                        raise Exception("'NOT' filter clauses must have "
-                                        "exactly one child clause.")
-                    val = val[0]
-                subcl = NotClause()
-                parent_clause.addClause(subcl)
-                self._addClausesFromConfigRecursive(val, subcl)
-
-            elif key[:4] == 'has_':
-                setting_name = key[4:]
-                if isinstance(val, list):
-                    wrappercl = AndBooleanClause()
-                    for c in val:
-                        wrappercl.addClause(HasFilterClause(setting_name, c))
-                    parent_clause.addClause(wrappercl)
-                else:
-                    parent_clause.addClause(HasFilterClause(setting_name, val))
-
-            elif key[:3] == 'is_':
-                setting_name = key[3:]
-                parent_clause.addClause(IsFilterClause(setting_name, val))
-
-            else:
-                raise Exception("Unknown filter clause: %s" % key)
-
-
 class IFilterClause(object):
     def addClause(self, clause):
         raise NotImplementedError()
@@ -126,6 +53,22 @@
         return False
 
 
+class IsDefinedFilterClause(IFilterClause):
+    def __init__(self, name):
+        self.name = name
+
+    def pageMatches(self, fil, page):
+        return self.name in page.config
+
+
+class IsNotEmptyFilterClause(IFilterClause):
+    def __init__(self, name):
+        self.name = name
+
+    def pageMatches(self, fil, page):
+        return bool(page.config.get(self.name))
+
+
 class SettingFilterClause(IFilterClause):
     def __init__(self, name, value, coercer=None):
         self.name = name
@@ -156,3 +99,93 @@
             actual_value = self.coercer(actual_value)
         return actual_value == self.value
 
+
+unary_ops = {'not': NotClause}
+binary_ops = {
+    'and': AndBooleanClause,
+    'or': OrBooleanClause}
+misc_ops = {
+    'defined': IsDefinedFilterClause,
+    'not_empty': IsNotEmptyFilterClause}
+
+
+class PaginationFilter(object):
+    def __init__(self):
+        self.root_clause = None
+
+    @property
+    def is_empty(self):
+        return self.root_clause is None
+
+    def addClause(self, clause):
+        self._ensureRootClause()
+        self.root_clause.addClause(clause)
+
+    def addClausesFromConfig(self, config):
+        self._ensureRootClause()
+        self._addClausesFromConfigRecursive(config, self.root_clause)
+
+    def pageMatches(self, page):
+        if self.root_clause is None:
+            return True
+        return self.root_clause.pageMatches(self, page)
+
+    def _ensureRootClause(self):
+        if self.root_clause is None:
+            self.root_clause = AndBooleanClause()
+
+    def _addClausesFromConfigRecursive(self, config, parent_clause):
+        for key, val in config.items():
+            clause_class = unary_ops.get(key)
+            if clause_class:
+                if isinstance(val, list):
+                    if len(val) != 1:
+                        raise Exception(
+                            "Unary filter '%s' must have exactly one child "
+                            "clause." % key)
+                    val = val[0]
+                subcl = clause_class()
+                parent_clause.addClause(subcl)
+                self._addClausesFromConfigRecursive(val, subcl)
+                continue
+
+            clause_class = binary_ops.get(key)
+            if clause_class:
+                if not isinstance(val, list) or len(val) == 0:
+                    raise Exception(
+                        "Binary filter clause '%s' doesn't have an array "
+                        "of child clauses." % key)
+                subcl = clause_class()
+                parent_clause.addClause(subcl)
+                for c in val:
+                    self._addClausesFromConfigRecursive(c, subcl)
+                continue
+
+            clause_class = misc_ops.get(key)
+            if clause_class:
+                if isinstance(val, list):
+                    wrappercl = AndBooleanClause()
+                    for c in val:
+                        wrappercl.addClause(clause_class(c))
+                    parent_clause.addClause(wrappercl)
+                else:
+                    parent_clause.addClause(clause_class(val))
+                continue
+
+            if key[:4] == 'has_':
+                setting_name = key[4:]
+                if isinstance(val, list):
+                    wrappercl = AndBooleanClause()
+                    for c in val:
+                        wrappercl.addClause(HasFilterClause(setting_name, c))
+                    parent_clause.addClause(wrappercl)
+                else:
+                    parent_clause.addClause(HasFilterClause(setting_name, val))
+                continue
+
+            if key[:3] == 'is_':
+                setting_name = key[3:]
+                parent_clause.addClause(IsFilterClause(setting_name, val))
+                continue
+
+            raise Exception("Unknown filter clause: %s" % key)