Mercurial > piecrust2
changeset 404:27b10024f8d8
linker: Add ability to return the parent and ancestors of a page.
Add reference documentation.
Add tests.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 24 May 2015 18:15:22 -0700 |
parents | 563e2e84ef6e |
children | 1970e7e3a18e |
files | docs/docs/99_reference/04_templating-data.md piecrust/data/linker.py tests/bakes/test_linker.yaml tests/test_data_linker.py |
diffstat | 4 files changed, 126 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/docs/docs/99_reference/04_templating-data.md Thu May 21 07:33:08 2015 -0700 +++ b/docs/docs/99_reference/04_templating-data.md Sun May 24 18:15:22 2015 -0700 @@ -97,6 +97,15 @@ children of the current page. This is only possible for page sources that support hierarchical navigation (but most of those shipping with PieCrust do). + * `parent`: Returns the data for a parent page, _i.e._ a page with the same + name as the current page's directory name. So if the current page is + `/foo/bar/something.md`, this would return the data for a page named + `/foo/bar.md`. + + * `ancestors`: Returns a list of data for all parents, all the way to the + root. In the previous example, this would return `/foo/bar.md` and + `/foo.md` (if they exist). + * `siblings`: Returns a list of the current page's siblings, _i.e._ pages on the same "level" as the current one.
--- a/piecrust/data/linker.py Thu May 21 07:33:08 2015 -0700 +++ b/piecrust/data/linker.py Sun May 24 18:15:22 2015 -0700 @@ -13,26 +13,55 @@ root page. """ def __init__(self, source, page_path): - self._linker = Linker(source, page_path) + self._source = source + self._root_page_path = page_path + self._linker = None + + @property + def parent(self): + self._load() + return self._linker.parent + + @property + def ancestors(self): + cur = self.parent + while cur: + yield cur + cur = cur.parent @property def siblings(self): + self._load() return self._linker @property def children(self): + self._load() self._linker._load() if self._linker._self_item is None: return [] - return self._linker._self_item._linker_info.child_linker + children = self._linker._self_item._linker_info.child_linker + if children is None: + return [] + return children @property def root(self): + self._load() return self._linker.root def forpath(self, rel_path): + self._load() return self._linker.forpath(rel_path) + def _load(self): + if self._linker is not None: + return + + dir_path = self._source.getDirpath(self._root_page_path) + self._linker = Linker(self._source, dir_path, + root_page_path=self._root_page_path) + class LinkedPageData(PaginationData): """ Class whose instances get returned when iterating on a `Linker` @@ -46,12 +75,25 @@ super(LinkedPageData, self).__init__(page) self.name = page._linker_info.name self.is_self = page._linker_info.is_self - self.children = page._linker_info.child_linker self.is_dir = page._linker_info.is_dir self.is_page = True + self._child_linker = page._linker_info.child_linker self.mapLoader('*', self._linkerChildLoader) + @property + def parent(self): + if self._child_linker is not None: + return self._child_linker.parent + print("No parent for ", self.url, self.title) + return None + + @property + def children(self): + if self._child_linker is not None: + return self._child_linker + return [] + def _linkerChildLoader(self, data, name): if self.children and hasattr(self.children, name): return getattr(self.children, name) @@ -106,7 +148,7 @@ self.name = None self.is_dir = False self.is_self = False - self.child_linker = [] + self.child_linker = None class _LinkedPage(object): @@ -121,12 +163,12 @@ class Linker(object): debug_render_doc = """Provides access to sibling and children pages.""" - def __init__(self, source, root_page_path, *, name=None, dir_path=None): + def __init__(self, source, dir_path, *, root_page_path=None): self._source = source + self._dir_path = dir_path self._root_page_path = root_page_path - self._name = name - self._dir_path = dir_path self._items = None + self._parent = None self._self_item = None self.is_dir = True @@ -148,17 +190,42 @@ return LinkedPageData(item) + def __str__(self): + return self.name + @property def name(self): - if self._name is None: - self._load() - return self._name + return self._source.getBasename(self._dir_path) @property def children(self): return self._iterItems(0) @property + def parent(self): + if self._dir_path == '': + return None + + if self._parent is None: + parent_name = self._source.getBasename(self._dir_path) + parent_dir_path = self._source.getDirpath(self._dir_path) + for is_dir, name, data in self._source.listPath(parent_dir_path): + if not is_dir and name == parent_name: + parent_page = data.buildPage() + item = _LinkedPage(parent_page) + item._linker_info.name = parent_name + item._linker_info.child_linker = Linker( + self._source, parent_dir_path, + root_page_path=self._root_page_path) + self._parent = LinkedPageData(item) + break + else: + self._parent = Linker(self._source, parent_dir_path, + root_page_path=self._root_page_path) + + return self._parent + + @property def pages(self): return self._iterItems(0, filter_page_items) @@ -183,8 +250,8 @@ return self.forpath('/') def forpath(self, rel_path): - return Linker(self._source, self._root_page_path, - name='.', dir_path=rel_path) + return Linker(self._source, rel_path, + root_page_path=self._root_page_path) def _iterItems(self, max_depth=-1, filter_func=None): items = walk_linkers(self, max_depth=max_depth, @@ -200,14 +267,6 @@ if not is_listable: raise Exception("Source '%s' can't be listed." % self._source.name) - if self._name is None: - self._name = self._source.getBasename(self._root_page_path) - if self._dir_path is None: - self._dir_path = self._source.getDirpath(self._root_page_path) - - if self._dir_path is None: - raise Exception("This linker has no directory to start from.") - items = list(self._source.listPath(self._dir_path)) self._items = collections.OrderedDict() with self._source.app.env.page_repository.startBatchGet(): @@ -215,8 +274,8 @@ # If `is_dir` is true, `data` will be the directory's source # path. If not, it will be a page factory. if is_dir: - item = Linker(self._source, self._root_page_path, - name=name, dir_path=data) + item = Linker(self._source, data, + root_page_path=self._root_page_path) else: page = data.buildPage() is_self = (page.rel_path == self._root_page_path)
--- a/tests/bakes/test_linker.yaml Thu May 21 07:33:08 2015 -0700 +++ b/tests/bakes/test_linker.yaml Sun May 24 18:15:22 2015 -0700 @@ -46,3 +46,34 @@ Bar Foo SELFIE! Other +--- +in: + pages/foo.md: "---\ntitle: Foo\n---\n" + pages/foo/one.md: | + {{family.parent.url}} {{family.parent.title}} +outfiles: + foo/one.html: /foo.html Foo +--- +in: + pages/foo.md: "---\ntitle: Foo\n---\n" + pages/foo/bar.md: "---\ntitle: Bar\n---\n" + pages/foo/bar/one.md: | + {{family.parent.url}} {{family.parent.title}} + {{family.parent.parent.url}} {{family.parent.parent.title}} +outfiles: + foo/bar/one.html: | + /foo/bar.html Bar + /foo.html Foo +--- +in: + pages/foo.md: "---\ntitle: Foo\n---\n" + pages/foo/bar.md: "---\ntitle: Bar\n---\n" + pages/foo/bar/one.md: | + {% for p in family.ancestors -%} + {{p.url}} {{p.title}} + {% endfor %} +outfiles: + foo/bar/one.html: | + /foo/bar.html Bar + /foo.html Foo +
--- a/tests/test_data_linker.py Thu May 21 07:33:08 2015 -0700 +++ b/tests/test_data_linker.py Sun May 24 18:15:22 2015 -0700 @@ -1,3 +1,4 @@ +import os.path import pytest from piecrust.data.linker import Linker from .mockutil import mock_fs, mock_fs_scope @@ -39,7 +40,8 @@ app = fs.getApp() app.config.set('site/pretty_urls', True) src = app.getSource('pages') - linker = Linker(src, page_path) + linker = Linker(src, os.path.dirname(page_path), + root_page_path=page_path) actual = list(iter(linker)) assert len(actual) == len(expected) @@ -83,7 +85,8 @@ app = fs.getApp() app.config.set('site/pretty_urls', True) src = app.getSource('pages') - linker = Linker(src, page_path) + linker = Linker(src, os.path.dirname(page_path), + root_page_path=page_path) actual = list(iter(linker.allpages)) assert len(actual) == len(expected)