diff --git a/docs/assets/css/cards.css b/docs/assets/css/cards.css new file mode 100644 index 0000000..95d2e5c --- /dev/null +++ b/docs/assets/css/cards.css @@ -0,0 +1,200 @@ +/************* + Grid Modification +*/ + +.md-typeset .grid.wide-gap { + gap: 1rem; +} + +/************* + Cards +*/ +.card-container { + background-color: white; + color: rgba(0, 0, 0, 0.87); + border-radius: 4px; + box-shadow: rgba(0, 0, 0, 0.2) 0 2px 1px -1px, rgba(0, 0, 0, 0.14) 0 1px 1px 0, rgba(0, 0, 0, 0.12) 0 1px 3px 0; + overflow: hidden; +} + +.card-container.depth-0 { + box-shadow: none; +} + +/************* + Cards - Header +*/ + +.card-header { + padding: 16px 16px 24px; + background: var(--md-primary-fg-color); + color: var(--md-primary-bg-color); +} + +.card-header.backstage-green { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='1368' height='400' fill='none'%3e%3cmask id='a' width='1368' height='401' x='0' y='0' maskUnits='userSpaceOnUse'%3e%3cpath fill='url(%23paint0_linear)' d='M437 116C223 116 112 0 112 0h1256v400c-82 0-225-21-282-109-112-175-436-175-649-175z'/%3e%3cpath fill='url(%23paint1_linear)' d='M1368 400V282C891-29 788 40 711 161 608 324 121 372 0 361v39h1368z'/%3e%3cpath fill='url(%23paint2_linear)' d='M1368 244v156H0V94c92-24 198-46 375 0l135 41c176 51 195 109 858 109z'/%3e%3cpath fill='url(%23paint3_linear)' d='M1252 400h116c-14-7-35-14-116-16-663-14-837-128-1013-258l-85-61C98 28 46 8 0 0v400h1252z'/%3e%3c/mask%3e%3cg mask='url(%23a)'%3e%3cpath fill='white' d='M-172-98h1671v601H-172z'/%3e%3c/g%3e%3cdefs%3e%3clinearGradient id='paint0_linear' x1='602' x2='1093.5' y1='-960.5' y2='272' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint1_linear' x1='482' x2='480' y1='1058.5' y2='70.5' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint2_linear' x1='424' x2='446.1' y1='-587.5' y2='274.6' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint3_linear' x1='587' x2='349' y1='-1120.5' y2='341' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3c/defs%3e%3c/svg%3e"), linear-gradient(90deg, rgb(0, 91, 75), rgb(0, 91, 75)); + color: rgb(255, 255, 255); +} + +.card-header.backstage-blue { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='1368' height='400' fill='none'%3e%3cmask id='a' width='1368' height='401' x='0' y='0' maskUnits='userSpaceOnUse'%3e%3cpath fill='url(%23paint0_linear)' d='M437 116C223 116 112 0 112 0h1256v400c-82 0-225-21-282-109-112-175-436-175-649-175z'/%3e%3cpath fill='url(%23paint1_linear)' d='M1368 400V282C891-29 788 40 711 161 608 324 121 372 0 361v39h1368z'/%3e%3cpath fill='url(%23paint2_linear)' d='M1368 244v156H0V94c92-24 198-46 375 0l135 41c176 51 195 109 858 109z'/%3e%3cpath fill='url(%23paint3_linear)' d='M1252 400h116c-14-7-35-14-116-16-663-14-837-128-1013-258l-85-61C98 28 46 8 0 0v400h1252z'/%3e%3c/mask%3e%3cg mask='url(%23a)'%3e%3cpath fill='white' d='M-172-98h1671v601H-172z'/%3e%3c/g%3e%3cdefs%3e%3clinearGradient id='paint0_linear' x1='602' x2='1093.5' y1='-960.5' y2='272' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint1_linear' x1='482' x2='480' y1='1058.5' y2='70.5' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint2_linear' x1='424' x2='446.1' y1='-587.5' y2='274.6' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint3_linear' x1='587' x2='349' y1='-1120.5' y2='341' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3c/defs%3e%3c/svg%3e"), linear-gradient(90deg, rgb(0, 109, 143), rgb(0, 73, 161)); + color: rgb(255, 255, 255); +} + +.card-header p.subtitle { + margin: 0; + font-size: 0.8em; + font-family: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif; + font-weight: 500; + line-height: 1.57; +} + +.card-header p.title { + margin: 0; + font-size: 1.14em; + font-family: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif; + font-weight: 700; + line-height: 1.6; + margin-bottom: 2px; +} + +/************* + Cards - Media +*/ + +.card-media { + display: block; + background-size: cover; + background-repeat: no-repeat; + background-position: center; + width: 100%; + object-fit: cover; +} + +.card-media p { + margin: 0; +} + +/************* + Cards - Content +*/ + +.card-content { + padding: 16px; +} + +.card-content p { + margin: 0; +} + +.card-content p + p { + margin-top: 1em; +} + +.card-content-title { + margin-top: 0; + font-weight: 400; + font-size: 1.5rem; + line-height: 1.334; + letter-spacing: 0; + margin-bottom: 0.35em; +} + +.card-content-title p { + margin: 0; +} + +/************* + Cards - Actions +*/ + +.card-actions { + padding: 16px; +} + +.card-actions ul:not([hidden]) { + display: flex; + margin: 0; +} + +.card-actions ul { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-wrap: wrap; + gap: 16px; +} +.card-actions.right ul { + justify-content: flex-end; +} + +.card-actions p { + margin: 0; +} + +[dir=ltr] .card-actions ul li { + margin: 0; +} + +.card-actions a { + background-color: transparent; + margin-top: 0; + vertical-align: middle; + font-weight: 500; + font-size: 0.8125rem; + line-height: 1.75; + letter-spacing: 0.02857em; + text-transform: uppercase; +} + +/************* + Cards - Tags +*/ + +.card-tags { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + padding: 16px; +} + +.card-tags ul:not([hidden]) { + display: flex; + margin: 0; +} + +.card-tags ul { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-wrap: wrap; + gap: 16px; +} + +[dir=ltr] .card-tags ul li { + padding: 2px 8px; + margin: 0; + box-sizing: border-box; + font-size: 12px; + font-weight: 500; + color: #000; + background-color: rgba(0, 0, 0, 0.12); + border-radius: 16px; + height: 24px; + line-height: 20px; +} + +/************* + Cards - Divider +*/ + +.card-divider { + margin: 0; + padding: 0 16px; +} + +.card-divider hr { + margin: 0; +} diff --git a/docs/assets/css/extra.css b/docs/assets/css/extra.css new file mode 100644 index 0000000..51a8e5b --- /dev/null +++ b/docs/assets/css/extra.css @@ -0,0 +1,54 @@ +/* Indentation. */ +div.doc-contents:not(.first) { + padding-left: 25px; + border-left: .05rem solid var(--md-typeset-table-color); +} + +/* Normal size fonts on parameter tables */ +.md-typeset div.doc-contents table { + font-size: 1em; + width: 100%; + display: table; +} + +/* Mark external links as such. */ +a.autorefs-external::after { + /* https://primer.style/octicons/arrow-up-right-24 */ + background-image: url('data:image/svg+xml,'); + content: ' '; + + display: inline-block; + vertical-align: middle; + position: relative; + bottom: 0.1em; + margin-left: 0.2em; + margin-right: 0.1em; + + height: 0.7em; + width: 0.7em; + border-radius: 100%; + background-color: var(--md-typeset-a-color); +} +a.autorefs-external:hover::after { + background-color: var(--md-accent-fg-color); +} +span.doc-param-annotation:first-child{ + border-top: 1px solid var(--md-typeset-table-color); + padding-top: 5px; + margin-top: 5px; + margin-bottom: -5px; +} +span.doc-param-annotation { + font-size: 0.8em; + display: block; +} +span.doc-param-default { + font-size: 0.8em; + float: unset; +} +.doc-label { border-radius: 15px; padding: 0 5px; } +.doc-label code { background-color: transparent; color: white;} +.doc-label-special { background-color: blue; color: white; } +.doc-label-private { background-color: red; color: white; } +.doc-label-property { background-color: green; color: white; } +.doc-label-read-only { background-color: yellow; color: black; } diff --git a/docs/assets/css/field-list.css b/docs/assets/css/field-list.css new file mode 100644 index 0000000..ace471b --- /dev/null +++ b/docs/assets/css/field-list.css @@ -0,0 +1,34 @@ +dl.field-list .doc-param-default, dl.doc-field-list .doc-param-default { + float: none; +} + +.field-list > dl, dl.field-list, dl.doc-field-list { + display: flex; + flex-flow: row wrap; + padding-left: 10px; +} + +.field-list > dl > dt, dl.field-list > dt, dl.doc-field-list > dt { + flex-basis: 20%; + font-weight: bold; + word-break: break-word; + padding: 10px 0; + border-bottom: 1px solid #e5e5e5; +} + +.field-list > dl > dt:after, dl.field-list > dt:after { + content: ":"; +} + +[dir=ltr] .field-list > dl > dd, dl.field-list > dd.doc-field-def, dl.doc-field-list > dd.doc-field-def { + flex-basis: 70%; + flex-grow: 1; + margin: 0; + padding: 10px 0 10px 10px; + border-bottom: 1px solid #e5e5e5; +} + +dd.doc-field-def > p:last-child { + padding-bottom: 0; + margin-bottom: 0; +} diff --git a/docs/assets/css/mkdocstrings.css b/docs/assets/css/mkdocstrings.css new file mode 100644 index 0000000..6288f53 --- /dev/null +++ b/docs/assets/css/mkdocstrings.css @@ -0,0 +1,39 @@ +/* Indentation. */ +div.doc-contents:not(.first) { + padding-left: 25px; + border-left: .05rem solid var(--md-typeset-table-color); +} + +/* Mark external links as such. */ +a.external::after, +a.autorefs-external::after { + /* https://primer.style/octicons/arrow-up-right-24 */ + mask-image: url('data:image/svg+xml,'); + -webkit-mask-image: url('data:image/svg+xml,'); + content: ' '; + + display: inline-block; + vertical-align: middle; + position: relative; + + height: 1em; + width: 1em; + background-color: var(--md-typeset-a-color); +} + +a.external:hover::after, +a.autorefs-external:hover::after { + background-color: var(--md-accent-fg-color); +} + +.doc-param-key, .doc-field-term, .doc-section-head { + font-weight: bold; +} + +.doc.doc-heading { + text-transform: none; +} + +h5.doc-heading, h6.doc-heading { + font-size: 1em; +} diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png new file mode 100644 index 0000000..c739158 Binary files /dev/null and b/docs/assets/favicon.png differ diff --git a/docs/assets/images/.gitkeep b/docs/assets/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg new file mode 100644 index 0000000..fa83545 --- /dev/null +++ b/docs/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/docs/explanation.md b/docs/explanation.md new file mode 100644 index 0000000..009914f --- /dev/null +++ b/docs/explanation.md @@ -0,0 +1,227 @@ +--- +title: Explanations +summary: Why Project Forge is the way it is! +date: 2024-08-26 +--- + +This page explains why Project Forge is the way it is. + +Explanation is **discussion** that clarifies and illuminates a particular topic. Explanation is **understanding-oriented.** + +- Give context and background on your library +- Explain why you created it +- Provide multiple examples and approaches of how to work with it +- Help the reader make connections +- Avoid writing instructions or technical descriptions here +- [More Information](https://diataxis.fr/explanation/) + + +## Overview + +Project Forge is a scaffolding tool. A scaffolding tool allows developers to generate a new project by answering a few questions. Developers can go from idea to coding very quickly. + +Additional needs: +- Combine several templates using composition. +- Projects can update themselves with updates from their dependent templates. +- Can use blocks within files to compose parts of files +- Don't ask the same question twice + +Issues to be aware of: +- Context collisions + - The question variable names match in two or more patterns, but their values and use are different. +- Patterns with similar questions but different names. + - `project_name` vs. `name_of_project` +- Storage location of pattern configuration and pattern template + - Local-Local + - Local-Remote + - Remote-Local + - Remote-Remote + +Similar metaphors: +- Stencil +- Pattern +- Template +- Mold +- Minting +- Composition +- Overlays + +## Elements + +- **Location.** A location is a reference to a source. +- **Pattern.** A set of template files to render and a rendering configuration file. +- **Pattern source management.** Downloading and caching the pattern sources (git repos, local directories) +- **Overlay.** A reference to a pattern and the configuration for using it in a specific composition. +- **Composition.** A list of overlays and context to render a project. +- **Template Rendering Engine.** The system that defines the structure of the templates and renders them into the final product +- **Hooks.** Scripts that are stored in a specific place and executed before or after the generation process. +- **Context.** The set of values that the *template rendering engine* uses to render templates +- **Question management.** Manages the questions across multiple overlays in a composition. Handles actual validation (Passes UI a validation function for each question) +- **User input.** Manages the questioning, validation, and error handling of user input for the pattern questions. +- **Migrations.** Adding overlays and updating overlays on projects + + +```mermaid +flowchart LR + CLI(Entry Point) + UI(User\nInterface) + QMgr(Question\nManager) + Render(Rendering) + Hook(Hooks) + User(User) + Comp(Composition) + Pat(Pattern) + PatSource(Pattern\nSource\nManager) + User -->|Calls| CLI + CLI -->|Sets up| UI + CLI -->|Passes info to| QMgr + CLI -->|Validates| Comp + QMgr <-->|Gets questions from| Comp + Comp <-->|Gets questions from| Pat + QMgr -->|Requests answer from| UI + UI -->|Gives answer to| QMgr + QMgr --> Render + Render -->|Gets source from| PatSource + Render --> Hook +``` + +## Location + +The location is a hashable reference to a source. A location consists of a `url` and `path` combination. + +At least one of `path` or `url` must be specified. When `url` is specified, the `path` is resolved using the root of the repository. When `url` is unspecified, the path is resolved using the local filesystem and current working directory. + +### Examples + +- Supports normal URLs (scheme://netloc/path;parameters?query#fragment) and SSH urls (user@domain/path) + +- specify branches, tags, or commits + + - GitHub: + - `https://github.com/owner/repository/tree/[branch-or-tag]` + - `https://github.com/owner/repository/commit/[commit-SHA]` + - GitLab: + - `https://gitlab.com/owner/repository/-/tree/[branch-or-tag]` + - `https://gitlab.com/owner/repository/-/commit/[commit-SHA]` + - Using `@` or `#`: + - `https:///owner/repository@[branch/tag/commit]` + - `https:///owner/repository#[branch/tag/commit]` + +- relative or absolute path to the object within the URL or local filesystem + + +## Pattern + +A _pattern_ consists of a set of template files and a configuration file. The configuration file defines the context required to render the template and the rendering rules. + +*Patterns* are meant to be focused and reusable. *Patterns* are combined with other *patterns* using a *composition.* + +*Patterns* are renderable as-is. They do not need to be a part of a composition. + +### Pattern configuration + +The pattern configuration file have a flexible suffix: `.yaml`, `.json`, `.toml` + +Includes: + +- Pattern settings +- Question objects +- Choice objects + +### Pattern Templates + +- how context is presented +- can you have templates fill parts of other overlays? + - Like treating each overlay as a chain map +- need to figure out how to use inheritance and multiple sources of templates +- need to figure out all the files (including overwrites and ) + +## Pattern source manager + +- Contains the interface for accessing local copies of the pattern templates +- Caching logic + - Local checkouts for remote repositories +- Hashing sources to detect changes + +### Sources of patterns + +- Local directory + - For local Git repositories, we will need to deal with the potential of a dirty repo and how that affects the snapshot + - Treat it as a Non-Git repository + - Don't allow dirty repositories: raise an error + - Non-Git directories + - Hash the contents of the directory. + - Treat it as "always new". + +## Composition + +A composition defines a set of overlays and how they should interact. + +The composition file is a list of overlay objects. An overlay is a reference to a pattern configuration with the ability to adjust the pattern's configuration. + + +## Template engine + +?? + +## Hooks + +?? + +## Context + +?? + +## Question manager + +- Treats everything as a composition. +- Resolves the dependency tree for questions across all overlays +- Uses the *User Interface* to ask questions and updates context and dependencies +- Output is a single context used to render all templates +- Handles validation + - Can pass validation function for each question to user interface +- Interface: + - `Pattern` or `Composition` + - `UI` (reference) + +## User interface + +- Provides interface for different types of questions, validation, error handling, etc +- messaging of status or state +- Abstract enough that it is pluggable for both console or web or other + +- Interface + - `type` + - `prompt` + - `help` + - `choices` + - `multiselect` + - `default` (pre-rendered) + - `validator` (a function to call to validate the input) +- Returns the answer + +## Migrations + +```mermaid +flowchart LR + Composition -->|contains| Overlay -->|references location of| Pattern\nConfiguration -->|references location of| Pattern\nTemplate +``` + +- The anchor for changes is: + - The location of the + - the hashed value of the composition and its overlays + - This detects changes to overridden values and changes to pattern configuration locations (like a more up-to-date git reference) + - +- What to store + - Store the composition location + - Store the composition contents hash to detect if its overrides have changed + - This catches when the pattern configuration location changes (like a more up-to-date git reference) + - For each pattern + - Store the requested location from the overlay + - This will provide information if a specific version of the pattern was requested + - The location may be very generic (like a GitHub repo), or very specific (commit 123456 of the repo) + - Store the location used: + - For git URLs, this would be a URL to a specific reference + - Will need to be able to resolve the specificity of Git references + - branches -> current commit + - tags -> commit diff --git a/docs/gen_doc_stubs.py b/docs/gen_doc_stubs.py new file mode 100644 index 0000000..cb9237a --- /dev/null +++ b/docs/gen_doc_stubs.py @@ -0,0 +1,39 @@ +"""Generate documentation stubs.""" + +from pathlib import Path + +import mkdocs_gen_files + +package_name = "project_forge" + +nav = mkdocs_gen_files.Nav() +mod_symbol = '' + +src_root = Path(__file__).parent.parent +package_root = src_root / package_name + + +for path in sorted(package_root.rglob("*.py")): + module_path = path.with_suffix("") + doc_path = path.with_suffix(".md") + full_doc_path = Path("reference/api", doc_path) + + parts = tuple(module_path.parts) + + if parts[-1] == "__init__": + parts = parts[:-1] + doc_path = doc_path.with_name("index.md") + full_doc_path = full_doc_path.with_name("index.md") + elif parts[-1].startswith("_"): + continue + + nav[parts] = doc_path.as_posix() + + with mkdocs_gen_files.open(full_doc_path, "w") as fd: + ident = ".".join(parts) + fd.write(f"::: {ident}") + + mkdocs_gen_files.set_edit_path(full_doc_path, path) + +with mkdocs_gen_files.open("reference/api/SUMMARY.md", "w") as nav_file: + nav_file.writelines(nav.build_literate_nav()) diff --git a/docs/glossary.md b/docs/glossary.md new file mode 100644 index 0000000..6d0e8f1 --- /dev/null +++ b/docs/glossary.md @@ -0,0 +1,11 @@ +--- +title: Glossary +summary: A glossary of terminology used in the Project Forge package and documentation. +date: 2024-08-26 +--- + +A glossary of terminology and nomenclature used in the project-forge package and documentation. + +!!! warning + + Glossary not implemented yet! Recommend each item has its own anchor link. diff --git a/docs/howtos/index.md b/docs/howtos/index.md new file mode 100644 index 0000000..51b49f4 --- /dev/null +++ b/docs/howtos/index.md @@ -0,0 +1,15 @@ +--- +title: How Tos +summary: Recipes for common tasks using Project Forge. +date: 2024-08-26 +--- + +The *How To's* are intended as recipes for common tasks using project-forge. See the API reference for detailed reference documentation and function, class etc definitions. + +!!! warning + + How Tos not implemented yet! + +How-to guides are directions that take the reader through the steps required to solve a real-world problem. How-to guides are **goal-oriented.** + +[More information](https://diataxis.fr/how-to-guides/) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b1aea5f --- /dev/null +++ b/docs/index.md @@ -0,0 +1,16 @@ +--- +title: project-forge +summary: a tool that combines one or more templates and the user's answers to questions to generate a code project +date: 2024-08-26 +comments: true +--- + +# Project Forge + +{% + include-markdown + "../README.md" + start="" + end="" + rewrite-relative-urls=false +%} diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..f0662ca --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,20 @@ +--- +title: Installation +summary: How to install project-forge. +date: 2024-08-26 +--- + +!!! warning + + Installation instructions not implemented yet! + + +## Installing the Stable Release from PyPI + +To install project-forge, run this command in your terminal: + +``` console +$ pip install project-forge +``` + +This is the preferred method to install project-forge, as it will always install the most recent stable release. You can also replace `pip` with `pipx`. diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000..93c106e --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,31 @@ +--- +title: Quickstart +summary: Quickstart instructions for Project Forge. +date: 2024-08-26 +--- + +Getting started quickly with Project Forge. + +!!! warning + + Quickstart instructions not implemented yet! + +## Prerequisites + +## Installation + +From source...from pypi? + +## The Basics +To use `project-forge` in a project: + +```python +import project_forge +``` + + +## Basic Operations + +## Tips and Tricks + +## Further Reading diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..b61d8f6 --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,15 @@ +--- +title: Reference +summary: Technical reference of Project Forge. +date: 2024-08-26 +--- + +The *Reference* is intended as technical descriptions of the parts of project-forge. + +!!! warning + + Reference not implemented yet! + +Reference guides are technical descriptions of the machinery and how to operate it. Reference material is **information-oriented.** + +[More information](https://diataxis.fr/reference/) diff --git a/docs/reference/validations.md b/docs/reference/validations.md new file mode 100644 index 0000000..b50eb05 --- /dev/null +++ b/docs/reference/validations.md @@ -0,0 +1,33 @@ + +is_boolean | Return true if the object is a boolean value. +is_defined | Checks if a value is defined. +is_divisibleby | Return true if the value is divisible by another one. +is_endingwith | Checks if the value is ending with a string. +is_eq | Test version of ==. +is_even | Checks if a value is even. +is_false | Checks if a value is false. +is_filter | Checks if a filter with a given name is available. +is_float | Checks if this value is a float +is_ge | Test version of >=. +is_gt | Test version of >. +is_in | Test version of in. +is_integer | Checks if this value is an integer. +is_iterable | Checks if this value can be iterated over. +is_le | Test version of <=. +is_lower | Checks if a string is all lowercase. +is_lt | Test version of <. +is_mapping | Checks if this value is a mapping +is_ne | Test version of !=. +is_none | Checks if a value is none. +is_number | Checks if this value is a number. +is_odd | Checks if a value is odd. +is_safe | Checks if a value is safe. +is_sequence | Checks if this value is a sequence +is_startingwith | Checks if the value is starting with a string. +is_string | Checks if this value is a string. +is_test | Checks if a test with a given name is available. +is_true | Checks if a value is true. +is_undefined | Checks if a value is undefined. +is_upper | Checks if a string is all uppercase. + +- pattern diff --git a/docs/tutorials/create_a_pattern.md b/docs/tutorials/create_a_pattern.md new file mode 100644 index 0000000..79e2cc7 --- /dev/null +++ b/docs/tutorials/create_a_pattern.md @@ -0,0 +1,92 @@ +--- +title: Create a Pattern +description: Create a pattern +icon: +date: 2024-08-28 +comments: true +--- +# Create a Pattern + +We are going to convert an existing project into a pattern. This process is going to require _lots_ of search and replace. + +In another tutorial, we will break it into several patterns. + +## Setup + +1. Create a new directory named `patterns.` +2. Download the example project as a `.zip` file from TODO + +3. Decompress the file and copy the `todo-manager` project into `patterns/`. + +!!! Note + +​ If you are doing this with an actual project, remove the `.git` directory if it exists. + +4. Inside the `patterns/` directory, create a file named `core_pattern.toml` + +## Extracting questions + +Questions are asked of the user during the project generation. Each answer is set as the value of a variable used in patterns. We will go through this example project and highlight information that would or could vary between projects. We will create questions to get the data from the user, and then we will replace the existing values in the example project with placeholders. + +### Different names in different places + +The outer folder is named `todo-manager`, the code folder is named `todo_manager`, and the title of the `README.md` file is `Todo Manager`. Are these three different variables that require three different questions? Is this one variable with three permutations that only require one question? + +We will treat them as three variables. However, we don't necessarily require three answers. + +### Project name + +Let's start with the title of the `README.md` file. We'll call this the `project_name`. We want to use this wherever we need a human-friendly name. Add a question to `core_pattern.toml` + +```toml title="patterns/core_pattern.toml" +[[questions]] +name = "project_name" +prompt = "What is the human-friendly name of the project?" +type = "str" +default = "My Project" +``` + +### Package name + +Now, let's deal with the outer folder name and the code folder. Python package names (the package name registered with the Python package index) are [normalized](https://packaging.python.org/en/latest/specifications/name-normalization/) using hyphens. However, hyphens are not allowed in [Python identifiers](https://docs.python.org/3.12/reference/lexical_analysis.html#identifiers). + +We'll call the name of the code folder the `package_name`. Add this question to `core_pattern.toml`: + +```toml title="patterns/core_pattern.toml" +# ... previous question + +[[questions]] +name = "package_name" +promp = "What is the name of the Python package?" +type = "str" +default = "{{ project_name|lower|replace(' ', '_') }}" +``` + +This sets the default answer to a modification of the previous question's answer. It converts the answer to lowercase and replaces all spaces with underscores. This is likely what the user wants, and if so, they can accept the default without having to type anything. + +### Repo name + +We'll call the name of the outer folder the `repo_name`. Add this question to `core_pattern.toml`: + +```toml title="patterns/core_pattern.toml" +# ... previous questions + +[[questions]] +name = "repo_name" +prompt = "What is the name of the project repository?" +type = "str" +default = "{{ package_name|replace('_', '-') }}" +``` + +This question modifies the answer from `package_name`, replacing the underscores with hyphens. + +### Set the placeholders in files + +- Search for `Todo Manager` and replace it with `{{ project_name }}` +- Search for `todo_manager` and replace it with `{{ package_name }}` +- Search for `todo-manager` and replace it with `{{ repo_name }}` + +### Renaming files and directories with placeholders + +- Rename the `todo-manager` directory to `{{ repo_name }}` +- Rename the `todo_manager` directory to `{{ package_name }}` diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md new file mode 100644 index 0000000..7a02777 --- /dev/null +++ b/docs/tutorials/index.md @@ -0,0 +1,22 @@ +--- +title: Tutorials +summary: Tutorials for how to use a tool that combines one or more templates and the user's answers to questions to generate a code project. +date: 2024-08-26 +--- + +This page provides tutorials on how to use project-forge. + +!!! warning + + Tutorials not implemented yet! Tutorials are for learning how to use the package. + +Tutorials are lessons that take the reader by the hand through a series of steps to complete a project of some kind. Tutorials are **learning-oriented.** + +- Help newcomers with getting started +- Teach readers about your library by making them write code +- Inspire confidence through examples that work for everyone, repeatably +- Give readers an immediate sense of achievement +- Show concrete examples, no abstractions +- Provide the minimum necessary explanation +- Avoid any distractions +- [More information](https://diataxis.fr/tutorials/) diff --git a/overrides/mkdocstrings/python/material/docstring/attributes.html b/overrides/mkdocstrings/python/material/docstring/attributes.html new file mode 100644 index 0000000..ce50d65 --- /dev/null +++ b/overrides/mkdocstrings/python/material/docstring/attributes.html @@ -0,0 +1,90 @@ +{{ log.debug("Rendering attributes section") }} + +{% import "language.html" as lang with context %} + +{% if config.docstring_section_style == "table" %} + {% block table_style scoped %} +

{{ section.title or lang.t("Attributes:") }}

+ + + + + + + + + + {% for attribute in section.value %} + + + + + + {% endfor %} + +
{{ lang.t("Name") }}{{ lang.t("Type") }}{{ lang.t("Description") }}
{{ attribute.name }} + {% if attribute.annotation %} + {% with expression = attribute.annotation %} + {% include "expression.html" with context %} + {% endwith %} + {% endif %} + +
+ {{ attribute.description|convert_markdown(heading_level, html_id) }} +
+
+ {% endblock table_style %} +{% elif config.docstring_section_style == "list" %} + {% block list_style scoped %} +

{{ section.title or lang.t("Attributes:") }}

+ + {% endblock list_style %} +{% elif config.docstring_section_style == "spacy" %} + {% block spacy_style scoped %} + + + + + + + + + {% for attribute in section.value %} + + + + + {% endfor %} + +
{{ (section.title or lang.t("ATTRIBUTE")).rstrip(":").upper() }}{{ lang.t("DESCRIPTION") }}
{{ attribute.name }} +
+ {{ attribute.description|convert_markdown(heading_level, html_id) }} +
+

+ {% if attribute.annotation %} + + TYPE: + {% with expression = attribute.annotation %} + {% include "expression.html" with context %} + {% endwith %} + + {% endif %} +

+
+ {% endblock spacy_style %} +{% endif %} diff --git a/overrides/mkdocstrings/python/material/docstring/parameters.html b/overrides/mkdocstrings/python/material/docstring/parameters.html new file mode 100644 index 0000000..ab4889e --- /dev/null +++ b/overrides/mkdocstrings/python/material/docstring/parameters.html @@ -0,0 +1,98 @@ +{{ log.debug("Rendering parameters section") }} + +{% import "language.html" as lang with context %} + +{% if config.docstring_section_style == "table" %} + {% block table_style scoped %} +

{{ section.title or lang.t("Parameters:") }}

+ + + + + + + + + + + {% for parameter in section.value %} + + + + + + + {% endfor %} + +
{{ lang.t("Name") }}{{ lang.t("Type") }}{{ lang.t("Description") }}{{ lang.t("Default") }}
{{ parameter.name }} + {% if parameter.annotation %} + {% with expression = parameter.annotation %} + {% include "expression.html" with context %} + {% endwith %} + {% endif %} + +
+ {{ parameter.description|convert_markdown(heading_level, html_id) }} +
+
+ {% if parameter.default %} + {% with expression = parameter.default %} + {% include "expression.html" with context %} + {% endwith %} + {% else %} + {{ lang.t("required") }} + {% endif %} +
+ {% endblock table_style %} +{% elif config.docstring_section_style == "list" %} + {% block list_style scoped %} +

{{ section.title or lang.t("Parameters:") }}

+ + {% endblock list_style %} +{% elif config.docstring_section_style == "spacy" %} + {% block spacy_style scoped %} +

{{ section.title or lang.t("Parameters:") }}

+
+ {% for parameter in section.value %} +
{{ parameter.name }}
+
+
+ {{ parameter.description|convert_markdown(heading_level, html_id) }} +
+ {% if parameter.annotation %}

+ {{ lang.t("TYPE:") }} + {% with expression = parameter.annotation %} + {% include "expression.html" with context %} + {% endwith %} +

{% endif %} + {% if parameter.default %}

+ {{ lang.t("DEFAULT:") }} + {% with expression = parameter.default %} + {% include "expression.html" with context %} + {% endwith %} +

{% endif %} +
+ {% endfor %} +
+ {% endblock spacy_style %} +{% endif %} diff --git a/overrides/mkdocstrings/python/material/docstring/raises.html b/overrides/mkdocstrings/python/material/docstring/raises.html new file mode 100644 index 0000000..628e41b --- /dev/null +++ b/overrides/mkdocstrings/python/material/docstring/raises.html @@ -0,0 +1,72 @@ +{{ log.debug("Rendering raises section") }} + +{% import "language.html" as lang with context %} + +{% if config.docstring_section_style == "table" %} + {% block table_style scoped %} +

{{ section.title or lang.t("Raises:") }}

+ + + + + + + + + {% for raises in section.value %} + + + + + {% endfor %} + +
{{ lang.t("Type") }}{{ lang.t("Description") }}
+ {% if raises.annotation %} + {% with expression = raises.annotation %} + {% include "expression.html" with context %} + {% endwith %} + {% endif %} + +
+ {{ raises.description|convert_markdown(heading_level, html_id) }} +
+
+ {% endblock table_style %} +{% elif config.docstring_section_style == "list" %} + {% block list_style scoped %} +

{{ lang.t(section.title) or lang.t("Raises:") }}

+ + {% endblock list_style %} +{% elif config.docstring_section_style == "spacy" %} + {% block spacy_style scoped %} +

{{ (section.title or lang.t("Raises:")) }}

+
+ {% for raises in section.value %} +
+ {% with expression = raises.annotation %} + {% include "expression.html" with context %} + {% endwith %} +
+
+
+ {{ raises.description|convert_markdown(heading_level, html_id) }} +
+
+ {% endfor %} +
+ {% endblock spacy_style %} +{% endif %} diff --git a/overrides/mkdocstrings/python/material/docstring/returns.html b/overrides/mkdocstrings/python/material/docstring/returns.html new file mode 100644 index 0000000..bd60840 --- /dev/null +++ b/overrides/mkdocstrings/python/material/docstring/returns.html @@ -0,0 +1,94 @@ +{{ log.debug("Rendering returns section") }} + +{% import "language.html" as lang with context %} + +{% if config.docstring_section_style == "table" %} + {% block table_style scoped %} + {% set name_column = section.value|selectattr("name")|any %} +

{{ section.title or lang.t("Returns:") }}

+ + + + {% if name_column %}{% endif %} + + + + + + {% for returns in section.value %} + + {% if name_column %}{% endif %} + + + + {% endfor %} + +
{{ lang.t("Name") }}{{ lang.t("Type") }}{{ lang.t("Description") }}
{% if returns.name %}{{ returns.name }}{% endif %} + {% if returns.annotation %} + {% with expression = returns.annotation %} + {% include "expression.html" with context %} + {% endwith %} + {% endif %} + +
+ {{ returns.description|convert_markdown(heading_level, html_id) }} +
+
+ {% endblock table_style %} +{% elif config.docstring_section_style == "list" %} + {% block list_style scoped %} +

{{ section.title or lang.t("Returns:") }}

+ + {% endblock list_style %} +{% elif config.docstring_section_style == "spacy" %} + {% block spacy_style scoped %} +

{{ (section.title or lang.t("Returns:")) }}

+
+ {% for returns in section.value %} +
+ {% if returns.name %} + {{ returns.name }} + {% elif returns.annotation %} + + {% with expression = returns.annotation %} + {% include "expression.html" with context %} + {% endwith %} + + {% endif %} +
+
+
+ {{ returns.description|convert_markdown(heading_level, html_id) }} +
+ {% if returns.name and returns.annotation %} +

+ + {{ lang.t("TYPE:") }} + {% with expression = returns.annotation %} + {% include "expression.html" with context %} + {% endwith %} + +

+ {% endif %} +
+ {% endfor %} +
+ {% endblock spacy_style %} +{% endif %} diff --git a/overrides/partials/comments.html b/overrides/partials/comments.html new file mode 100644 index 0000000..d7ae25a --- /dev/null +++ b/overrides/partials/comments.html @@ -0,0 +1,37 @@ +{% if page.meta.comments %} +

{{ lang.t("meta.comments") }}

+ + + + +{% endif %}