comparison piecrust/data/linker.py @ 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 043b9d8304c7
children fd8e39254da0
comparison
equal deleted inserted replaced
403:563e2e84ef6e 404:27b10024f8d8
11 class PageLinkerData(object): 11 class PageLinkerData(object):
12 """ Entry template data to get access to related pages from a given 12 """ Entry template data to get access to related pages from a given
13 root page. 13 root page.
14 """ 14 """
15 def __init__(self, source, page_path): 15 def __init__(self, source, page_path):
16 self._linker = Linker(source, page_path) 16 self._source = source
17 self._root_page_path = page_path
18 self._linker = None
19
20 @property
21 def parent(self):
22 self._load()
23 return self._linker.parent
24
25 @property
26 def ancestors(self):
27 cur = self.parent
28 while cur:
29 yield cur
30 cur = cur.parent
17 31
18 @property 32 @property
19 def siblings(self): 33 def siblings(self):
34 self._load()
20 return self._linker 35 return self._linker
21 36
22 @property 37 @property
23 def children(self): 38 def children(self):
39 self._load()
24 self._linker._load() 40 self._linker._load()
25 if self._linker._self_item is None: 41 if self._linker._self_item is None:
26 return [] 42 return []
27 return self._linker._self_item._linker_info.child_linker 43 children = self._linker._self_item._linker_info.child_linker
44 if children is None:
45 return []
46 return children
28 47
29 @property 48 @property
30 def root(self): 49 def root(self):
50 self._load()
31 return self._linker.root 51 return self._linker.root
32 52
33 def forpath(self, rel_path): 53 def forpath(self, rel_path):
54 self._load()
34 return self._linker.forpath(rel_path) 55 return self._linker.forpath(rel_path)
56
57 def _load(self):
58 if self._linker is not None:
59 return
60
61 dir_path = self._source.getDirpath(self._root_page_path)
62 self._linker = Linker(self._source, dir_path,
63 root_page_path=self._root_page_path)
35 64
36 65
37 class LinkedPageData(PaginationData): 66 class LinkedPageData(PaginationData):
38 """ Class whose instances get returned when iterating on a `Linker` 67 """ Class whose instances get returned when iterating on a `Linker`
39 or `RecursiveLinker`. It's just like what gets usually returned by 68 or `RecursiveLinker`. It's just like what gets usually returned by
44 73
45 def __init__(self, page): 74 def __init__(self, page):
46 super(LinkedPageData, self).__init__(page) 75 super(LinkedPageData, self).__init__(page)
47 self.name = page._linker_info.name 76 self.name = page._linker_info.name
48 self.is_self = page._linker_info.is_self 77 self.is_self = page._linker_info.is_self
49 self.children = page._linker_info.child_linker
50 self.is_dir = page._linker_info.is_dir 78 self.is_dir = page._linker_info.is_dir
51 self.is_page = True 79 self.is_page = True
80 self._child_linker = page._linker_info.child_linker
52 81
53 self.mapLoader('*', self._linkerChildLoader) 82 self.mapLoader('*', self._linkerChildLoader)
83
84 @property
85 def parent(self):
86 if self._child_linker is not None:
87 return self._child_linker.parent
88 print("No parent for ", self.url, self.title)
89 return None
90
91 @property
92 def children(self):
93 if self._child_linker is not None:
94 return self._child_linker
95 return []
54 96
55 def _linkerChildLoader(self, data, name): 97 def _linkerChildLoader(self, data, name):
56 if self.children and hasattr(self.children, name): 98 if self.children and hasattr(self.children, name):
57 return getattr(self.children, name) 99 return getattr(self.children, name)
58 raise LazyPageConfigLoaderHasNoValue 100 raise LazyPageConfigLoaderHasNoValue
104 class _LinkerInfo(object): 146 class _LinkerInfo(object):
105 def __init__(self): 147 def __init__(self):
106 self.name = None 148 self.name = None
107 self.is_dir = False 149 self.is_dir = False
108 self.is_self = False 150 self.is_self = False
109 self.child_linker = [] 151 self.child_linker = None
110 152
111 153
112 class _LinkedPage(object): 154 class _LinkedPage(object):
113 def __init__(self, page): 155 def __init__(self, page):
114 self._page = page 156 self._page = page
119 161
120 162
121 class Linker(object): 163 class Linker(object):
122 debug_render_doc = """Provides access to sibling and children pages.""" 164 debug_render_doc = """Provides access to sibling and children pages."""
123 165
124 def __init__(self, source, root_page_path, *, name=None, dir_path=None): 166 def __init__(self, source, dir_path, *, root_page_path=None):
125 self._source = source 167 self._source = source
168 self._dir_path = dir_path
126 self._root_page_path = root_page_path 169 self._root_page_path = root_page_path
127 self._name = name
128 self._dir_path = dir_path
129 self._items = None 170 self._items = None
171 self._parent = None
130 self._self_item = None 172 self._self_item = None
131 173
132 self.is_dir = True 174 self.is_dir = True
133 self.is_page = False 175 self.is_page = False
134 self.is_self = False 176 self.is_self = False
146 if isinstance(item, Linker): 188 if isinstance(item, Linker):
147 return item 189 return item
148 190
149 return LinkedPageData(item) 191 return LinkedPageData(item)
150 192
193 def __str__(self):
194 return self.name
195
151 @property 196 @property
152 def name(self): 197 def name(self):
153 if self._name is None: 198 return self._source.getBasename(self._dir_path)
154 self._load()
155 return self._name
156 199
157 @property 200 @property
158 def children(self): 201 def children(self):
159 return self._iterItems(0) 202 return self._iterItems(0)
160 203
161 @property 204 @property
205 def parent(self):
206 if self._dir_path == '':
207 return None
208
209 if self._parent is None:
210 parent_name = self._source.getBasename(self._dir_path)
211 parent_dir_path = self._source.getDirpath(self._dir_path)
212 for is_dir, name, data in self._source.listPath(parent_dir_path):
213 if not is_dir and name == parent_name:
214 parent_page = data.buildPage()
215 item = _LinkedPage(parent_page)
216 item._linker_info.name = parent_name
217 item._linker_info.child_linker = Linker(
218 self._source, parent_dir_path,
219 root_page_path=self._root_page_path)
220 self._parent = LinkedPageData(item)
221 break
222 else:
223 self._parent = Linker(self._source, parent_dir_path,
224 root_page_path=self._root_page_path)
225
226 return self._parent
227
228 @property
162 def pages(self): 229 def pages(self):
163 return self._iterItems(0, filter_page_items) 230 return self._iterItems(0, filter_page_items)
164 231
165 @property 232 @property
166 def directories(self): 233 def directories(self):
181 @property 248 @property
182 def root(self): 249 def root(self):
183 return self.forpath('/') 250 return self.forpath('/')
184 251
185 def forpath(self, rel_path): 252 def forpath(self, rel_path):
186 return Linker(self._source, self._root_page_path, 253 return Linker(self._source, rel_path,
187 name='.', dir_path=rel_path) 254 root_page_path=self._root_page_path)
188 255
189 def _iterItems(self, max_depth=-1, filter_func=None): 256 def _iterItems(self, max_depth=-1, filter_func=None):
190 items = walk_linkers(self, max_depth=max_depth, 257 items = walk_linkers(self, max_depth=max_depth,
191 filter_func=filter_func) 258 filter_func=filter_func)
192 src = LinkerSource(items, self._source) 259 src = LinkerSource(items, self._source)
197 return 264 return
198 265
199 is_listable = isinstance(self._source, IListableSource) 266 is_listable = isinstance(self._source, IListableSource)
200 if not is_listable: 267 if not is_listable:
201 raise Exception("Source '%s' can't be listed." % self._source.name) 268 raise Exception("Source '%s' can't be listed." % self._source.name)
202
203 if self._name is None:
204 self._name = self._source.getBasename(self._root_page_path)
205 if self._dir_path is None:
206 self._dir_path = self._source.getDirpath(self._root_page_path)
207
208 if self._dir_path is None:
209 raise Exception("This linker has no directory to start from.")
210 269
211 items = list(self._source.listPath(self._dir_path)) 270 items = list(self._source.listPath(self._dir_path))
212 self._items = collections.OrderedDict() 271 self._items = collections.OrderedDict()
213 with self._source.app.env.page_repository.startBatchGet(): 272 with self._source.app.env.page_repository.startBatchGet():
214 for is_dir, name, data in items: 273 for is_dir, name, data in items:
215 # If `is_dir` is true, `data` will be the directory's source 274 # If `is_dir` is true, `data` will be the directory's source
216 # path. If not, it will be a page factory. 275 # path. If not, it will be a page factory.
217 if is_dir: 276 if is_dir:
218 item = Linker(self._source, self._root_page_path, 277 item = Linker(self._source, data,
219 name=name, dir_path=data) 278 root_page_path=self._root_page_path)
220 else: 279 else:
221 page = data.buildPage() 280 page = data.buildPage()
222 is_self = (page.rel_path == self._root_page_path) 281 is_self = (page.rel_path == self._root_page_path)
223 item = _LinkedPage(page) 282 item = _LinkedPage(page)
224 item._linker_info.name = name 283 item._linker_info.name = name