1import os
2
3from plain.internal import internalcode
4from plain.urls import path
5
6from .exceptions import PageNotFoundError
7from .pages import Page
8
9
10@internalcode
11class PagesRegistry:
12 """
13 The registry loads up all the pages at once, so we only have to do a
14 dict key lookup at runtime to get a page.
15 """
16
17 def __init__(self):
18 # url path -> file path
19 self.registered_pages = {}
20
21 def get_page_urls(self):
22 """
23 Generate a list of real urls based on the files that exist.
24 This way, you get a concrete url reversingerror if you try
25 to refer to a page/url that isn't going to work.
26 """
27 paths = []
28
29 for url_name in self.registered_pages.keys():
30 page = self.get_page(url_name)
31 url = page.get_url_path()
32 view_class = page.get_view_class()
33
34 paths.append(
35 path(
36 url,
37 view_class,
38 name=url_name,
39 )
40 )
41
42 return paths
43
44 def discover_pages(self, pages_dir):
45 for root, _, files in os.walk(pages_dir):
46 for file in files:
47 relative_path = os.path.relpath(os.path.join(root, file), pages_dir)
48 absolute_path = os.path.join(root, file)
49
50 page_args = (relative_path, absolute_path)
51 url_name = Page(*page_args).get_url_name()
52
53 # Some pages don't get a url (like templates)
54 if url_name is None:
55 continue
56
57 self.registered_pages[url_name] = page_args
58
59 def get_page(self, url_name):
60 try:
61 page_args = self.registered_pages[url_name]
62 # Instantiate the page here, so we don't store a ton of cached data over time
63 # as we render all the pages
64 return Page(*page_args)
65 except KeyError:
66 raise PageNotFoundError(f"Could not find a page for {url_name}")
67
68
69pages_registry = PagesRegistry()