diff --git a/docs/comparisons.md b/docs/comparisons.md index 89ad29fbe..264df34bb 100644 --- a/docs/comparisons.md +++ b/docs/comparisons.md @@ -26,6 +26,7 @@ docs! We don't want to be biased, but it's easy that we tend to be: | Requires programming | No | No | Yes, JS | | Requires templates to have a suffix | Yes by default, configurable[^3] | No, not configurable | You choose | | Task hooks | Yes | Yes | Yes | +| Context hooks | Yes[^5] | Yes | ? | | Template in a subfolder | Not required, but you choose | Yes, required | Yes, required | | Template package format | Git repo[^2], Git bundle, folder | Git or Mercurial repo, Zip file | NPM package | | Template updates | **Yes**[^4] | No | No | @@ -49,3 +50,8 @@ docs! We don't want to be biased, but it's easy that we tend to be: [^4]: Only for git templates, because Copier uses git tags to obtain available versions and extract smart diffs between them. + +[^5]: Context hooks are provided through the [`ContextHook` extension][context-hook]. + +[context-hook]: + https://github.com/copier-org/copier-templates-extensions#context-hook-extension diff --git a/docs/configuring.md b/docs/configuring.md index 64db9944a..8f7610592 100644 --- a/docs/configuring.md +++ b/docs/configuring.md @@ -699,9 +699,11 @@ on them, so they are always installed when Copier is installed. provides a `slugify` filter using [python-slugify](https://github.com/un33k/python-slugify). - - [`copier_templates_extensions.TemplateExtensionLoader`](https://github.com/pawamoy/copier-templates-extensions): + - [`copier_templates_extensions.TemplateExtensionLoader`](https://github.com/copier-org/copier-templates-extensions): enhances the extension loading mecanism to allow templates writers to put their - extensions directly in their templates. + extensions directly in their templates. It also allows to modify the rendering context + (the Jinja variables that you can use in your templates) before + rendering templates, see [using a context hook](../faq#how-can-i-alter-the-context-before-rendering-the-project). - [`jinja_markdown.MarkdownExtension`](https://github.com/jpsca/jinja-markdown): provides a `markdown` tag that will render Markdown to HTML using [PyMdown extensions](https://facelessuser.github.io/pymdown-extensions/). diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 000000000..db9b54bba --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,100 @@ +# Frequently Asked Questions + +## How can I alter the context before rendering the project? + +Similar questions: + +- **How can I add/remove variables to/from the rendering context?** +- **How to infer context variables based on the users answers, without prompting + users?** + +Answer: + +**Use the [`ContextHook` extension][context-hook].** + +The [`ContextHook` extension][context-hook] lets you modify the context used to render +templates, so that you can add, change or remove variables. + +[context-hook]: + https://github.com/copier-org/copier-templates-extensions#context-hook-extension + +In order for Copier to be able to load and use the extension when generating a project, +it must be installed alongside Copier itself. More details in the +[`jinja_extensions` docs](#jinja_extensions). + +You can then configure your Jinja extensions in Copier's configuration file: + +```yaml title="copier.yaml" +_jinja_extensions: + - copier_templates_extensions.TemplateExtensionLoader + - extensions/context.py:ContextUpdater +``` + +Following this example, you are supposed to provide a `context.py` file in the +`extensions` folder at the root of your template to modify the context. If for example +your `copier.yaml` contains a multiple-choice variable like this: + +```yaml title="copier.yaml" +flavor: + type: str + choices: + - Docker + - Instances + - Kubernetes + - None +``` + +The `context.py` file contains your context hook which could look like: + +```python title="extensions/context.py" +from copier_templates_extensions import ContextHook + + +class ContextUpdater(ContextHook): + def hook(self, context): + flavor = context["flavor"] # user's answer to the "flavor" question + return { + "isDocker": flavor == "docker" + "isK8s": flavor == "kubernetes" + "isInstances": flavor == "instances" + "isLite": flavor == "none" + "isNotDocker": flavor != "docker" + "isNotK8s": flavor != "kubernetes" + "isNotInstances": flavor != "instances" + "isNotLite": flavor != "none" + "hasContainers": flavor in {"docker", "kubernetes"} + } +``` + +Before rendering each templated file/folder, the context will be updated with this new +context object that you return from the hook. If you wish to update the context in-place +rather than update it, set the `update` class attribute to false: + +```python title="extensions/context.py" +from copier_templates_extensions import ContextHook + + +class ContextUpdater(ContextHook): + update = False + + def hook(self, context): + flavor = context["flavor"] # user's answer to the "flavor" question + + context["isDocker"] = flavor == "docker" + context["isK8s"] = flavor == "kubernetes" + context["isInstances"] = flavor == "instances" + context["isLite"] = flavor == "none" + + context["isNotDocker"] = flavor != "docker" + context["isNotK8s"] = flavor != "kubernetes" + context["isNotInstances"] = flavor != "instances" + context["isNotLite"] = flavor != "none" + + context["hasContainers"] = context["isDocker"] or context["isK8s"] + + # you can now actually remove items from the context + del context["flavor"] +``` + +Now you can use these added variables in your Jinja templates, and in files and folders +names! diff --git a/mkdocs.yml b/mkdocs.yml index 9ef2f5343..1d6b1dc9e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,7 @@ nav: - user_data.py: "reference/user_data.md" - vcs.py: "reference/vcs.md" - Comparisons: comparisons.md + - Frequently Asked Questions: faq.md - Contributing: "contributing.md" - Changelog: "changelog.md"