-
Notifications
You must be signed in to change notification settings - Fork 316
Page context #140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Page context #140
Changes from all commits
cc17fc6
2aac837
5ae3209
045b078
8e03912
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why access |
||
|
|
||
| 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above |
||
|
|
||
| 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:]) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This alters
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right, this changes
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that you might as well parse the context when loading the page itself. There doesn't seem to be a use case for separating those two steps. You could probably separate the whole page loading and parsing logic from the page object itself. |
||
| self._page_context = values | ||
| return values | ||
|
|
||
| def __repr__(self): | ||
| return '<Page: {0}>'.format(self.source_path) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain why those changes are needed? I understand the changes in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| # 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): | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| Author: Me | ||
| Title: Big Thing | ||
|
|
||
| Let's have one thing very clear: whatever. | ||
|
|
||
| Next Paragraph |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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()) | ||
| self.assertEqual('False', f.read()) | ||
|
|
||
|
|
||
| class TestPage(SiteTestCase): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be a separate class?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't need to be a separate class. But I wanted to separate the more granular tests for
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be ideal to break out the other tests for |
||
|
|
||
| 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") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather you set
_datatoNone(either in__init__or in theclassdefinition) and check forNone. Functionally equivalent an plays much better with with refactoring tools.