diff options
| -rw-r--r-- | CHANGES.txt | 6 | ||||
| -rw-r--r-- | docs/api/template.rst | 22 | ||||
| -rw-r--r-- | docs/conf.py | 4 | ||||
| -rw-r--r-- | docs/narr/templates.rst | 10 | ||||
| -rw-r--r-- | repoze/bfg/chameleon_text.py | 85 | ||||
| -rw-r--r-- | repoze/bfg/tests/fixtures/minimal.txt | 1 | ||||
| -rw-r--r-- | repoze/bfg/tests/fixtures/nonminimal.txt | 1 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_chameleon_text.py | 226 |
8 files changed, 347 insertions, 8 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 705c2a65b..33c924c2a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,8 @@ -Next release +0.4.9 (11/17/2008) + + - Add chameleon text template API (chameleon ${name} renderings + where the template does not need to be wrapped in any containing + XML). - Change docs to explain install in terms of a virtualenv (unconditionally). diff --git a/docs/api/template.rst b/docs/api/template.rst index 8cf63c91b..8685073c3 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -3,12 +3,12 @@ :mod:`repoze.bfg` Built-in Templating Facilties =============================================== -Three templating facilities are provided by :mod:`repoze.bfg` "out of -the box": :term:`ZPT` -style, :term:`Genshi` -style, and :term:`XSLT` -templating. +Four templating facilities are provided by :mod:`repoze.bfg` "out of +the box": :term:`ZPT` -style, :term:`Genshi` -style, text templating, +and :term:`XSLT` templating. -ZPT-style and Genshi-style templates are in :mod:`repoze.bfg` are -supported by the :term:`Chameleon` (nee :term:`z3c.pt`) templating +ZPT-style, Genshi-style, and text templates are in :mod:`repoze.bfg` +are supported by the :term:`Chameleon` (nee :term:`z3c.pt`) templating engine, which contains alternate implementations of both the ZPT and Genshi language specifications. @@ -32,6 +32,7 @@ statement, e.g.: from repoze.chameleon_zpt import render_template as zpt_render from repoze.chameleon_genshi import render_template as genshi_render + from repoze.chameleon_text import render_template as text_render :mod:`repoze.bfg.chameleon_zpt` ------------------------------- @@ -58,6 +59,17 @@ statement, e.g.: .. autofunction:: render_template_to_response +:mod:`repoze.bfg.chameleon_text` +---------------------------------- + +.. automodule:: repoze.bfg.chameleon_text + + .. autofunction:: get_template + + .. autofunction:: render_template + + .. autofunction:: render_template_to_response + :mod:`repoze.bfg.xslt` ---------------------- diff --git a/docs/conf.py b/docs/conf.py index d494ce20a..7d3991698 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,9 +51,9 @@ copyright = '2008, Agendaless Consulting' # other places throughout the built documents. # # The short X.Y version. -version = '0.4.8' +version = '0.4.9' # The full version, including alpha/beta/rc tags. -release = '0.4.8' +release = '0.4.9' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index 4878e728c..48a9ac52e 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -136,6 +136,16 @@ application's configuration section, e.g.:: use = egg:MyProject#app reload_templates = true +Using Text Templates +-------------------- + +:mod:`repoze.bfg` also allows for the use of templates which are +composed entirely of non-XML text via :term:`Chameleon`. To do so, +you can create templates that are entirely composed of text except for +``${name}`` -style substitution points. The rendering API is a mirror +of the ZPT rendering facility, it's just imported from another place; +see :ref:`template_module` for more information. + Templating with other Templating Languages ------------------------------------------ diff --git a/repoze/bfg/chameleon_text.py b/repoze/bfg/chameleon_text.py new file mode 100644 index 000000000..e00106e5c --- /dev/null +++ b/repoze/bfg/chameleon_text.py @@ -0,0 +1,85 @@ +from webob import Response + +from zope.component import queryUtility + +from zope.interface import classProvides +from zope.interface import implements + +from repoze.bfg.interfaces import ITemplateRenderer +from repoze.bfg.interfaces import ITemplateRendererFactory +from repoze.bfg.interfaces import ISettings + +from repoze.bfg.templating import renderer_from_cache + +from chameleon.core.template import TemplateFile +from chameleon.zpt.language import Parser + +class TextTemplateFile(TemplateFile): + default_parser = Parser() + + def __init__(self, filename, parser=None, format=None, + doctype=None, **kwargs): + if parser is None: + parser = self.default_parser + super(TextTemplateFile, self).__init__(filename, parser, format, + doctype, **kwargs) + +class TextTemplateRenderer(object): + classProvides(ITemplateRendererFactory) + implements(ITemplateRenderer) + + def __init__(self, path, auto_reload=False): + self.template = TextTemplateFile( + path, + format='text', + auto_reload=auto_reload + ) + + def implementation(self): + return self.template + + def __call__(self, **kw): + return self.template(**kw) + +def _auto_reload(): + settings = queryUtility(ISettings) + auto_reload = settings and settings.reload_templates + return auto_reload + +def get_renderer(path): + """ Return a callable ``ITemplateRenderer`` object representing a + ``chameleon`` text template at the package-relative path (may also + be absolute).""" + auto_reload = _auto_reload() + renderer = renderer_from_cache(path, TextTemplateRenderer, + auto_reload=auto_reload) + return renderer + +def get_template(path): + """ Return a ``chameleon`` text template at the package-relative + path (may also be absolute).""" + auto_reload = _auto_reload() + renderer = renderer_from_cache(path, TextTemplateRenderer, + auto_reload=auto_reload) + return renderer.implementation() + +def render_template(path, **kw): + """ Render a ``chameleon`` text template at the package-relative + path (may also be absolute) using the kwargs in ``*kw`` as + top-level names and return a string.""" + auto_reload = _auto_reload() + renderer = renderer_from_cache(path, TextTemplateRenderer, + auto_reload=auto_reload) + return renderer(**kw) + +def render_template_to_response(path, **kw): + """ Render a ``chameleon`` text template at the package-relative + path (may also be absolute) using the kwargs in ``*kw`` as + top-level names and return a Response object with the body as the + template result.""" + auto_reload = _auto_reload() + renderer = renderer_from_cache(path, TextTemplateRenderer, + auto_reload=auto_reload) + result = renderer(**kw) + return Response(result) + diff --git a/repoze/bfg/tests/fixtures/minimal.txt b/repoze/bfg/tests/fixtures/minimal.txt new file mode 100644 index 000000000..18832d351 --- /dev/null +++ b/repoze/bfg/tests/fixtures/minimal.txt @@ -0,0 +1 @@ +Hello. diff --git a/repoze/bfg/tests/fixtures/nonminimal.txt b/repoze/bfg/tests/fixtures/nonminimal.txt new file mode 100644 index 000000000..9de95ec92 --- /dev/null +++ b/repoze/bfg/tests/fixtures/nonminimal.txt @@ -0,0 +1 @@ +Hello, ${name}! diff --git a/repoze/bfg/tests/test_chameleon_text.py b/repoze/bfg/tests/test_chameleon_text.py new file mode 100644 index 000000000..8e53b7ff9 --- /dev/null +++ b/repoze/bfg/tests/test_chameleon_text.py @@ -0,0 +1,226 @@ +import unittest + +from zope.testing.cleanup import cleanUp + +class Base: + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _zcmlConfigure(self): + import repoze.bfg + import zope.configuration.xmlconfig + zope.configuration.xmlconfig.file('configure.zcml', package=repoze.bfg) + + def _getTemplatePath(self, name): + import os + here = os.path.abspath(os.path.dirname(__file__)) + return os.path.join(here, 'fixtures', name) + +class TextTemplateRendererTests(unittest.TestCase, Base): + def setUp(self): + Base.setUp(self) + + def tearDown(self): + Base.tearDown(self) + + def _getTargetClass(self): + from repoze.bfg.chameleon_text import TextTemplateRenderer + return TextTemplateRenderer + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_instance_implements_ITemplate(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import ITemplateRenderer + path = self._getTemplatePath('minimal.txt') + verifyObject(ITemplateRenderer, self._makeOne(path)) + + def test_class_implements_ITemplate(self): + from zope.interface.verify import verifyClass + from repoze.bfg.interfaces import ITemplateRenderer + verifyClass(ITemplateRenderer, self._getTargetClass()) + + def test_call(self): + self._zcmlConfigure() + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + result = instance() + self.failUnless(isinstance(result, str)) + self.assertEqual(result, 'Hello.\n') + + def test_call_nonminimal(self): + self._zcmlConfigure() + nonminimal = self._getTemplatePath('nonminimal.txt') + instance = self._makeOne(nonminimal) + result = instance(name='Chris') + self.failUnless(isinstance(result, str)) + self.assertEqual(result, 'Hello, Chris!\n') + + def test_implementation(self): + self._zcmlConfigure() + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + result = instance.implementation()() + self.failUnless(isinstance(result, str)) + self.assertEqual(result, 'Hello.\n') + +class RenderTemplateTests(unittest.TestCase, Base): + def setUp(self): + Base.setUp(self) + + def tearDown(self): + Base.tearDown(self) + + def _getFUT(self): + from repoze.bfg.chameleon_text import render_template + return render_template + + def test_it(self): + self._zcmlConfigure() + minimal = self._getTemplatePath('minimal.txt') + render = self._getFUT() + result = render(minimal) + self.failUnless(isinstance(result, str)) + self.assertEqual(result, 'Hello.\n') + +class RenderTemplateToResponseTests(unittest.TestCase, Base): + def setUp(self): + Base.setUp(self) + + def tearDown(self): + Base.tearDown(self) + + def _getFUT(self): + from repoze.bfg.chameleon_text import render_template_to_response + return render_template_to_response + + def test_minimal(self): + self._zcmlConfigure() + minimal = self._getTemplatePath('minimal.txt') + render = self._getFUT() + result = render(minimal) + from webob import Response + self.failUnless(isinstance(result, Response)) + self.assertEqual(result.app_iter, ['Hello.\n']) + self.assertEqual(result.status, '200 OK') + self.assertEqual(len(result.headerlist), 2) + +class GetRendererTests(unittest.TestCase, Base): + def setUp(self): + Base.setUp(self) + + def tearDown(self): + Base.tearDown(self) + + def _getFUT(self): + from repoze.bfg.chameleon_text import get_renderer + return get_renderer + + def test_nonabs_registered(self): + from zope.component import getGlobalSiteManager + from zope.component import queryUtility + from repoze.bfg.chameleon_text import TextTemplateRenderer + from repoze.bfg.interfaces import ITemplateRenderer + minimal = self._getTemplatePath('minimal.txt') + utility = TextTemplateRenderer(minimal) + gsm = getGlobalSiteManager() + gsm.registerUtility(utility, ITemplateRenderer, name=minimal) + get = self._getFUT() + result = get(minimal) + self.assertEqual(result, utility) + self.assertEqual(queryUtility(ITemplateRenderer, minimal), utility) + + def test_nonabs_unregistered(self): + from zope.component import getGlobalSiteManager + from zope.component import queryUtility + from repoze.bfg.chameleon_text import TextTemplateRenderer + from repoze.bfg.interfaces import ITemplateRenderer + minimal = self._getTemplatePath('minimal.txt') + self.assertEqual(queryUtility(ITemplateRenderer, minimal), None) + utility = TextTemplateRenderer(minimal) + gsm = getGlobalSiteManager() + gsm.registerUtility(utility, ITemplateRenderer, name=minimal) + get = self._getFUT() + result = get(minimal) + self.assertEqual(result, utility) + self.assertEqual(queryUtility(ITemplateRenderer, minimal), utility) + + def test_testing(self): + from zope.component import getGlobalSiteManager + from repoze.bfg.interfaces import ITestingTemplateRenderer + class Dummy: + template = object() + def implementation(self): + return self.template + gsm = getGlobalSiteManager() + utility = Dummy() + gsm.registerUtility(utility, ITestingTemplateRenderer, name='foo') + get = self._getFUT() + result = get('foo') + self.failUnless(result is utility) + +class GetTemplateTests(unittest.TestCase, Base): + def setUp(self): + Base.setUp(self) + + def tearDown(self): + Base.tearDown(self) + + def _getFUT(self): + from repoze.bfg.chameleon_text import get_template + return get_template + + def test_nonabs_registered(self): + self._zcmlConfigure() + from zope.component import getGlobalSiteManager + from zope.component import queryUtility + from repoze.bfg.chameleon_text import TextTemplateRenderer + from repoze.bfg.interfaces import ITemplateRenderer + minimal = self._getTemplatePath('minimal.txt') + utility = TextTemplateRenderer(minimal) + gsm = getGlobalSiteManager() + gsm.registerUtility(utility, ITemplateRenderer, name=minimal) + get = self._getFUT() + result = get(minimal) + self.assertEqual(result.filename, minimal) + self.assertEqual(queryUtility(ITemplateRenderer, minimal), utility) + + def test_nonabs_unregistered(self): + self._zcmlConfigure() + from zope.component import getGlobalSiteManager + from zope.component import queryUtility + from repoze.bfg.chameleon_text import TextTemplateRenderer + from repoze.bfg.interfaces import ITemplateRenderer + minimal = self._getTemplatePath('minimal.txt') + self.assertEqual(queryUtility(ITemplateRenderer, minimal), None) + utility = TextTemplateRenderer(minimal) + gsm = getGlobalSiteManager() + gsm.registerUtility(utility, ITemplateRenderer, name=minimal) + get = self._getFUT() + result = get(minimal) + self.assertEqual(result.filename, minimal) + self.assertEqual(queryUtility(ITemplateRenderer, minimal), utility) + + def test_testing(self): + from zope.component import getGlobalSiteManager + from repoze.bfg.interfaces import ITestingTemplateRenderer + class Dummy: + template = object() + def implementation(self): + return self.template + gsm = getGlobalSiteManager() + utility = Dummy() + gsm.registerUtility(utility, ITestingTemplateRenderer, name='foo') + get = self._getFUT() + result = get('foo') + self.failUnless(result is utility.template) + + + + + |
