Commit d7475e63 by Chris Jerdonek

Refactoring of Locator class: make_template_name() and locate_path().

Changed make_template_name() from a function into a method.  Moved
the search_dirs argument from a Locator constructor argument to
an argument of Locator.locate_path().
parent 04176645
# coding: utf-8
"""
This module provides a Locator class.
This module provides a Locator class for finding template files.
"""
......@@ -13,42 +13,14 @@ import sys
DEFAULT_EXTENSION = 'mustache'
def make_template_name(obj):
"""
Return the canonical template name for an object instance.
This method converts Python-style class names (PEP 8's recommended
CamelCase, aka CapWords) to lower_case_with_underscords. Here
is an example with code:
>>> class HelloWorld(object):
... pass
>>> hi = HelloWorld()
>>> make_template_name(hi)
'hello_world'
"""
template_name = obj.__class__.__name__
def repl(match):
return '_' + match.group(0).lower()
return re.sub('[A-Z]', repl, template_name)[1:]
class Locator(object):
def __init__(self, search_dirs=None, extension=None):
def __init__(self, extension=None):
"""
Construct a template locator.
Arguments:
search_dirs: the list of directories in which to search for templates,
for example when looking for partials. Defaults to the current
working directory. If given a string, the string is interpreted
as a single directory.
extension: the template file extension. Defaults to "mustache".
Pass False for no extension (i.e. extensionless template files).
......@@ -56,13 +28,6 @@ class Locator(object):
if extension is None:
extension = DEFAULT_EXTENSION
if search_dirs is None:
search_dirs = os.curdir # i.e. "."
if isinstance(search_dirs, basestring):
search_dirs = [search_dirs]
self.search_dirs = search_dirs
self.template_extension = extension
def make_file_name(self, template_name):
......@@ -72,15 +37,41 @@ class Locator(object):
return file_name
def locate_path(self, template_name):
def make_template_name(self, obj):
"""
Return the canonical template name for an object instance.
This method converts Python-style class names (PEP 8's recommended
CamelCase, aka CapWords) to lower_case_with_underscords. Here
is an example with code:
>>> class HelloWorld(object):
... pass
>>> hi = HelloWorld()
>>>
>>> locator = Locator()
>>> locator.make_template_name(hi)
'hello_world'
"""
template_name = obj.__class__.__name__
def repl(match):
return '_' + match.group(0).lower()
return re.sub('[A-Z]', repl, template_name)[1:]
def locate_path(self, template_name, search_dirs):
"""
Find and return the path to the template with the given name.
Raises an IOError if the template cannot be found.
"""
search_dirs = self.search_dirs
Arguments:
search_dirs: the list of directories in which to search for templates.
"""
file_name = self.make_file_name(template_name)
for dir_path in search_dirs:
......
......@@ -174,12 +174,12 @@ class Renderer(object):
"""
return Reader(encoding=self.file_encoding, decode_errors=self.decode_errors)
def _make_locator(self):
def make_locator(self):
"""
Create a Locator instance using current attributes.
"""
return Locator(search_dirs=self.search_dirs, extension=self.file_extension)
return Locator(extension=self.file_extension)
def _make_load_template(self):
"""
......@@ -187,10 +187,10 @@ class Renderer(object):
"""
reader = self._make_reader()
locator = self._make_locator()
locator = self.make_locator()
def load_template(template_name):
path = locator.locate_path(template_name)
path = locator.locate_path(template_name=template_name, search_dirs=self.search_dirs)
return reader.read(path)
return load_template
......
......@@ -6,7 +6,7 @@ This module provides a View class.
"""
from .context import Context
from .locator import make_template_name
from .locator import Locator
from .renderer import Renderer
......@@ -20,6 +20,8 @@ class View(object):
_renderer = None
locator = Locator()
def __init__(self, template=None, context=None, partials=None, **kwargs):
"""
Construct a View instance.
......@@ -85,7 +87,7 @@ class View(object):
if self.template_name:
return self.template_name
return make_template_name(self)
return self.locator.make_template_name(self)
def render(self):
"""
......
......@@ -9,27 +9,12 @@ import os
import sys
import unittest
from pystache.locator import make_template_name
from pystache.locator import Locator
from pystache.reader import Reader
from .common import DATA_DIR
class MakeTemplateNameTests(unittest.TestCase):
"""
Test the make_template_name() function.
"""
def test(self):
class FooBar(object):
pass
foo = FooBar()
self.assertEquals(make_template_name(foo), 'foo_bar')
class LocatorTests(unittest.TestCase):
search_dirs = 'examples'
......@@ -37,14 +22,6 @@ class LocatorTests(unittest.TestCase):
def _locator(self):
return Locator(search_dirs=DATA_DIR)
def test_init__search_dirs(self):
# Test the default value.
locator = Locator()
self.assertEquals(locator.search_dirs, [os.curdir])
locator = Locator(search_dirs=['foo'])
self.assertEquals(locator.search_dirs, ['foo'])
def test_init__extension(self):
# Test the default value.
locator = Locator()
......@@ -69,14 +46,14 @@ class LocatorTests(unittest.TestCase):
self.assertEquals(locator.make_file_name('foo'), 'foo.')
def test_locate_path(self):
locator = Locator(search_dirs='examples')
path = locator.locate_path('simple')
locator = Locator()
path = locator.locate_path('simple', search_dirs=['examples'])
self.assertEquals(os.path.basename(path), 'simple.mustache')
def test_locate_path__using_list_of_paths(self):
locator = Locator(search_dirs=['doesnt_exist', 'examples'])
path = locator.locate_path('simple')
locator = Locator()
path = locator.locate_path('simple', search_dirs=['doesnt_exist', 'examples'])
self.assertTrue(path)
......@@ -90,13 +67,10 @@ class LocatorTests(unittest.TestCase):
dir1 = DATA_DIR
dir2 = os.path.join(DATA_DIR, 'locator')
locator.search_dirs = [dir1]
self.assertTrue(locator.locate_path('duplicate'))
locator.search_dirs = [dir2]
self.assertTrue(locator.locate_path('duplicate'))
self.assertTrue(locator.locate_path('duplicate', search_dirs=[dir1]))
self.assertTrue(locator.locate_path('duplicate', search_dirs=[dir2]))
locator.search_dirs = [dir2, dir1]
path = locator.locate_path('duplicate')
path = locator.locate_path('duplicate', search_dirs=[dir2, dir1])
dirpath = os.path.dirname(path)
dirname = os.path.split(dirpath)[-1]
......@@ -105,5 +79,17 @@ class LocatorTests(unittest.TestCase):
def test_locate_path__non_existent_template_fails(self):
locator = Locator()
self.assertRaises(IOError, locator.locate_path, 'doesnt_exist')
self.assertRaises(IOError, locator.locate_path, 'doesnt_exist', search_dirs=[])
def test_make_template_name(self):
"""
Test make_template_name().
"""
locator = Locator()
class FooBar(object):
pass
foo = FooBar()
self.assertEquals(locator.make_template_name(foo), 'foo_bar')
......@@ -216,53 +216,40 @@ class RendererTestCase(unittest.TestCase):
actual = self._read(renderer, filename)
self.assertEquals(actual, 'non-ascii: ')
## Test the _make_locator() method.
## Test the make_locator() method.
def test__make_locator__return_type(self):
def test_make_locator__return_type(self):
"""
Test that _make_locator() returns a Locator.
Test that make_locator() returns a Locator.
"""
renderer = Renderer()
locator = renderer._make_locator()
locator = renderer.make_locator()
self.assertEquals(type(locator), Locator)
def test__make_locator__file_extension(self):
def test_make_locator__file_extension(self):
"""
Test that _make_locator() respects the file_extension attribute.
Test that make_locator() respects the file_extension attribute.
"""
renderer = Renderer()
renderer.file_extension = 'foo'
locator = renderer._make_locator()
locator = renderer.make_locator()
self.assertEquals(locator.template_extension, 'foo')
def test__make_locator__search_dirs(self):
"""
Test that _make_locator() respects the search_dirs attribute.
"""
renderer = Renderer()
renderer.search_dirs = ['foo']
locator = renderer._make_locator()
self.assertEquals(locator.search_dirs, ['foo'])
# This test is a sanity check. Strictly speaking, it shouldn't
# be necessary based on our tests above.
def test__make_locator__default(self):
def test_make_locator__default(self):
renderer = Renderer()
actual = renderer._make_locator()
actual = renderer.make_locator()
expected = Locator()
self.assertEquals(type(actual), type(expected))
self.assertEquals(actual.template_extension, expected.template_extension)
self.assertEquals(actual.search_dirs, expected.search_dirs)
## Test the render() method.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment