Mercurial > p > roundup > code
changeset 4740:fe9568a6cbd6
Untangle template selection logic from template loading functionality.
| author | anatoly techtonik <techtonik@gmail.com> |
|---|---|
| date | Tue, 15 Jan 2013 00:10:01 +0300 |
| parents | 94be76e04140 |
| children | e68f63877b25 |
| files | CHANGES.txt roundup/cgi/client.py roundup/cgi/engine_chameleon.py roundup/cgi/engine_zopetal.py roundup/cgi/templating.py test/test_templating.py |
| diffstat | 6 files changed, 86 insertions(+), 85 deletions(-) [+] |
line wrap: on
line diff
--- a/CHANGES.txt Mon Jan 14 20:25:00 2013 +0300 +++ b/CHANGES.txt Tue Jan 15 00:10:01 2013 +0300 @@ -9,6 +9,10 @@ - Renamed old Templates classes to Loader classes to clarify sources for alternative templating engines, updated docs (anatoly techtonik) +- Template selection code is moved from Loader classes into cgi.client + limiting the responsibility of Loaders to compilation and rendering. + Internally, templating.find_template is replaced with + client.selectTemplate (anatoly techtonik) Fixed:
--- a/roundup/cgi/client.py Mon Jan 14 20:25:00 2013 +0300 +++ b/roundup/cgi/client.py Tue Jan 15 00:10:01 2013 +0300 @@ -1063,23 +1063,50 @@ self.error_message.append(message) self.write_html(self.renderContext()) - def selectTemplate(self): - """ Template selection logic """ + def selectTemplate(self, name, view): + """ Choose existing template for the given combination of + classname (name parameter) and template request variable + (view parameter) and return its name. + + In most cases the name will be "classname.view", but + if "view" is None, then template name "classname" will + be returned. + + If "classname.view" template doesn't exist, the + "_generic.view" is used as a fallback. + + [ ] cover with tests + """ loader = self.instance.templates - name = self.classname - view = self.template - # if classname is not set, use "home" template if name is None: name = 'home' - return name, view + tplname = name + if view: + tplname = '%s.%s' % (name, view) + + if loader.check(tplname): + return tplname + + # rendering class/context with generic template for this view. + # with no view it's impossible to choose which generic template to use + if not view: + raise templating.NoTemplate('Template "%s" doesn\'t exist' % name) + + generic = '_generic.%s' % view + if loader.check(generic): + return generic + + raise templating.NoTemplate('No template file exists for templating ' + '"%s" with template "%s" (neither "%s" nor "%s")' % (name, view, + tplname, generic)) def renderContext(self): """ Return a PageTemplate for the named page """ - name, view = self.selectTemplate() + tplname = self.selectTemplate(self.classname, self.template) # catch errors so we can handle PT rendering errors more nicely args = { @@ -1087,7 +1114,7 @@ 'error_message': self.error_message } try: - pt = self.instance.templates.load(name, view) + pt = self.instance.templates.load(tplname) # let the template render figure stuff out result = pt.render(self, None, None, **args) self.additional_headers['Content-Type'] = pt.content_type
--- a/roundup/cgi/engine_chameleon.py Mon Jan 14 20:25:00 2013 +0300 +++ b/roundup/cgi/engine_chameleon.py Tue Jan 15 00:10:01 2013 +0300 @@ -5,15 +5,22 @@ import os.path import chameleon -from roundup.cgi.templating import StringIO, context, find_template, LoaderBase +from roundup.cgi.templating import StringIO, context, LoaderBase class Loader(LoaderBase): def __init__(self, dir): self.dir = dir self.loader = chameleon.PageTemplateLoader(dir) - def load(self, name, view=None): - src, filename = find_template(self.dir, name, view) + def check(self, name): + for extension in ['', '.html', '.xml']: + f = name + extension + src = os.path.join(self.dir, f) + if os.path.exists(src): + return (src, f) + + def load(self, tplname): + src, filename = self.check(tplname) return RoundupPageTemplate(self.loader.load(src)) class RoundupPageTemplate(object):
--- a/roundup/cgi/engine_zopetal.py Mon Jan 14 20:25:00 2013 +0300 +++ b/roundup/cgi/engine_zopetal.py Tue Jan 15 00:10:01 2013 +0300 @@ -8,7 +8,7 @@ import os import os.path -from roundup.cgi.templating import StringIO, context, translationService, find_template, LoaderBase +from roundup.cgi.templating import StringIO, context, translationService, LoaderBase from roundup.cgi.PageTemplates import PageTemplate, GlobalTranslationService from roundup.cgi.PageTemplates.Expressions import getEngine from roundup.cgi.TAL import TALInterpreter @@ -21,9 +21,16 @@ def __init__(self, dir): self.dir = dir - def load(self, name, view=None): + def check(self, name): + for extension in ['', '.html', '.xml']: + f = name + extension + src = os.path.join(self.dir, f) + if os.path.exists(src): + return (src, f) + + def load(self, tplname): # find the source - src, filename = find_template(self.dir, name, view) + src, filename = self.check(tplname) # has it changed? try:
--- a/roundup/cgi/templating.py Mon Jan 14 20:25:00 2013 +0300 +++ b/roundup/cgi/templating.py Tue Jan 15 00:10:01 2013 +0300 @@ -77,49 +77,8 @@ 'items of class %(class)s') % { 'action': self.action, 'class': self.klass} -def find_template(dir, name, view): - """ Find a template in the nominated dir - """ - # find the source - if view: - filename = '%s.%s'%(name, view) - else: - filename = name - - # try old-style - src = os.path.join(dir, filename) - if os.path.exists(src): - return (src, filename) - - # try with a .html or .xml extension (new-style) - for extension in '.html', '.xml': - f = filename + extension - src = os.path.join(dir, f) - if os.path.exists(src): - return (src, f) - - # no view == no generic template is possible - if not view: - raise NoTemplate, 'Template file "%s" doesn\'t exist'%name - - # try _generic template for the view - generic = '_generic.%s'%view - src = os.path.join(dir, generic) - if os.path.exists(src): - return (src, generic) - - # finally, try _generic.html - generic = generic + '.html' - src = os.path.join(dir, generic) - if os.path.exists(src): - return (src, generic) - - raise NoTemplate('No template file exists for templating "%s" ' - 'with template "%s" (neither "%s" nor "%s")'%(name, view, - filename, generic)) - class LoaderBase: - """Base for engine-specific template Loader class.""" + """ Base for engine-specific template Loader class.""" def precompileTemplates(self): """ Go through a directory and precompile all the templates therein """ @@ -137,37 +96,31 @@ # remove extension filename = filename[:-len(extension)] - - # load the template - if '.' in filename: - name, view = filename.split('.', 1) - self.load(name, view) - else: - self.load(filename, None) - - def load(self, name, view=None): + self.load(filename) + + def load(self, tplname): """ Load template and return template object with render() method. - "name" and "view" are used to select the template, which in - most cases will be "name.view". If "view" is None, then a - template called "name" will be selected. - - If the file "name.view" doesn't exist, we look for - "_generic.view" as a fallback. + "tplname" is a template name. For filesystem loaders it is a + filename without extensions, typically in the "classname.view" + format. """ - # [ ] document default 'home' template and other special pages + raise NotImplementedError + + def check(self, name): + """ Check if template with the given name exists. Return None or + a tuple (src, filename) that can be reused in load() method. + """ raise NotImplementedError def __getitem__(self, name): """Special method to access templates by loader['name']""" - view = None - if '.' in name: - name, view = name.split('.', 1) try: - return self.load(name, view) + return self.load(name) except NoTemplate, message: raise KeyError, message + def get_loader(dir, engine_name): if engine_name == 'chameleon': import engine_chameleon as engine @@ -700,7 +653,9 @@ req.update(kwargs) # new template, using the specified classname and request - pt = self._client.instance.templates.load(self.classname, name) + # [ ] this code is too similar to client.renderContext() + tplname = self._client.selectTemplate(self.classname, name) + pt = self._client.instance.templates.load(tplname) # use our fabricated request args = { @@ -845,9 +800,8 @@ isinstance(self._props[prop_n], hyperdb.Link)): classname = self._props[prop_n].classname try: - template = find_template(self._db.config.TEMPLATES, - classname, 'item') - if template[1].startswith('_generic'): + template = self._client.selectTemplate(classname, 'item') + if template.startswith('_generic.'): raise NoTemplate, 'not really...' except NoTemplate: pass @@ -917,9 +871,9 @@ ) % locals() labelprop = linkcl.labelprop(1) try: - template = find_template(self._db.config.TEMPLATES, - classname, 'item') - if template[1].startswith('_generic'): + template = self._client.selectTemplate(classname, + 'item') + if template.startswith('_generic.'): raise NoTemplate, 'not really...' hrefable = 1 except NoTemplate: @@ -1087,7 +1041,10 @@ '&@queryname=%s'%urllib.quote(name)) # new template, using the specified classname and request - pt = self._client.instance.templates.load(req.classname, 'search') + # [ ] the custom logic for search page doesn't belong to + # generic templating module (techtonik) + tplname = self._client.selectTemplate(req.classname, 'search') + pt = self._client.instance.templates.load(tplname) # The context for a search page should be the class, not any # node. self._client.nodeid = None
--- a/test/test_templating.py Mon Jan 14 20:25:00 2013 +0300 +++ b/test/test_templating.py Tue Jan 15 00:10:01 2013 +0300 @@ -336,7 +336,6 @@ class Unauthorised(Exception): def __init__(self, action, klass): def __str__(self): -def find_template(dir, name, view): class Loader: def __init__(self, dir):
