Mercurial > piecrust2
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 |