Mercurial > piecrust2
comparison piecrust/commands/builtin/scaffolding.py @ 100:69d5eecfa449
Better `prepare` command, with templates and help topics.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sat, 13 Sep 2014 23:31:21 -0700 |
parents | |
children | 8355eb9dd8fe |
comparison
equal
deleted
inserted
replaced
99:8703be118430 | 100:69d5eecfa449 |
---|---|
1 import os | |
2 import os.path | |
3 import re | |
4 import io | |
5 import time | |
6 import logging | |
7 import textwrap | |
8 from piecrust import RESOURCES_DIR | |
9 from piecrust.chefutil import print_help_item | |
10 from piecrust.commands.base import ExtendableChefCommand, ChefCommandExtension | |
11 from piecrust.sources.base import IPreparingSource, MODE_CREATING | |
12 from piecrust.uriutil import multi_replace | |
13 | |
14 | |
15 logger = logging.getLogger(__name__) | |
16 | |
17 | |
18 def make_title(slug): | |
19 slug = re.sub(r'[\-_]', ' ', slug) | |
20 return slug.title() | |
21 | |
22 | |
23 class PrepareCommand(ExtendableChefCommand): | |
24 """ Chef command for creating pages with some default content. | |
25 """ | |
26 def __init__(self): | |
27 super(PrepareCommand, self).__init__() | |
28 self.name = 'prepare' | |
29 self.description = "Prepares new content for your website." | |
30 | |
31 def setupParser(self, parser, app): | |
32 # Don't setup anything if this is a null app | |
33 # (for when `chef` is run from outside a website) | |
34 if app.root_dir is None: | |
35 return | |
36 | |
37 subparsers = parser.add_subparsers() | |
38 for src in app.sources: | |
39 if not isinstance(src, IPreparingSource): | |
40 logger.debug("Skipping source '%s' because it's not " | |
41 "preparable." % src.name) | |
42 continue | |
43 if src.is_theme_source: | |
44 logger.debug("Skipping source '%s' because it's a theme " | |
45 "source." % src.name) | |
46 continue | |
47 p = subparsers.add_parser( | |
48 src.item_name, | |
49 help="Creates an empty page in the '%s' source." % src.name) | |
50 src.setupPrepareParser(p, app) | |
51 p.add_argument('-t', '--template', default='default', | |
52 help="The template to use, which will change the " | |
53 "generated text and header.") | |
54 p.set_defaults(source=src) | |
55 | |
56 def run(self, ctx): | |
57 app = ctx.app | |
58 source = ctx.args.source | |
59 metadata = source.buildMetadata(ctx.args) | |
60 rel_path, metadata = source.findPagePath(metadata, MODE_CREATING) | |
61 path = source.resolveRef(rel_path) | |
62 name, ext = os.path.splitext(path) | |
63 if ext == '.*': | |
64 path = '%s.%s' % (name, | |
65 app.config.get('site/default_auto_format')) | |
66 if os.path.exists(path): | |
67 raise Exception("'%s' already exists." % path) | |
68 | |
69 tpl_name = ctx.args.template | |
70 extensions = self.getExtensions(app) | |
71 ext = next( | |
72 filter( | |
73 lambda e: tpl_name in e.getTemplateNames(ctx.app), | |
74 extensions), | |
75 None) | |
76 if ext is None: | |
77 raise Exception("No such page template: %s" % tpl_name) | |
78 | |
79 tpl_text = ext.getTemplate(ctx.app, tpl_name) | |
80 title = (metadata.get('slug') or metadata.get('path') or | |
81 'Untitled page') | |
82 title = make_title(title) | |
83 tokens = { | |
84 '%title%': title, | |
85 '%time.today%': time.strftime('%Y/%m/%d'), | |
86 '%time.now%': time.strftime('%H:%M:%S')} | |
87 tpl_text = multi_replace(tpl_text, tokens) | |
88 | |
89 logger.info("Creating page: %s" % os.path.relpath(path, app.root_dir)) | |
90 if not os.path.exists(os.path.dirname(path)): | |
91 os.makedirs(os.path.dirname(path), 0o755) | |
92 | |
93 with open(path, 'w') as f: | |
94 f.write(tpl_text) | |
95 | |
96 | |
97 class DefaultPrepareTemplatesCommandExtension(ChefCommandExtension): | |
98 """ Provides the default scaffolding tempaltes to the `prepare` | |
99 command. | |
100 """ | |
101 def __init__(self): | |
102 super(DefaultPrepareTemplatesCommandExtension, self).__init__() | |
103 self.command_name = 'prepare' | |
104 | |
105 def getTemplateNames(self, app): | |
106 return ['default', 'rss', 'atom'] | |
107 | |
108 def getTemplateDescription(self, app, name): | |
109 descs = { | |
110 'default': "The default template, for a simple page.", | |
111 'rss': "A fully functional RSS feed.", | |
112 'atom': "A fully functional Atom feed."} | |
113 return descs[name] | |
114 | |
115 def getTemplate(self, app, name): | |
116 assert name in ['default', 'rss', 'atom'] | |
117 src_path = os.path.join(RESOURCES_DIR, 'prepare', '%s.html' % name) | |
118 with open(src_path, 'r', encoding='utf8') as fp: | |
119 return fp.read() | |
120 | |
121 | |
122 class DefaultPrepareTemplatesHelpTopic(ChefCommandExtension): | |
123 """ Provides help topics for the `prepare` command. | |
124 """ | |
125 command_name = 'help' | |
126 | |
127 def getHelpTopics(self): | |
128 return [('scaffolding', | |
129 "Available templates for the 'prepare' command.")] | |
130 | |
131 def getHelpTopic(self, topic, app): | |
132 with io.StringIO() as tplh: | |
133 extensions = app.plugin_loader.getCommandExtensions() | |
134 for e in extensions: | |
135 if e.command_name == 'prepare' and e.supports(app): | |
136 for n in e.getTemplateNames(app): | |
137 d = e.getTemplateDescription(app, n) | |
138 print_help_item(tplh, n, d) | |
139 help_list = tplh.getvalue() | |
140 | |
141 help_txt = ( | |
142 textwrap.fill("Running the 'prepare' command will let " | |
143 "PieCrust setup a page for you in the correct place, with " | |
144 "some hopefully useful default text.") + | |
145 "\n\n" + | |
146 textwrap.fill("The following templates are available:") + | |
147 "\n\n" + | |
148 help_list) | |
149 return help_txt | |
150 |