changeset 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 8703be118430
children d74ae33832ae
files piecrust/__init__.py piecrust/commands/builtin/scaffolding.py piecrust/commands/builtin/util.py piecrust/plugins/builtin.py piecrust/resources/prepare/default.html
diffstat 5 files changed, 167 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/__init__.py	Sat Sep 13 14:47:01 2014 -0700
+++ b/piecrust/__init__.py	Sat Sep 13 23:31:21 2014 -0700
@@ -22,3 +22,6 @@
 except ImportError:
     APP_VERSION = 'unknown'
 
+import os.path
+RESOURCES_DIR = os.path.join(os.path.dirname(__file__), 'resources')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/commands/builtin/scaffolding.py	Sat Sep 13 23:31:21 2014 -0700
@@ -0,0 +1,150 @@
+import os
+import os.path
+import re
+import io
+import time
+import logging
+import textwrap
+from piecrust import RESOURCES_DIR
+from piecrust.chefutil import print_help_item
+from piecrust.commands.base import ExtendableChefCommand, ChefCommandExtension
+from piecrust.sources.base import IPreparingSource, MODE_CREATING
+from piecrust.uriutil import multi_replace
+
+
+logger = logging.getLogger(__name__)
+
+
+def make_title(slug):
+    slug = re.sub(r'[\-_]', ' ', slug)
+    return slug.title()
+
+
+class PrepareCommand(ExtendableChefCommand):
+    """ Chef command for creating pages with some default content.
+    """
+    def __init__(self):
+        super(PrepareCommand, self).__init__()
+        self.name = 'prepare'
+        self.description = "Prepares new content for your website."
+
+    def setupParser(self, parser, app):
+        # Don't setup anything if this is a null app
+        # (for when `chef` is run from outside a website)
+        if app.root_dir is None:
+            return
+
+        subparsers = parser.add_subparsers()
+        for src in app.sources:
+            if not isinstance(src, IPreparingSource):
+                logger.debug("Skipping source '%s' because it's not "
+                             "preparable." % src.name)
+                continue
+            if src.is_theme_source:
+                logger.debug("Skipping source '%s' because it's a theme "
+                             "source." % src.name)
+                continue
+            p = subparsers.add_parser(
+                    src.item_name,
+                    help="Creates an empty page in the '%s' source." % src.name)
+            src.setupPrepareParser(p, app)
+            p.add_argument('-t', '--template', default='default',
+                           help="The template to use, which will change the "
+                                "generated text and header.")
+            p.set_defaults(source=src)
+
+    def run(self, ctx):
+        app = ctx.app
+        source = ctx.args.source
+        metadata = source.buildMetadata(ctx.args)
+        rel_path, metadata = source.findPagePath(metadata, MODE_CREATING)
+        path = source.resolveRef(rel_path)
+        name, ext = os.path.splitext(path)
+        if ext == '.*':
+            path = '%s.%s' % (name,
+                    app.config.get('site/default_auto_format'))
+        if os.path.exists(path):
+            raise Exception("'%s' already exists." % path)
+
+        tpl_name = ctx.args.template
+        extensions = self.getExtensions(app)
+        ext = next(
+                filter(
+                    lambda e: tpl_name in e.getTemplateNames(ctx.app),
+                    extensions),
+                None)
+        if ext is None:
+            raise Exception("No such page template: %s" % tpl_name)
+
+        tpl_text = ext.getTemplate(ctx.app, tpl_name)
+        title = (metadata.get('slug') or metadata.get('path') or
+                'Untitled page')
+        title = make_title(title)
+        tokens = {
+                '%title%': title,
+                '%time.today%': time.strftime('%Y/%m/%d'),
+                '%time.now%': time.strftime('%H:%M:%S')}
+        tpl_text = multi_replace(tpl_text, tokens)
+
+        logger.info("Creating page: %s" % os.path.relpath(path, app.root_dir))
+        if not os.path.exists(os.path.dirname(path)):
+            os.makedirs(os.path.dirname(path), 0o755)
+
+        with open(path, 'w') as f:
+            f.write(tpl_text)
+
+
+class DefaultPrepareTemplatesCommandExtension(ChefCommandExtension):
+    """ Provides the default scaffolding tempaltes to the `prepare`
+        command.
+    """
+    def __init__(self):
+        super(DefaultPrepareTemplatesCommandExtension, self).__init__()
+        self.command_name = 'prepare'
+
+    def getTemplateNames(self, app):
+        return ['default', 'rss', 'atom']
+
+    def getTemplateDescription(self, app, name):
+        descs = {
+                'default': "The default template, for a simple page.",
+                'rss': "A fully functional RSS feed.",
+                'atom': "A fully functional Atom feed."}
+        return descs[name]
+
+    def getTemplate(self, app, name):
+        assert name in ['default', 'rss', 'atom']
+        src_path = os.path.join(RESOURCES_DIR, 'prepare', '%s.html' % name)
+        with open(src_path, 'r', encoding='utf8') as fp:
+            return fp.read()
+
+
+class DefaultPrepareTemplatesHelpTopic(ChefCommandExtension):
+    """ Provides help topics for the `prepare` command.
+    """
+    command_name = 'help'
+
+    def getHelpTopics(self):
+        return [('scaffolding',
+            "Available templates for the 'prepare' command.")]
+
+    def getHelpTopic(self, topic, app):
+        with io.StringIO() as tplh:
+            extensions = app.plugin_loader.getCommandExtensions()
+            for e in extensions:
+                if e.command_name == 'prepare' and e.supports(app):
+                    for n in e.getTemplateNames(app):
+                        d = e.getTemplateDescription(app, n)
+                        print_help_item(tplh, n, d)
+            help_list = tplh.getvalue()
+
+        help_txt = (
+                textwrap.fill("Running the 'prepare' command will let "
+                "PieCrust setup a page for you in the correct place, with "
+                "some hopefully useful default text.") +
+                "\n\n" +
+                textwrap.fill("The following templates are available:") +
+                "\n\n" +
+                help_list)
+        return help_txt
+
--- a/piecrust/commands/builtin/util.py	Sat Sep 13 14:47:01 2014 -0700
+++ b/piecrust/commands/builtin/util.py	Sat Sep 13 23:31:21 2014 -0700
@@ -6,7 +6,6 @@
 import yaml
 from piecrust.app import CONFIG_PATH
 from piecrust.commands.base import ChefCommand
-from piecrust.sources.base import IPreparingSource, MODE_CREATING
 
 
 logger = logging.getLogger(__name__)
@@ -66,55 +65,6 @@
             shutil.rmtree(cache_dir)
 
 
-class PrepareCommand(ChefCommand):
-    def __init__(self):
-        super(PrepareCommand, self).__init__()
-        self.name = 'prepare'
-        self.description = "Prepares new content for your website."
-
-    def setupParser(self, parser, app):
-        # Don't setup anything if this is a null app
-        # (for when `chef` is run from outside a website)
-        if app.root_dir is None:
-            return
-
-        subparsers = parser.add_subparsers()
-        for src in app.sources:
-            if not isinstance(src, IPreparingSource):
-                logger.debug("Skipping source '%s' because it's not "
-                             "preparable." % src.name)
-                continue
-            if src.is_theme_source:
-                logger.debug("Skipping source '%s' because it's a theme "
-                             "source." % src.name)
-                continue
-            p = subparsers.add_parser(src.item_name)
-            src.setupPrepareParser(p, app)
-            p.set_defaults(source=src)
-
-    def run(self, ctx):
-        app = ctx.app
-        source = ctx.args.source
-        metadata = source.buildMetadata(ctx.args)
-        rel_path, metadata = source.findPagePath(metadata, MODE_CREATING)
-        path = source.resolveRef(rel_path)
-        name, ext = os.path.splitext(path)
-        if ext == '.*':
-            path = '%s.%s' % (name,
-                    app.config.get('site/default_auto_format'))
-        if os.path.exists(path):
-            raise Exception("'%s' already exists." % path)
-
-        logger.info("Creating page: %s" % os.path.relpath(path, app.root_dir))
-        if not os.path.exists(os.path.dirname(path)):
-            os.makedirs(os.path.dirname(path), 0o755)
-        with open(path, 'w') as f:
-            f.write('---\n')
-            f.write('title: %s\n' % 'Unknown title')
-            f.write('---\n')
-            f.write("This is a new page!\n")
-
-
 class ImportCommand(ChefCommand):
     def __init__(self):
         super(ImportCommand, self).__init__()
--- a/piecrust/plugins/builtin.py	Sat Sep 13 14:47:01 2014 -0700
+++ b/piecrust/plugins/builtin.py	Sat Sep 13 23:31:21 2014 -0700
@@ -2,9 +2,12 @@
 from piecrust.commands.builtin.baking import (BakeCommand, ShowRecordCommand)
 from piecrust.commands.builtin.info import (RootCommand, ShowConfigCommand,
         FindCommand, ShowRoutesCommand, ShowPathsCommand)
+from piecrust.commands.builtin.scaffolding import (PrepareCommand,
+        DefaultPrepareTemplatesCommandExtension,
+        DefaultPrepareTemplatesHelpTopic)
 from piecrust.commands.builtin.serving import (ServeCommand)
 from piecrust.commands.builtin.util import (InitCommand, PurgeCommand,
-        PrepareCommand, ImportCommand)
+        ImportCommand)
 from piecrust.data.provider import (IteratorDataProvider, BlogDataProvider)
 from piecrust.formatting.markdownformatter import MarkdownFormatter
 from piecrust.formatting.smartypantsformatter import SmartyPantsFormatter
@@ -42,7 +45,9 @@
                 ServeCommand()]
 
     def getCommandExtensions(self):
-        return []
+        return [
+                DefaultPrepareTemplatesCommandExtension(),
+                DefaultPrepareTemplatesHelpTopic()]
 
     def getSources(self):
         return [
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/resources/prepare/default.html	Sat Sep 13 23:31:21 2014 -0700
@@ -0,0 +1,7 @@
+---
+title: %title%
+time: '%time.now%'
+---
+
+This is a brand new page.
+