diff --git a/cactus/page.py b/cactus/page.py index 7816e6b3..f9338b68 100644 --- a/cactus/page.py +++ b/cactus/page.py @@ -42,6 +42,7 @@ def __init__(self, site, source_path): else: self.final_url = self.link_url self.build_path = self.source_path + self._read_data() def is_html(self): return urlparse.urlparse(self.source_path).path.endswith('.html') @@ -65,26 +66,27 @@ def full_build_path(self): return os.path.join(self.site.build_path, self.build_path) def data(self): + if not hasattr(self, "_data"): + self._read_data() + return self._data + + def _read_data(self): with open(self.full_source_path, 'rU') as f: try: - return f.read().decode('utf-8') + self._data = f.read().decode('utf-8') except: - logger.warning("Template engine could not process page: %s", self.path) + logger.warning("Page file could not be read: %s", self.path) + self._data = '' - def context(self, data=None, extra=None): + def context(self, extra=None): """ The page context. """ - if extra is None: - extra = {} - context = {'__CACTUS_CURRENT_PAGE__': self,} - - page_context, data = self.parse_context(data or self.data()) context.update(self.site.context()) - context.update(extra) - context.update(page_context) + context.update(extra or {}) + context.update(self.parse_context()) return Context(context) @@ -93,17 +95,10 @@ def render(self): Takes the template data with context and renders it to the final output file. """ - data = self.data() - context = self.context(data=data) - - # This is not very nice, but we already used the header context in the - # page context, so we don't need it anymore. - page_context, data = self.parse_context(data) + context, self._data = self.site.plugin_manager.preBuildPage( + self.site, self, self.context(), self._data) - context, data = self.site.plugin_manager.preBuildPage( - self.site, self, context, data) - - return Template(data).render(context) + return Template(self._data).render(context) def build(self): """ @@ -125,37 +120,47 @@ def build(self): self.site.plugin_manager.postBuildPage(self) - def parse_context(self, data, splitChar=':'): + def parse_context(self, splitChar=':'): """ Values like name: koen age: 29 - will be converted in a dict: {'name': 'koen', 'age': '29'} + will be converted in a dict: + + {'name': 'koen', 'age': '29'} + + and these lines are deleted from 'self.data' """ + # make sure that the page context is only calculated once + if hasattr(self, "_page_context"): + return self._page_context + if not self.is_html(): - return {}, data + return {} values = {} - lines = data.splitlines() + lines = self._data.splitlines() if not lines: - return {}, '' + return {} for i, line in enumerate(lines): - - if not line: - continue - - elif splitChar in line: - line = line.split(splitChar) - values[line[0].strip()] = (splitChar.join(line[1:])).strip() - - else: + # Context lines start at the top of the file. + # The text start after the first empty line + # or at the first line without a colon + if not splitChar in line: + if not line: + i += 1 break - return values, '\n'.join(lines[i:]) + key, value = line.split(splitChar, 1) + values[key.strip()] = value.strip() + + self._data = '\n'.join(lines[i:]) + self._page_context = values + return values def __repr__(self): return ''.format(self.source_path) diff --git a/cactus/site.py b/cactus/site.py index 052366a7..c3e2a30e 100644 --- a/cactus/site.py +++ b/cactus/site.py @@ -267,6 +267,10 @@ def build(self): else: os.remove(path) + # One test / use case depends on + # the pages being read from disk for every built + self._build_page_list() + # Render the pages to their output files mapper = map if self._parallel >= PARALLEL_AGGRESSIVE: @@ -341,23 +345,18 @@ def pages(self): """ List of pages. """ + if not hasattr(self, "_pages"): + self._build_page_list() + return self._pages - if not hasattr(self, "_page_cache"): - self._page_cache = {} - - pages = [] - - for path in fileList(self.page_path, relative=True): - - if path.endswith("~"): - continue - - if not self._page_cache.has_key(path): - self._page_cache[path] = Page(self, path) - - pages.append(self._page_cache[path]) - - return pages + def _build_page_list(self): + """ + Build the page list from the files found on disk + """ + self._pages = [ + Page(self, path) + for path in fileList(self.page_path, relative=True) + if not path.endswith("~")] def _rebuild_should_ignore(self, file_path): diff --git a/cactus/tests/data/colon-in-first-line.html b/cactus/tests/data/colon-in-first-line.html new file mode 100644 index 00000000..6f865578 --- /dev/null +++ b/cactus/tests/data/colon-in-first-line.html @@ -0,0 +1,6 @@ +Author: Me +Title: Big Thing + +Let's have one thing very clear: whatever. + +Next Paragraph diff --git a/cactus/tests/test_basic.py b/cactus/tests/test_basic.py index 044091a4..d7fd46f1 100644 --- a/cactus/tests/test_basic.py +++ b/cactus/tests/test_basic.py @@ -5,6 +5,7 @@ from cactus.tests import SiteTestCase from cactus.utils.filesystem import fileList from cactus.utils.url import path_to_url +from cactus.page import Page class TestSite(SiteTestCase): @@ -48,7 +49,7 @@ def testRenderPage(self): def testPageContext(self): """ - Test that page context is parsed and uses in the pages. + Test that page context is parsed and used in the pages. """ shutil.copy( @@ -113,4 +114,30 @@ def test_current_page(self): self.assertEqual('True', f.read()) with open(os.path.join(self.path, '.build', other), 'rU') as f: - self.assertEqual('False', f.read()) \ No newline at end of file + self.assertEqual('False', f.read()) + + +class TestPage(SiteTestCase): + + def testPageContext(self): + """ + Test parsing of page context is parsed and used in the pages. + """ + + shutil.copy( + os.path.join('cactus', 'tests', 'data', "koenpage-in.html"), + os.path.join(self.path, 'pages', 'koenpage.html') + ) + page = Page(self.site, 'koenpage.html') + self.assertEqual(page.parse_context(), + {'name': 'Koen Bok', 'age': '29'}) + + shutil.copy( + os.path.join('cactus', 'tests', 'data', 'colon-in-first-line.html'), + os.path.join(self.path, 'pages', 'test.html') + ) + page = Page(self.site, 'test.html') + self.assertEqual(page.parse_context(), + {u'Author': u'Me', u'Title': u'Big Thing'}) + self.assertEqual(page._data, + "Let's have one thing very clear: whatever.\n\nNext Paragraph")