Commit 4c575f4a authored by Christian Heimes's avatar Christian Heimes

Keep defer expression like it was coded by Evan. Added new expression type...

Keep defer expression like it was coded by Evan. Added new expression type lazy: that works as a lazy initialization expression. Also added some doc strings
parent 9a24abe5
......@@ -10,27 +10,62 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Defer expression handler
"""Defer and Lazy expression handler
defer expressions can be usesd for a design pattern called deferred evaluation.
Example:
<div tal:define="xis defer:string:x is $x">
<p tal:repeat="x python:range(3)"
tal:content="xis"></p>
</div>
Output:
<div>
<p>x is 0</p>
<p>x is 1</p>
<p>x is 2</p>
</div>
A lazy expressions is implemented in a similar way but has a different result. While
a defer expression is evaluated every time it is used according to its context a lazy
expression is evaluted only the first time it is used. Lazy expression are known
under the name lazy initialization of variables, too.
A common use case for a lazy expression is a lazy binding of a costly expression.
While one could call an expression only when it's required it makes sense to define
it only one time when it could be used multiple times.
Example
<div tal:define="lazyvar lazy:here/suckMyCPU">
<div tal:condition="foo" tal:content="lazyvar" />
<div tal:condition="bar" tal:content="lazyvar" />
<div tal:condition"python: not (foo or bar)">...</div>
</div>
"""
_marker = []
_marker = object()
# defer expression
class DeferWrapper:
"""Wrapper for defer: expression
"""
def __init__(self, expr, econtext):
self._expr = expr
self._econtext = econtext
self._result = _marker
def __str__(self):
return str(self())
def __call__(self):
r = self._result
if r is _marker:
r = self._expr(self._econtext)
return r
return self._expr(self._econtext)
class DeferExpr:
"""defer: expression handler for deferred evaluation of the context
"""
def __init__(self, name, expr, compiler):
self._s = expr = expr.lstrip()
self._c = compiler.compile(expr)
......@@ -40,3 +75,28 @@ class DeferExpr:
def __repr__(self):
return 'defer:%s' % `self._s`
# lazy expression
class LazyWrapper(DeferWrapper):
"""Wrapper for lazy: expression
"""
def __init__(self, expr, econtext):
DeferWrapper.__init__(self, expr, econtext)
self._result = _marker
def __call__(self):
r = self._result
if r is _marker:
self._result = r = self._expr(self._econtext)
return r
class LazyExpr(DeferExpr):
"""lazy: expression handler for lazy initialization of expressions
"""
def __call__(self, econtext):
return LazyWrapper(self._c, econtext)
def __repr__(self):
return 'lazy:%s' % `self._s`
......@@ -42,7 +42,8 @@ from ZRPythonExpr import _SecureModuleImporter
from ZRPythonExpr import call_with_ns
from DeferExpr import DeferWrapper
from DeferExpr import DeferExpr
from DeferExpr import LazyWrapper
from DeferExpr import LazyExpr
_engine = None
def getEngine():
......@@ -62,6 +63,7 @@ def installHandlers(engine):
reg('python', PythonExpr)
reg('not', NotExpr)
reg('defer', DeferExpr)
reg('lazy', LazyExpr)
SecureModuleImporter = _SecureModuleImporter()
......
import os, sys, unittest
from Products.PageTemplates import Expressions
from Products.PageTemplates.DeferExpr import LazyWrapper
from Products.PageTemplates.DeferExpr import DeferWrapper
class ExpressionTests(unittest.TestCase):
......@@ -51,6 +53,15 @@ class ExpressionTests(unittest.TestCase):
assert ec.evaluate('x | string:x') == 'x'
assert ec.evaluate('x | string:$one') == '1'
assert ec.evaluate('x | not:exists:x')
def testWrappers(self):
"""Test if defer and lazy are returning their wrappers
"""
ec = self.ec
defer = ec.evaluate('defer: b')
lazy = ec.evaluate('lazy: b')
self.failUnless(isinstance(defer, DeferWrapper))
self.failUnless(isinstance(lazy, LazyWrapper))
def test_suite():
return unittest.makeSuite(ExpressionTests)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment