[z3-checkins] r10706 - in z3/clarity: . trunk trunk/src
trunk/src/clarity
faassen at codespeak.net
faassen at codespeak.net
Fri Apr 15 20:20:57 MEST 2005
Author: faassen
Date: Fri Apr 15 20:20:57 2005
New Revision: 10706
Added:
z3/clarity/
z3/clarity/trunk/
z3/clarity/trunk/CREDITS.txt
z3/clarity/trunk/LICENSE.txt
z3/clarity/trunk/README.txt
z3/clarity/trunk/clarity-meta.zcml
z3/clarity/trunk/src/
z3/clarity/trunk/src/clarity/
z3/clarity/trunk/src/clarity/__init__.py
z3/clarity/trunk/src/clarity/meta.zcml
z3/clarity/trunk/src/clarity/metadirective.py
z3/clarity/trunk/src/clarity/viewmeta.py
Log:
Initial import of clarity, ClearSimple template integration into Zope 3.
Added: z3/clarity/trunk/CREDITS.txt
==============================================================================
--- (empty file)
+++ z3/clarity/trunk/CREDITS.txt Fri Apr 15 20:20:57 2005
@@ -0,0 +1,4 @@
+Credits
+=======
+
+Martijn Faassen - initial and main developer
Added: z3/clarity/trunk/LICENSE.txt
==============================================================================
--- (empty file)
+++ z3/clarity/trunk/LICENSE.txt Fri Apr 15 20:20:57 2005
@@ -0,0 +1,29 @@
+Copyright (c) 2004 Infrae. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. Neither the name of Infrae nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INFRAE OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
Added: z3/clarity/trunk/README.txt
==============================================================================
--- (empty file)
+++ z3/clarity/trunk/README.txt Fri Apr 15 20:20:57 2005
@@ -0,0 +1,81 @@
+Clarity - ClearSilver templates in Zope 3
+=========================================
+
+This package, Clarity, integrates ClearSilver templates into Zope 3.
+
+Why?
+---
+
+Zope 3 already has page template and even DTML, why would I want to
+use this? Some possible benefits ClearSilver brings are:
+
+* Absolute separation of logic from template. You cannot do
+ complicated logic in a ClearSilver template, you can only extract
+ data. You cannot call into the Zope 3 API *at all*.
+
+* Increased debuggability. Calling APIs is impossible from the
+ template, which means that most bugs the API should appear in Python
+ code already, where the programmer can worry about them, not the
+ person who is tweaking the templates. While you are building a
+ template you will *never* be bothered by, say,
+ ComponentLookupErrors.
+
+* Increased testability. Since you know what HDF structure you expect
+ to get in advance, you can test this using a normal unit test from
+ Python code. Your template can stay simple.
+
+* Performance. Admittedly simplistic testing with 'siege' shows that
+ for larger templates, ClearSilver templates integrated into Zope run
+ several times faster than the equivalent ZPT page.
+
+However, since no significant codebases exist that use this facility,
+these benefits are somewhat speculative.
+
+More about ClearSilver here:
+
+http://www.clearsilver.net
+
+Installation
+------------
+
+You need ClearSilver and its Python bindings to install it. It's
+simple in Debian; just install python-clearsilver.
+
+For source downloads check here:
+
+http://www.clearsilver.net
+
+To install it, you need to place the 'src' directory on your
+PYTHONPATH somehow, and copy clarity-meta.zcml into your Zope's
+package-includes directory. Then restart Zope 3.
+
+Usage
+-----
+
+To use it, use the browser:cspage directive. This behaves very
+similarly to the browser:page directive, except that it loads a
+ClearSilver page.
+
+To make it work, you need to use the 'class' statement on the cspage
+directive, and define a method 'hdf' on the class. This fills a
+hierarchical data structure with data that can be picked up by the
+ClearSilver template. For example::
+
+ def hdf(self):
+ hdf = self.createHDF()
+ hdf.setValue('foo.bar', 'Hello world')
+ return hdf
+
+In the ClearSilver template you then say something like this::
+
+ <html>
+ <body>
+ <p><?cs var:foo.bar ?></p>
+ </body>
+ </p>
+
+License
+-------
+
+BSD. See LICENSE.txt. ClearSilver itself has an open source license
+based on the Apache Software License v1.1.
Added: z3/clarity/trunk/clarity-meta.zcml
==============================================================================
--- (empty file)
+++ z3/clarity/trunk/clarity-meta.zcml Fri Apr 15 20:20:57 2005
@@ -0,0 +1 @@
+<include package="clarity" file="meta.zcml"/>
\ No newline at end of file
Added: z3/clarity/trunk/src/clarity/__init__.py
==============================================================================
--- (empty file)
+++ z3/clarity/trunk/src/clarity/__init__.py Fri Apr 15 20:20:57 2005
@@ -0,0 +1 @@
+# this is a package
Added: z3/clarity/trunk/src/clarity/meta.zcml
==============================================================================
--- (empty file)
+++ z3/clarity/trunk/src/clarity/meta.zcml Fri Apr 15 20:20:57 2005
@@ -0,0 +1,15 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:meta="http://namespaces.zope.org/meta">
+
+ <meta:directives namespace="http://namespaces.zope.org/browser">
+
+ <meta:directive
+ name="cspage"
+ schema=".metadirective.ICsPageDirective"
+ handler=".viewmeta.cspage"
+ />
+
+ </meta:directives>
+
+</configure>
Added: z3/clarity/trunk/src/clarity/metadirective.py
==============================================================================
--- (empty file)
+++ z3/clarity/trunk/src/clarity/metadirective.py Fri Apr 15 20:20:57 2005
@@ -0,0 +1,5 @@
+# we just import this from zope 3 for now; we may want to define
+# it differently later, though
+from zope.app.publisher.browser.metadirectives import IPageDirective
+
+ICsPageDirective = IPageDirective
Added: z3/clarity/trunk/src/clarity/viewmeta.py
==============================================================================
--- (empty file)
+++ z3/clarity/trunk/src/clarity/viewmeta.py Fri Apr 15 20:20:57 2005
@@ -0,0 +1,188 @@
+import os, sys
+from zope.interface import implements, classImplements, Interface
+from zope.configuration.exceptions import ConfigurationError
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from zope.publisher.interfaces.browser import IBrowserPublisher
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.security.checker import defineChecker
+from zope.app.publisher.browser.viewmeta import \
+ _handle_menu, _handle_permission, _handle_allowed_interface, \
+ _handle_allowed_attributes, _handle_for
+from zope.app.publisher.browser import BrowserView
+from zope.security.checker import CheckerPublic, Checker
+from zope.app.component.metaconfigure import handler
+
+import neo_cgi # must be loaded before loading neo_util or neo_cs..
+import neo_util
+import neo_cs
+
+def cspage(_context, name, permission, for_,
+ layer=IDefaultBrowserLayer, template=None, class_=None,
+ allowed_interface=None, allowed_attributes=None,
+ attribute='__call__', menu=None, title=None,
+ ):
+
+ _handle_menu(_context, menu, title, [for_], name, permission, layer)
+ required = {}
+
+ permission = _handle_permission(_context, permission)
+
+ if not (class_ or template):
+ raise ConfigurationError("Must specify a class or template")
+
+ if attribute != '__call__':
+ if template:
+ raise ConfigurationError(
+ "Attribute and template cannot be used together.")
+
+ if not class_:
+ raise ConfigurationError(
+ "A class must be provided if attribute is used")
+
+ if template:
+ template = os.path.abspath(str(_context.path(template)))
+ if not os.path.isfile(template):
+ raise ConfigurationError("No such file", template)
+ required['__getitem__'] = permission
+
+ # XXX: new __name__ attribute must be tested
+ if class_:
+ if attribute != '__call__':
+ if not hasattr(class_, attribute):
+ raise ConfigurationError(
+ "The provided class doesn't have the specified attribute "
+ )
+ if template:
+ # class and template
+ new_class = SimpleViewClass(template, bases=(class_, ), name=name)
+ else:
+ if not hasattr(class_, 'browserDefault'):
+ cdict = {
+ 'browserDefault':
+ lambda self, request: (getattr(self, attribute), ())
+ }
+ else:
+ cdict = {}
+
+ cdict['__name__'] = name
+ cdict['__page_attribute__'] = attribute
+ new_class = type(class_.__name__, (class_, simple,), cdict)
+
+ if hasattr(class_, '__implements__'):
+ classImplements(new_class, IBrowserPublisher)
+
+ else:
+ # template
+ new_class = SimpleViewClass(template, name=name)
+
+ for n in (attribute, 'browserDefault', '__call__', 'publishTraverse'):
+ required[n] = permission
+
+ _handle_allowed_interface(_context, allowed_interface, permission,
+ required)
+ _handle_allowed_attributes(_context, allowed_interface, permission,
+ required)
+
+ _handle_for(_context, for_)
+
+ defineChecker(new_class, Checker(required))
+
+ _context.action(
+ discriminator = ('view', for_, name, IBrowserRequest, layer),
+ callable = handler,
+ args = ('provideAdapter',
+ (for_, layer), Interface, name, new_class, _context.info),
+ )
+
+class simple(BrowserView):
+
+ implements(IBrowserPublisher)
+
+ def browserDefault(self, request):
+ return self, ()
+
+ def publishTraverse(self, request, name):
+ if name == 'index.html':
+ return self.index
+
+ raise NotFound(self, name, request)
+
+ def createHDF(self):
+ return neo_util.HDF()
+
+ def hdf(self):
+ return self.createHDF()
+
+ def __call__(self, *args, **kw):
+ return self.index(*args, **kw)
+
+def SimpleViewClass(src, offering=None, bases=(), name=u''):
+ if offering is None:
+ offering = sys._getframe(1).f_globals
+ bases += (simple,)
+ class_ = type("SimpleViewClass from %s" % src, bases,
+ {'index': ClearSilverFile(src, offering),
+ '__name__': name})
+ return class_
+
+class ClearSilverFile(object):
+ content_type = 'text/html'
+
+ def __init__(self, filename, _prefix=None, content_type=None):
+ path = self.get_path_from_prefix(_prefix)
+ self.filename = os.path.join(path, filename)
+ if not os.path.isfile(self.filename):
+ raise ValueError("No such file", self.filename)
+ if content_type is not None:
+ self.content_type = content_type
+
+ def get_path_from_prefix(self, _prefix):
+ if isinstance(_prefix, str):
+ path = _prefix
+ else:
+ if _prefix is None:
+ _prefix = sys._getframe(2).f_globals
+ path = package_home(_prefix)
+ return path
+
+ def __call__(self, instance, *args, **keywords):
+ # for some reason it is not that easy to actually cache
+ # parsed clearsilver templates -- the HDF structure needs
+ # to be known during creation. Some testing showed that
+ # ClearSilver with parsing is still blazingly fast, however,
+ # so we won't worry about this for now.
+ hdf = instance.hdf()
+ cs = neo_cs.CS(hdf)
+ cs.parseFile(self.filename)
+ s = cs.render()
+ response = instance.request.response
+ if not response.getHeader("Content-Type"):
+ response.setHeader("Content-Type", self.content_type)
+ return s
+
+ def __get__(self, instance, type):
+ return BoundClearSilver(self, instance)
+
+class BoundClearSilver(object):
+ def __init__(self, cs, ob):
+ object.__setattr__(self, 'im_func', cs)
+ object.__setattr__(self, 'im_self', ob)
+
+ filename = property(lambda self: self.im_func.filename)
+
+ def __call__(self, *args, **kw):
+ if self.im_self is None:
+ im_self, args = args[0], args[1:]
+ else:
+ im_self = self.im_self
+ return self.im_func(im_self, *args, **kw)
+
+ def __setattr__(self, name, v):
+ raise AttributeError("Can't set attribute", name)
+
+ def __repr__(self):
+ return "<BoundClearSilver of %r>" % self.im_self
+
+def package_home(gdict):
+ filename = gdict["__file__"]
+ return os.path.dirname(filename)
More information about the z3-checkins
mailing list