diff options
author | Carl Whittaker <carl.whittaker@hogarthww.com> | 2011-01-12 11:32:18 +0000 |
---|---|---|
committer | Carl Whittaker <carl.whittaker@hogarthww.com> | 2011-01-12 11:32:18 +0000 |
commit | 28e63d4fa5b845ce4eea33d86ab65c17dd7137ff (patch) | |
tree | b38551106b74f8bf60dd6fc6a1e94df59f61b3b6 | |
parent | 01dd06b313bfae5a9fe6b2b1caf312c94c608f15 (diff) | |
download | pystache-28e63d4fa5b845ce4eea33d86ab65c17dd7137ff.tar.gz |
Adding template Loader class
-rw-r--r-- | pystache/__init__.py | 1 | ||||
-rw-r--r-- | pystache/loader.py | 41 | ||||
-rw-r--r-- | pystache/template.py | 8 | ||||
-rw-r--r-- | pystache/view.py | 33 | ||||
-rw-r--r-- | tests/test_loader.py | 21 | ||||
-rw-r--r-- | tests/test_simple.py | 13 |
6 files changed, 112 insertions, 5 deletions
diff --git a/pystache/__init__.py b/pystache/__init__.py index bb832b8..314c5c8 100644 --- a/pystache/__init__.py +++ b/pystache/__init__.py @@ -1,5 +1,6 @@ from pystache.template import Template from pystache.view import View +from pystache.loader import Loader def render(template, context=None, **kwargs): context = context and context.copy() or {} diff --git a/pystache/loader.py b/pystache/loader.py new file mode 100644 index 0000000..c2d6f02 --- /dev/null +++ b/pystache/loader.py @@ -0,0 +1,41 @@ +import os + +class Loader(object): + + template_extension = 'mustache' + template_path = '.' + template_encoding = None + + def load_template(self, template_name, template_dirs=None): + '''Returns the template string from a file or throws IOError if it non existent''' + if None == template_dirs: + template_dirs = self.template_path + + file_name = template_name + '.' + self.template_extension + + # Given a single directory we'll load from it + if isinstance(template_dirs, basestring): + file_path = os.path.join(template_dirs, file_name) + + return self._load_template_file(file_path) + + # Given a list of directories we'll check each for our file + for path in template_dirs: + file_path = os.path.join(path, file_name) + if os.path.exists(file_path): + return self._load_template_file(file_path) + + raise IOError('"%s" not found in "%s"' % (template_name, ':'.join(template_dirs),)) + + def _load_template_file(self, file_path): + '''Loads and returns the template file from disk''' + f = open(file_path, 'r') + + try: + template = f.read() + if self.template_encoding: + template = unicode(template, self.template_encoding) + finally: + f.close() + + return template
\ No newline at end of file diff --git a/pystache/template.py b/pystache/template.py index 5faf7cd..27380af 100644 --- a/pystache/template.py +++ b/pystache/template.py @@ -1,6 +1,7 @@ import re import cgi import collections +import os modifiers = {} def modifier(symbol): @@ -107,6 +108,7 @@ class Template(object): insides = [] for item in listing: view = View(context=item) + view.template_path = self.view.template_path view.parent = self.view insides.append(Template(template, view).render()) @@ -117,6 +119,12 @@ class Template(object): raw = view.get(tag_name, '') return cgi.escape(unicode(raw)) + + @modifier('>') + def _render_partial(self, template_name, view): + # mothereffin template loader goes here + template = view.get_template(template_name) + return Template(template, view).render() def render(self): template = self._render_sections(self.template, self.view) diff --git a/pystache/view.py b/pystache/view.py index 415775a..0b11eeb 100644 --- a/pystache/view.py +++ b/pystache/view.py @@ -5,6 +5,10 @@ from types import * class View(object): + template_name = None + template_path = None + template = None + def __init__(self, template=None, context=None, **kwargs): self.template = template self.context = context or {} @@ -22,4 +26,31 @@ class View(object): if hasattr(self, 'parent'): return self.parent.get(attr, default) else: - return default
\ No newline at end of file + return default + + def get_template(self, template_name): + if not self.template: + from pystache import Loader + template_name = self._get_template_name(template_name) + self.template = Loader().load_template(template_name, self.template_path) + + return self.template + + def _get_template_name(self, template_name=None): + """TemplatePartial => template_partial + Takes a string but defaults to using the current class' name or + the `template_name` attribute + """ + if self.template_name: + return self.template_name + + if not template_name: + template_name = self.__class__.__name__ + + def repl(match): + return '_' + match.group(0).lower() + + return re.sub('[A-Z]', repl, template_name)[1:] + + def render(self): + return Template(self.get_template(self.template_name), self).render()
\ No newline at end of file diff --git a/tests/test_loader.py b/tests/test_loader.py new file mode 100644 index 0000000..42222ac --- /dev/null +++ b/tests/test_loader.py @@ -0,0 +1,21 @@ +import unittest +import pystache + +class TestLoader(unittest.TestCase): + + def test_template_is_loaded(self): + loader = pystache.Loader() + template = loader.load_template('simple', 'examples') + + self.assertEqual(template, 'Hi {{thing}}!{{blank}}') + + def test_using_list_of_paths(self): + loader = pystache.Loader() + template = loader.load_template('simple', ['doesnt_exist', 'examples']) + + self.assertEqual(template, 'Hi {{thing}}!{{blank}}') + + def test_non_existent_template_fails(self): + loader = pystache.Loader() + + self.assertRaises(IOError, loader.load_template, 'simple', 'doesnt_exist')
\ No newline at end of file diff --git a/tests/test_simple.py b/tests/test_simple.py index edfa203..fdbd1a6 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -3,13 +3,12 @@ import pystache from examples.nested_context import NestedContext from examples.complex_view import ComplexView from examples.lambdas import Lambdas +from examples.template_partial import TemplatePartial class TestSimple(unittest.TestCase): def test_simple_render(self): - tmpl = '{{derp}}' - - self.assertEqual('herp', pystache.Template(tmpl, {'derp': 'herp'}).render()) + self.assertEqual('herp', pystache.Template('{{derp}}', {'derp': 'herp'}).render()) def test_nested_context(self): view = NestedContext() @@ -25,4 +24,10 @@ class TestSimple(unittest.TestCase): def test_callables(self): view = Lambdas() - self.assertEquals(pystache.Template('{{#replace_foo_with_bar}}foo != bar. oh, it does!{{/replace_foo_with_bar}}', view).render(), 'bar != bar. oh, it does!')
\ No newline at end of file + self.assertEquals(pystache.Template('{{#replace_foo_with_bar}}foo != bar. oh, it does!{{/replace_foo_with_bar}}', view).render(), 'bar != bar. oh, it does!') + + def test_rendering_partial(self): + view = TemplatePartial() + self.assertEquals(pystache.Template('{{>inner_partial}}', view).render(), 'Again, Welcome!') + + self.assertEquals(pystache.Template('{{#looping}}{{>inner_partial}} {{/looping}}', view).render(), 'Again, Welcome! Again, Welcome! Again, Welcome! ')
\ No newline at end of file |