changeset 212:701591ebfcba

data: Improve the Linker and RecursiveLinker features. Add tests.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 31 Jan 2015 11:50:50 -0800
parents 0b2d8f6df4ce
children e34a6826a3d4
files piecrust/data/linker.py tests/test_data_linker.py
diffstat 2 files changed, 128 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- 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
 
--- /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]
+