summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Whittaker <carl.whittaker@hogarthww.com>2011-01-12 11:32:18 +0000
committerCarl Whittaker <carl.whittaker@hogarthww.com>2011-01-12 11:32:18 +0000
commit28e63d4fa5b845ce4eea33d86ab65c17dd7137ff (patch)
treeb38551106b74f8bf60dd6fc6a1e94df59f61b3b6
parent01dd06b313bfae5a9fe6b2b1caf312c94c608f15 (diff)
downloadpystache-28e63d4fa5b845ce4eea33d86ab65c17dd7137ff.tar.gz
Adding template Loader class
-rw-r--r--pystache/__init__.py1
-rw-r--r--pystache/loader.py41
-rw-r--r--pystache/template.py8
-rw-r--r--pystache/view.py33
-rw-r--r--tests/test_loader.py21
-rw-r--r--tests/test_simple.py13
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