# HG changeset patch # User Ludovic Chabant # Date 1422733850 28800 # Node ID 701591ebfcbaf17b80ef309492d0f71c190e8b89 # Parent 0b2d8f6df4ceac6ec3310d6c099cbda0a0c6a0dc data: Improve the Linker and RecursiveLinker features. Add tests. diff -r 0b2d8f6df4ce -r 701591ebfcba piecrust/data/linker.py --- a/piecrust/data/linker.py Fri Jan 30 23:51:54 2015 -0800 +++ b/piecrust/data/linker.py Sat Jan 31 11:50:50 2015 -0800 @@ -1,6 +1,7 @@ import logging import collections -from piecrust.data.base import PaginationData +from piecrust.data.base import PaginationData, IPaginationSource +from piecrust.data.iterators import PageIterator from piecrust.sources.base import IListableSource, build_pages @@ -20,6 +21,27 @@ return False +class LinkedPageDataIterator(object): + def __init__(self, items): + self._items = list(items) + self._index = -1 + + def __iter__(self): + return self + + def __next__(self): + self._index += 1 + if self._index >= len(self._items): + raise StopIteration() + return self._items[self._index] + + def sort(self, name): + def key_getter(item): + return item[name] + self._items = sorted(self._item, key=key_getter) + return self + + class Linker(object): debug_render_doc = """Provides access to sibling and children pages.""" @@ -33,7 +55,7 @@ def __iter__(self): self._load() - return iter(self._cache.values()) + return LinkedPageDataIterator(self._cache.values()) def __getattr__(self, name): self._load() @@ -85,21 +107,31 @@ class RecursiveLinker(Linker): - def __init__(self, source, page_path): - super(RecursiveLinker, self).__init__(source, page_path=page_path) + def __init__(self, source, *args, **kwargs): + super(RecursiveLinker, self).__init__(source, *args, **kwargs) def __iter__(self): + return iter(PageIterator(self._iterateLinkers())) + + def siblings(self): + return PageIterator(self._iterateLinkers(0)) + + def frompath(self, rel_path): + return RecursiveLinker(self.source, name='.', dir_path=rel_path) + + def _iterateLinkers(self, max_depth=-1): self._load() if not self._is_listable: return - yield from walk_linkers(self) + yield from walk_linkers(self, 0, max_depth) -def walk_linkers(linker): +def walk_linkers(linker, depth=0, max_depth=-1): linker._load() for item in linker._cache.values(): if item.is_dir: - yield from walk_linkers(item) + if max_depth < 0 or depth + 1 <= max_depth: + yield from walk_linkers(item, depth + 1, max_depth) else: yield item diff -r 0b2d8f6df4ce -r 701591ebfcba tests/test_data_linker.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_data_linker.py Sat Jan 31 11:50:50 2015 -0800 @@ -0,0 +1,89 @@ +import pytest +from piecrust.data.linker import Linker, RecursiveLinker +from .mockutil import mock_fs, mock_fs_scope + + +@pytest.mark.parametrize( + 'fs, page_path, expected', + [ + (mock_fs().withPage('pages/foo'), 'foo.md', + [('/foo', True, False)]), + ((mock_fs() + .withPage('pages/foo') + .withPage('pages/bar')), + 'foo.md', + [('/bar', False, False), ('/foo', True, False)]), + ((mock_fs() + .withPage('pages/baz') + .withPage('pages/something/else') + .withPage('pages/foo') + .withPage('pages/bar')), + 'foo.md', + [('/bar', False, False), ('/baz', False, False), + ('/foo', True, False), ('something', False, True)]), + ((mock_fs() + .withPage('pages/something/else') + .withPage('pages/foo') + .withPage('pages/something/good') + .withPage('pages/bar')), + 'something/else.md', + [('/something/else', True, False), + ('/something/good', False, False)]) + ]) +def test_linker_iteration(fs, page_path, expected): + with mock_fs_scope(fs): + app = fs.getApp() + src = app.getSource('pages') + linker = Linker(src, page_path=page_path) + actual = list(iter(linker)) + + assert len(actual) == len(expected) + for i, (a, e) in enumerate(zip(actual, expected)): + assert a.is_dir == e[2] + if a.is_dir: + assert a.name == e[0] + else: + assert a.url == e[0] + assert a.is_self == e[1] + + +@pytest.mark.parametrize( + 'fs, page_path, expected', + [ + (mock_fs().withPage('pages/foo'), 'foo.md', + [('/foo', True)]), + ((mock_fs() + .withPage('pages/foo') + .withPage('pages/bar')), + 'foo.md', + [('/bar', False), ('/foo', True)]), + ((mock_fs() + .withPage('pages/baz') + .withPage('pages/something/else') + .withPage('pages/foo') + .withPage('pages/bar')), + 'foo.md', + [('/bar', False), ('/baz', False), + ('/foo', True), ('/something/else', False)]), + ((mock_fs() + .withPage('pages/something/else') + .withPage('pages/foo') + .withPage('pages/something/good') + .withPage('pages/bar')), + 'something/else.md', + [('/something/else', True), + ('/something/good', False)]) + ]) +def test_recursive_linker_iteration(fs, page_path, expected): + with mock_fs_scope(fs): + app = fs.getApp() + src = app.getSource('pages') + linker = RecursiveLinker(src, page_path=page_path) + actual = list(iter(linker)) + + assert len(actual) == len(expected) + for i, (a, e) in enumerate(zip(actual, expected)): + assert a.is_dir is False + assert a.url == e[0] + assert a.is_self == e[1] +