scaffold-ca-python 0.2.1__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scaffold_ca_python/commands/generate_model.py +18 -6
- scaffold_ca_python/commands/generate_use_case.py +23 -13
- scaffold_ca_python/core/module_locator.py +25 -2
- scaffold_ca_python/core/name_utils.py +8 -0
- scaffold_ca_python/factory/simple/delete_module_factory.py +4 -38
- scaffold_ca_python/factory/simple/model_factory.py +28 -23
- scaffold_ca_python/factory/simple/use_case_factory.py +4 -4
- scaffold_ca_python/templates/model/__init__.py.jinja2 +1 -0
- scaffold_ca_python/templates/model/gateway.py.jinja2 +7 -0
- scaffold_ca_python/templates/model/gateways_init.py.jinja2 +1 -0
- scaffold_ca_python/templates/model/test_gateway.py.jinja2 +9 -0
- scaffold_ca_python/templates/model/test_model.py.jinja2 +1 -1
- scaffold_ca_python/templates/use_case/test_use_case.py.jinja2 +1 -1
- {scaffold_ca_python-0.2.1.dist-info → scaffold_ca_python-0.4.0.dist-info}/METADATA +14 -3
- {scaffold_ca_python-0.2.1.dist-info → scaffold_ca_python-0.4.0.dist-info}/RECORD +18 -14
- {scaffold_ca_python-0.2.1.dist-info → scaffold_ca_python-0.4.0.dist-info}/WHEEL +0 -0
- {scaffold_ca_python-0.2.1.dist-info → scaffold_ca_python-0.4.0.dist-info}/entry_points.txt +0 -0
- {scaffold_ca_python-0.2.1.dist-info → scaffold_ca_python-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,12 +16,12 @@ from scaffold_ca_python.commands._shared import (
|
|
|
16
16
|
)
|
|
17
17
|
from scaffold_ca_python.core.module_builder import ModuleBuilder
|
|
18
18
|
from scaffold_ca_python.core.name_utils import validate_module_name
|
|
19
|
-
from scaffold_ca_python.core.project_detector import load_project_context
|
|
19
|
+
from scaffold_ca_python.core.project_detector import load_project_context, resolve_tests_root
|
|
20
20
|
from scaffold_ca_python.factory.simple.model_factory import ModelFactory
|
|
21
21
|
from scaffold_ca_python.models.context import ModuleContext
|
|
22
22
|
from scaffold_ca_python.models.layer import Layer
|
|
23
23
|
|
|
24
|
-
_GM_HELP = "Scaffold a Pydantic v2 domain model and test
|
|
24
|
+
_GM_HELP = "Scaffold a Pydantic v2 domain model, its gateway, and test stubs. Alias 'gm'."
|
|
25
25
|
_GM_EPILOG = "Example:\n\n * scaffold gm --name order\n\n * scaffold generate-model --name order\n\n"
|
|
26
26
|
|
|
27
27
|
|
|
@@ -31,12 +31,24 @@ def _generate_model_impl(name: str, dry_run: bool) -> None:
|
|
|
31
31
|
project_ctx = load_project_context(project_root)
|
|
32
32
|
module_ctx = ModuleContext(name=name, layer=Layer.DOMAIN_MODEL, project=project_ctx)
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
model_root = project_root / "src" / project_ctx.python_package / "domain" / "model"
|
|
35
35
|
ensure_absent(
|
|
36
|
-
|
|
36
|
+
model_root / module_ctx.module_name,
|
|
37
|
+
project_root,
|
|
38
|
+
kind="Directory",
|
|
39
|
+
hint="Choose a different name or remove the existing folder first.",
|
|
40
|
+
)
|
|
41
|
+
ensure_absent(
|
|
42
|
+
model_root / f"{module_ctx.module_name}.py",
|
|
37
43
|
project_root,
|
|
38
44
|
kind="File",
|
|
39
|
-
hint="
|
|
45
|
+
hint="A model with this name already exists in the legacy flat layout. Remove it or choose a different name.",
|
|
46
|
+
)
|
|
47
|
+
ensure_absent(
|
|
48
|
+
resolve_tests_root(project_root) / "domain" / "model" / module_ctx.module_name,
|
|
49
|
+
project_root,
|
|
50
|
+
kind="Directory",
|
|
51
|
+
hint="Remove the leftover tests folder or choose a different name.",
|
|
40
52
|
)
|
|
41
53
|
|
|
42
54
|
builder = ModuleBuilder(project_root=project_root, project_ctx=project_ctx, module_ctx=module_ctx, dry_run=dry_run)
|
|
@@ -70,7 +82,7 @@ def register(app: typer.Typer) -> None:
|
|
|
70
82
|
),
|
|
71
83
|
] = False,
|
|
72
84
|
) -> None:
|
|
73
|
-
"""Scaffold a domain model in domain/model
|
|
85
|
+
"""Scaffold a domain model package with its gateway in domain/model/<name>/."""
|
|
74
86
|
if name is None:
|
|
75
87
|
typer.echo(ctx.get_help())
|
|
76
88
|
raise typer.Exit(0)
|
|
@@ -10,43 +10,53 @@ from scaffold_ca_python.commands._shared import (
|
|
|
10
10
|
build_and_persist,
|
|
11
11
|
console,
|
|
12
12
|
ensure_absent,
|
|
13
|
+
fail,
|
|
13
14
|
print_dry_run_tree,
|
|
14
15
|
require_project_root,
|
|
15
16
|
validate_name_or_exit,
|
|
16
17
|
)
|
|
17
18
|
from scaffold_ca_python.core.module_builder import ModuleBuilder
|
|
18
|
-
from scaffold_ca_python.core.name_utils import to_snake_case, validate_module_name
|
|
19
|
-
from scaffold_ca_python.core.project_detector import load_project_context
|
|
19
|
+
from scaffold_ca_python.core.name_utils import strip_usecase_suffix, to_snake_case, validate_module_name
|
|
20
|
+
from scaffold_ca_python.core.project_detector import load_project_context, resolve_tests_root
|
|
20
21
|
from scaffold_ca_python.factory.simple.use_case_factory import UseCaseFactory
|
|
21
22
|
from scaffold_ca_python.models.context import ModuleContext
|
|
22
23
|
from scaffold_ca_python.models.layer import Layer
|
|
23
24
|
|
|
24
25
|
_GUC_HELP = "Scaffold a use case in domain/usecase/. Alias 'guc'."
|
|
25
26
|
_GUC_EPILOG = (
|
|
26
|
-
"Example:\n\n * scaffold guc --name create_order
|
|
27
|
+
"Example:\n\n * scaffold guc --name create_order (creates domain/usecase/create_order_usecase.py)\n\n"
|
|
28
|
+
" * scaffold generate-use-case --name create_order\n\n"
|
|
27
29
|
)
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def _generate_use_case_impl(name: str, dry_run: bool) -> None:
|
|
31
33
|
validate_name_or_exit(name, validate_module_name)
|
|
34
|
+
base = strip_usecase_suffix(to_snake_case(name))
|
|
35
|
+
if not base:
|
|
36
|
+
fail(
|
|
37
|
+
f"Use case name '{name}' is only the 'usecase' suffix.",
|
|
38
|
+
hint="Use a descriptive name such as 'create_order'.",
|
|
39
|
+
)
|
|
32
40
|
project_root = require_project_root()
|
|
33
41
|
project_ctx = load_project_context(project_root)
|
|
34
|
-
module_ctx = ModuleContext(name=
|
|
42
|
+
module_ctx = ModuleContext(name=base, layer=Layer.DOMAIN_USECASE, project=project_ctx)
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
src_path = project_root / "src" / project_ctx.python_package / "domain" / "usecase" / f"{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
module = f"{base}_usecase"
|
|
45
|
+
src_path = project_root / "src" / project_ctx.python_package / "domain" / "usecase" / f"{module}.py"
|
|
46
|
+
test_path = resolve_tests_root(project_root) / "domain" / "usecase" / f"test_{module}.py"
|
|
47
|
+
for path in (src_path, test_path):
|
|
48
|
+
ensure_absent(
|
|
49
|
+
path,
|
|
50
|
+
project_root,
|
|
51
|
+
kind="File",
|
|
52
|
+
hint="Choose a different name or remove the existing file first.",
|
|
53
|
+
)
|
|
44
54
|
|
|
45
55
|
builder = ModuleBuilder(project_root=project_root, project_ctx=project_ctx, module_ctx=module_ctx, dry_run=dry_run)
|
|
46
56
|
created = build_and_persist(UseCaseFactory(), builder)
|
|
47
57
|
|
|
48
58
|
if dry_run:
|
|
49
|
-
print_dry_run_tree(
|
|
59
|
+
print_dry_run_tree(module, created, project_root)
|
|
50
60
|
return
|
|
51
61
|
|
|
52
62
|
msg = (
|
|
@@ -4,13 +4,15 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
from scaffold_ca_python.core.name_utils import strip_usecase_suffix
|
|
7
8
|
from scaffold_ca_python.core.project_detector import resolve_tests_root
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def find_module_targets(project_root: Path, pkg: str, snake: str) -> list[tuple[Path, Path | None]]:
|
|
11
12
|
"""Return ``(src_path, test_path | None)`` pairs for every match of *snake*.
|
|
12
13
|
|
|
13
|
-
Searches in order: domain/model, domain/
|
|
14
|
+
Searches in order: domain/model/<snake>/ (package), domain/model/<snake>.py (legacy flat),
|
|
15
|
+
domain/usecase (``<base>_usecase.py`` first, then legacy ``<snake>.py``), driven_adapters,
|
|
14
16
|
entry_points, helpers.
|
|
15
17
|
"""
|
|
16
18
|
src_root = project_root / "src" / pkg
|
|
@@ -18,9 +20,30 @@ def find_module_targets(project_root: Path, pkg: str, snake: str) -> list[tuple[
|
|
|
18
20
|
|
|
19
21
|
candidates: list[tuple[Path, Path | None]] = []
|
|
20
22
|
|
|
23
|
+
model_pkg = src_root / "domain" / "model" / snake
|
|
24
|
+
if model_pkg.is_dir():
|
|
25
|
+
model_pkg_test = tests_root / "domain" / "model" / snake
|
|
26
|
+
candidates.append((model_pkg, model_pkg_test if model_pkg_test.exists() else None))
|
|
27
|
+
|
|
28
|
+
usecase_pairs: list[tuple[Path, Path]] = []
|
|
29
|
+
base = strip_usecase_suffix(snake)
|
|
30
|
+
if base:
|
|
31
|
+
usecase_pairs.append(
|
|
32
|
+
(
|
|
33
|
+
src_root / "domain" / "usecase" / f"{base}_usecase.py",
|
|
34
|
+
tests_root / "domain" / "usecase" / f"test_{base}_usecase.py",
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
legacy_usecase = (
|
|
38
|
+
src_root / "domain" / "usecase" / f"{snake}.py",
|
|
39
|
+
tests_root / "domain" / "usecase" / f"test_{snake}.py",
|
|
40
|
+
)
|
|
41
|
+
if legacy_usecase not in usecase_pairs:
|
|
42
|
+
usecase_pairs.append(legacy_usecase)
|
|
43
|
+
|
|
21
44
|
for src_rel, test_rel in [
|
|
22
45
|
(src_root / "domain" / "model" / f"{snake}.py", tests_root / "domain" / "model" / f"test_{snake}.py"),
|
|
23
|
-
|
|
46
|
+
*usecase_pairs,
|
|
24
47
|
]:
|
|
25
48
|
if src_rel.exists():
|
|
26
49
|
candidates.append((src_rel, test_rel if test_rel.exists() else None))
|
|
@@ -9,6 +9,9 @@ NAME_RE = re.compile(r"^[a-z][a-z0-9]*([_-][a-z0-9]+)*$")
|
|
|
9
9
|
# Stricter pattern for module/class-generating commands: snake_case only (no hyphens).
|
|
10
10
|
MODULE_NAME_RE = re.compile(r"^[a-z][a-z0-9]*(_[a-z0-9]+)*$")
|
|
11
11
|
|
|
12
|
+
# Trailing use case suffixes (applied to "_" + name so a bare "usecase" also matches)
|
|
13
|
+
_USECASE_SUFFIX_RE = re.compile(r"(?:_(?:usecase|use_case))+$")
|
|
14
|
+
|
|
12
15
|
# Matches transitions: lowercase→uppercase and letter→digit boundary runs
|
|
13
16
|
_CAMEL_BOUNDARY_RE = re.compile(r"(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])")
|
|
14
17
|
|
|
@@ -50,6 +53,11 @@ def to_snake_case(name: str) -> str:
|
|
|
50
53
|
return s.lower()
|
|
51
54
|
|
|
52
55
|
|
|
56
|
+
def strip_usecase_suffix(name: str) -> str:
|
|
57
|
+
"""Remove every trailing ``_usecase`` / ``_use_case`` suffix from *name* (may return an empty string)."""
|
|
58
|
+
return _USECASE_SUFFIX_RE.sub("", f"_{name}").removeprefix("_")
|
|
59
|
+
|
|
60
|
+
|
|
53
61
|
def to_kebab_case(name: str) -> str:
|
|
54
62
|
"""Convert *name* (PascalCase, snake_case, or already kebab-case) to kebab-case."""
|
|
55
63
|
# Normalise underscores to hyphens first (snake_case support)
|
|
@@ -6,8 +6,8 @@ import shutil
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
|
+
from scaffold_ca_python.core.module_locator import find_module_targets
|
|
9
10
|
from scaffold_ca_python.core.name_utils import to_snake_case
|
|
10
|
-
from scaffold_ca_python.core.project_detector import resolve_tests_root
|
|
11
11
|
from scaffold_ca_python.factory import ModuleFactory
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
@@ -21,8 +21,8 @@ class DeleteModuleFactory(ModuleFactory):
|
|
|
21
21
|
"""Build deletion operations for a module.
|
|
22
22
|
|
|
23
23
|
Finds and deletes modules across:
|
|
24
|
-
- domain/model/<snake>.py
|
|
25
|
-
- domain/usecase/<snake>.py
|
|
24
|
+
- domain/model/<snake>/ (package) or domain/model/<snake>.py (legacy)
|
|
25
|
+
- domain/usecase/<base>_usecase.py or domain/usecase/<snake>.py (legacy)
|
|
26
26
|
- infrastructure/driven_adapters/<snake>/
|
|
27
27
|
- infrastructure/entry_points/<snake>/
|
|
28
28
|
- infrastructure/helpers/<snake>/
|
|
@@ -34,41 +34,7 @@ class DeleteModuleFactory(ModuleFactory):
|
|
|
34
34
|
module_name = builder.module_ctx.name if builder.module_ctx else "Module"
|
|
35
35
|
snake_name = to_snake_case(module_name)
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
tests_root = resolve_tests_root(project_root)
|
|
39
|
-
|
|
40
|
-
# Build list of possible module locations
|
|
41
|
-
candidates: list[tuple[Path, Path | None]] = []
|
|
42
|
-
|
|
43
|
-
# Single-file layers (domain/model, domain/usecase)
|
|
44
|
-
model_src = src_root / "domain" / "model" / f"{snake_name}.py"
|
|
45
|
-
model_test = tests_root / "domain" / "model" / f"test_{snake_name}.py"
|
|
46
|
-
usecase_src = src_root / "domain" / "usecase" / f"{snake_name}.py"
|
|
47
|
-
usecase_test = tests_root / "domain" / "usecase" / f"test_{snake_name}.py"
|
|
48
|
-
for src_rel, test_rel in [
|
|
49
|
-
(model_src, model_test),
|
|
50
|
-
(usecase_src, usecase_test),
|
|
51
|
-
]:
|
|
52
|
-
if src_rel.exists():
|
|
53
|
-
candidates.append((src_rel, test_rel if test_rel.exists() else None))
|
|
54
|
-
|
|
55
|
-
# Directory-based layers (infrastructure sub-dirs)
|
|
56
|
-
for src_rel, test_rel in [
|
|
57
|
-
(
|
|
58
|
-
src_root / "infrastructure" / "driven_adapters" / snake_name,
|
|
59
|
-
tests_root / "infrastructure" / "driven_adapters" / snake_name,
|
|
60
|
-
),
|
|
61
|
-
(
|
|
62
|
-
src_root / "infrastructure" / "entry_points" / snake_name,
|
|
63
|
-
tests_root / "infrastructure" / "entry_points" / snake_name,
|
|
64
|
-
),
|
|
65
|
-
(
|
|
66
|
-
src_root / "infrastructure" / "helpers" / snake_name,
|
|
67
|
-
tests_root / "infrastructure" / "helpers" / snake_name,
|
|
68
|
-
),
|
|
69
|
-
]:
|
|
70
|
-
if src_rel.exists():
|
|
71
|
-
candidates.append((src_rel, test_rel if test_rel.exists() else None))
|
|
37
|
+
candidates = find_module_targets(project_root, pkg, snake_name)
|
|
72
38
|
|
|
73
39
|
if not builder.dry_run:
|
|
74
40
|
for src_path, test_path in candidates:
|
|
@@ -15,43 +15,48 @@ class ModelFactory(ModuleFactory):
|
|
|
15
15
|
"""Factory for domain model generation."""
|
|
16
16
|
|
|
17
17
|
def build(self, builder: ModuleBuilder) -> None:
|
|
18
|
-
"""Build domain model files.
|
|
18
|
+
"""Build domain model package files.
|
|
19
19
|
|
|
20
20
|
Queues files:
|
|
21
|
-
- src/domain/model/{module_name}.py
|
|
22
|
-
-
|
|
21
|
+
- src/domain/model/{module_name}/__init__.py
|
|
22
|
+
- src/domain/model/{module_name}/{module_name}.py
|
|
23
|
+
- src/domain/model/{module_name}/gateways/__init__.py
|
|
24
|
+
- src/domain/model/{module_name}/gateways/{module_name}_repository.py
|
|
25
|
+
- tests/domain/model/{module_name}/test_{module_name}.py
|
|
26
|
+
- tests/domain/model/{module_name}/gateways/test_{module_name}_repository.py
|
|
23
27
|
|
|
24
28
|
No dependencies injected (pydantic is already a project dependency).
|
|
25
29
|
"""
|
|
26
30
|
project_root = builder.project_root
|
|
27
31
|
pkg = builder.project_ctx.python_package
|
|
28
32
|
|
|
29
|
-
# Get module name from module context
|
|
30
33
|
module_name = builder.module_ctx.module_name if builder.module_ctx else "model"
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
test_dir = resolve_tests_root(project_root) / "domain" / "model"
|
|
35
|
+
src_dir = project_root / "src" / pkg / "domain" / "model" / module_name
|
|
36
|
+
gateways_dir = src_dir / "gateways"
|
|
37
|
+
test_dir = resolve_tests_root(project_root) / "domain" / "model" / module_name
|
|
38
|
+
test_gateways_dir = test_dir / "gateways"
|
|
35
39
|
|
|
36
|
-
# Template base path
|
|
37
40
|
base = "model"
|
|
38
41
|
|
|
39
|
-
# Build context dict from module context
|
|
40
42
|
ctx_dict = {}
|
|
41
43
|
if builder.module_ctx:
|
|
42
44
|
ctx_dict.update(builder.module_ctx.model_dump())
|
|
43
45
|
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
src_dir /
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
# (target path, template file name, is_test)
|
|
47
|
+
files = [
|
|
48
|
+
(src_dir / "__init__.py", "__init__.py.jinja2", False),
|
|
49
|
+
(src_dir / f"{module_name}.py", "model.py.jinja2", False),
|
|
50
|
+
(gateways_dir / "__init__.py", "gateways_init.py.jinja2", False),
|
|
51
|
+
(gateways_dir / f"{module_name}_repository.py", "gateway.py.jinja2", False),
|
|
52
|
+
(test_dir / f"test_{module_name}.py", "test_model.py.jinja2", True),
|
|
53
|
+
(test_gateways_dir / f"test_{module_name}_repository.py", "test_gateway.py.jinja2", True),
|
|
54
|
+
]
|
|
55
|
+
for path, template, is_test in files:
|
|
56
|
+
template_name = f"{base}/{template}"
|
|
57
|
+
builder.add_file(
|
|
58
|
+
path,
|
|
59
|
+
builder.render(template_name, ctx_dict),
|
|
60
|
+
template_name=template_name,
|
|
61
|
+
is_test=is_test,
|
|
62
|
+
)
|
|
@@ -19,8 +19,8 @@ class UseCaseFactory(ModuleFactory):
|
|
|
19
19
|
"""Build domain use case files.
|
|
20
20
|
|
|
21
21
|
Queues files:
|
|
22
|
-
- src/domain/usecase/{snake_name}.py
|
|
23
|
-
- tests/domain/usecase/test_{snake_name}.py
|
|
22
|
+
- src/domain/usecase/{snake_name}_usecase.py
|
|
23
|
+
- tests/domain/usecase/test_{snake_name}_usecase.py
|
|
24
24
|
|
|
25
25
|
No dependencies injected (async/await are built-in).
|
|
26
26
|
"""
|
|
@@ -45,14 +45,14 @@ class UseCaseFactory(ModuleFactory):
|
|
|
45
45
|
|
|
46
46
|
# Add source file
|
|
47
47
|
builder.add_file(
|
|
48
|
-
src_dir / f"{snake_name}.py",
|
|
48
|
+
src_dir / f"{snake_name}_usecase.py",
|
|
49
49
|
builder.render(f"{base}/use_case.py.jinja2", ctx_dict),
|
|
50
50
|
template_name=f"{base}/use_case.py.jinja2",
|
|
51
51
|
)
|
|
52
52
|
|
|
53
53
|
# Add test file
|
|
54
54
|
builder.add_file(
|
|
55
|
-
test_dir / f"test_{snake_name}.py",
|
|
55
|
+
test_dir / f"test_{snake_name}_usecase.py",
|
|
56
56
|
builder.render(f"{base}/test_use_case.py.jinja2", ctx_dict),
|
|
57
57
|
template_name=f"{base}/test_use_case.py.jinja2",
|
|
58
58
|
is_test=True,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""{{ class_name }} domain model package."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""{{ class_name }} gateways."""
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Tests for {{ class_name }}Repository gateway."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC
|
|
4
|
+
|
|
5
|
+
from {{ project.python_package }}.domain.model.{{ module_name }}.gateways.{{ module_name }}_repository import {{ class_name }}Repository
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_{{ module_name }}_repository_is_abstract_contract() -> None:
|
|
9
|
+
assert issubclass({{ class_name }}Repository, ABC)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Tests for {{ class_name }} domain model."""
|
|
2
2
|
|
|
3
|
-
from {{ project.python_package }}.domain.model.{{ module_name }} import {{ class_name }}
|
|
3
|
+
from {{ project.python_package }}.domain.model.{{ module_name }}.{{ module_name }} import {{ class_name }}
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def test_{{ module_name }}_can_be_instantiated() -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: scaffold-ca-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Scaffolder to generate Clean Architecture apps
|
|
5
5
|
Project-URL: Homepage, https://github.com/bancolombia/scaffold-clean-architecture-py
|
|
6
6
|
Project-URL: Repository, https://github.com/bancolombia/scaffold-clean-architecture-py
|
|
@@ -105,19 +105,30 @@ scaffold ca --name my-project --dry-run
|
|
|
105
105
|
|
|
106
106
|
### scaffold gm
|
|
107
107
|
|
|
108
|
-
Scaffold a Pydantic v2 domain model and
|
|
108
|
+
Scaffold a Pydantic v2 domain model, its gateway (an empty abstract repository contract), and test stubs.
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
111
|
scaffold gm --name order
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
Generated structure:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
domain/model/order/
|
|
118
|
+
├── __init__.py
|
|
119
|
+
├── order.py
|
|
120
|
+
└── gateways/
|
|
121
|
+
├── __init__.py
|
|
122
|
+
└── order_repository.py
|
|
123
|
+
```
|
|
124
|
+
|
|
114
125
|
```bash
|
|
115
126
|
scaffold gm --name order --dry-run
|
|
116
127
|
```
|
|
117
128
|
|
|
118
129
|
### scaffold guc
|
|
119
130
|
|
|
120
|
-
Scaffold a use case in `domain/usecase/`.
|
|
131
|
+
Scaffold a use case in `domain/usecase/`. The module is always named `<name>_usecase.py` and the class `<Name>UseCase`. Writing `_usecase` in `--name` is optional and the suffix is never duplicated: `create_order`, `create_order_usecase` and `create_order_use_case` all create `create_order_usecase.py` with `CreateOrderUseCase`.
|
|
121
132
|
|
|
122
133
|
```bash
|
|
123
134
|
scaffold guc --name create_order
|
|
@@ -8,18 +8,18 @@ scaffold_ca_python/commands/delete_module.py,sha256=J-nkA_o9mY2AOOzQBg3kyTEWm6el
|
|
|
8
8
|
scaffold_ca_python/commands/generate_driven_adapter.py,sha256=IRGXCinpa694_9W7WXL7k43hOm9V52Jk87DVgayzeEs,4215
|
|
9
9
|
scaffold_ca_python/commands/generate_entry_point.py,sha256=NvHF1auzB0bfcx9KzaHCx3Z_pA0FLswwDB9yW3LdKG8,9449
|
|
10
10
|
scaffold_ca_python/commands/generate_helper.py,sha256=O98ua5Fj3judGxF_vmTYbm_j-qEAxGd4eA0F1YYtPqM,2884
|
|
11
|
-
scaffold_ca_python/commands/generate_model.py,sha256=
|
|
11
|
+
scaffold_ca_python/commands/generate_model.py,sha256=GBHPkoTYrABNdmdILWGLuQFM4ppzYbuHyunjhc-o4Dc,3387
|
|
12
12
|
scaffold_ca_python/commands/generate_pipeline.py,sha256=Ip-rN4nSbwaUbtJ49awAs4LDURsrDTQsZdG5kBCLdyE,3773
|
|
13
13
|
scaffold_ca_python/commands/generate_project.py,sha256=5KRsPI_WiWQVY1d_0AblUHKdDQCWpXJA7MaOCHae0fE,5312
|
|
14
|
-
scaffold_ca_python/commands/generate_use_case.py,sha256=
|
|
14
|
+
scaffold_ca_python/commands/generate_use_case.py,sha256=3uKYZik9eF23Z9QMM2psGNWuW2AhxkB468urum5N8WA,3441
|
|
15
15
|
scaffold_ca_python/commands/update_project.py,sha256=7bu2neRasxEtYK2rIvx0CFISSx9D64BNCkijp90zWGk,2367
|
|
16
16
|
scaffold_ca_python/commands/validate_structure.py,sha256=p_bYTgOl4_TrC80z-v2teSsssc9xghlkCYlUp3SuzHo,2526
|
|
17
17
|
scaffold_ca_python/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
scaffold_ca_python/core/file_writer.py,sha256=KdnNrq33Jsvqs1IQf0jbUKdXls4jBSWAizw4uJhMalU,5561
|
|
19
19
|
scaffold_ca_python/core/mcp_tool_spec.py,sha256=mPLN7vOSgWFWxHQQxmr0fWEaimHWI-9A0oP7ob73-rU,10602
|
|
20
20
|
scaffold_ca_python/core/module_builder.py,sha256=AHC3szEdfS3i3q74IM6USZTHWEUK6QizL6NEKcHCrrI,6837
|
|
21
|
-
scaffold_ca_python/core/module_locator.py,sha256=
|
|
22
|
-
scaffold_ca_python/core/name_utils.py,sha256=
|
|
21
|
+
scaffold_ca_python/core/module_locator.py,sha256=FFAwA7Vajxg0JgHoVXc1HpvdB67clvkv_BFVwiQDxHA,2552
|
|
22
|
+
scaffold_ca_python/core/name_utils.py,sha256=IpXAieBKFe7QbOY4K8gNsjr4NXUfJX5MBCy2bQyPIk4,2958
|
|
23
23
|
scaffold_ca_python/core/openapi_routes.py,sha256=RCQS7mHufWJZgbLI1cxGli-xGbU7p7WI95fHJhDnIss,1098
|
|
24
24
|
scaffold_ca_python/core/project_detector.py,sha256=vcDAFCGgQVDMQ2MivJVUmn5Yyj58vGpTbzKhZTBQNtw,3524
|
|
25
25
|
scaffold_ca_python/core/pyproject_writer.py,sha256=zpxgubt-XSZR5domgL4LG_9AWD9b_cNrDM8fuHmZRzE,7792
|
|
@@ -40,10 +40,10 @@ scaffold_ca_python/factory/entry_points/ep_restapi.py,sha256=PFkt9TH1aN6YO8w4zAe
|
|
|
40
40
|
scaffold_ca_python/factory/pipelines/__init__.py,sha256=0YPpPGsZ6A3-udHTSZDyUHleLFLQQh1V5IL6AB0qJ6g,557
|
|
41
41
|
scaffold_ca_python/factory/pipelines/pipeline_factory.py,sha256=s4fpuyhdxXiwko-LlM6Pv-QJdRGQJb17KVrC-DW6ojo,842
|
|
42
42
|
scaffold_ca_python/factory/simple/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
-
scaffold_ca_python/factory/simple/delete_module_factory.py,sha256=
|
|
43
|
+
scaffold_ca_python/factory/simple/delete_module_factory.py,sha256=v_Kl-m7bxvCwUsdQuCPq66uPksuLFLYvbsTNdE4Cs8Q,1696
|
|
44
44
|
scaffold_ca_python/factory/simple/helper_factory.py,sha256=nBVOu_kGBVrEGnXFa918GPo-UFlcW-Y5-Uk6bwYWuhQ,2295
|
|
45
|
-
scaffold_ca_python/factory/simple/model_factory.py,sha256=
|
|
46
|
-
scaffold_ca_python/factory/simple/use_case_factory.py,sha256=
|
|
45
|
+
scaffold_ca_python/factory/simple/model_factory.py,sha256=YdUpBvGgUrz4zzXMG2p8d6PWit62B9esjLZG49dT5PU,2467
|
|
46
|
+
scaffold_ca_python/factory/simple/use_case_factory.py,sha256=CUY2SkneFWv-KukK-Cdn2KoLZwgYukNa25Hv15SQYx8,1986
|
|
47
47
|
scaffold_ca_python/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
48
|
scaffold_ca_python/models/context.py,sha256=Itp-YyPgY-0yNYA9YlCXrmPfa5nEVklGAOJQGHfBdHE,1909
|
|
49
49
|
scaffold_ca_python/models/file_operation.py,sha256=R5Tu3KBvUnSmH5QZXBQTPoQjvr4YdH1YOKsKS9ICH_Q,1698
|
|
@@ -94,8 +94,12 @@ scaffold_ca_python/templates/entry_point/restapi/test_server.py.jinja2,sha256=7_
|
|
|
94
94
|
scaffold_ca_python/templates/helper/__init__.py.jinja2,sha256=LA4hikmh30a_8LgZ8h18zdC3NEfDPNJeZOSBJ4RZBwU,39
|
|
95
95
|
scaffold_ca_python/templates/helper/helper.py.jinja2,sha256=rPaSM5Y4U25Ht8nshnw9m_fc3FXkx-CHA_egJM7QDmc,110
|
|
96
96
|
scaffold_ca_python/templates/helper/test_helper.py.jinja2,sha256=5nmkjej1FQngXh5JAoftdRvE8kdad0Knjxf25Lb--RI,268
|
|
97
|
+
scaffold_ca_python/templates/model/__init__.py.jinja2,sha256=fz54Iv6Z8sDtCMgIz8eFBXokdDz0SA2uhQMf8IGYUcs,45
|
|
98
|
+
scaffold_ca_python/templates/model/gateway.py.jinja2,sha256=o2FNIfFseauf_5REavVnM-Qe7k8rjoXRqSuqtOI0vIk,184
|
|
99
|
+
scaffold_ca_python/templates/model/gateways_init.py.jinja2,sha256=8bO2y7wFlMVRMURwxLD9ubAHuu71YXCRhGtfSjkWtcM,33
|
|
97
100
|
scaffold_ca_python/templates/model/model.py.jinja2,sha256=pvzy78xH5-qWJ6IQb-9bY-dnqPj4FPBsQ-Kalh_mFr0,151
|
|
98
|
-
scaffold_ca_python/templates/model/
|
|
101
|
+
scaffold_ca_python/templates/model/test_gateway.py.jinja2,sha256=xedkiihpUCARhGSKEPLmAlbJ0yOxS2uvDwpIdLbmSFg,338
|
|
102
|
+
scaffold_ca_python/templates/model/test_model.py.jinja2,sha256=Wdz84c8Yt6aeUFQIINdfsq0K9Ihk7GT_ZVzpEGrQ4uM,281
|
|
99
103
|
scaffold_ca_python/templates/pipeline/azure/azure_pipelines.yml.jinja2,sha256=ecbvubtMhoyqxiM69PunOYwOOBBLUFqSBsoqJsIqyDI,555
|
|
100
104
|
scaffold_ca_python/templates/pipeline/github/ci.yml.jinja2,sha256=y20dpIoOHlZNCIXzyJdjsTgq2HiyCjHpcg2UPKZuQRg,672
|
|
101
105
|
scaffold_ca_python/templates/project/README.jinja2,sha256=HazR7Ls3utOfMFjxDsVS1-WMCPG1OR4XR2ftn02Fnuo,696
|
|
@@ -115,10 +119,10 @@ scaffold_ca_python/templates/project/application/config/container.py.jinja2,sha2
|
|
|
115
119
|
scaffold_ca_python/templates/project/application/config/driven_adapters_container.py.jinja2,sha256=WhRpupvLP8tOJgp4IJ0A5RitntfRPVFjCDUjhAhBt8U,464
|
|
116
120
|
scaffold_ca_python/templates/project/application/config/resource_container.py.jinja2,sha256=k3wy8uSD7GJZNEf379fv2lMEzdq5eaNqmoaOw9Jq1vs,643
|
|
117
121
|
scaffold_ca_python/templates/project/application/config/usecases_container.py.jinja2,sha256=3fEHe6mkXkDHlfO5nhLqzLB7ccTpqwtjHZfLyfysbro,527
|
|
118
|
-
scaffold_ca_python/templates/use_case/test_use_case.py.jinja2,sha256=
|
|
122
|
+
scaffold_ca_python/templates/use_case/test_use_case.py.jinja2,sha256=spomnRHeXWiWjy3QHQt839yIMFDUqA7h-Wq4KfZxAyg,372
|
|
119
123
|
scaffold_ca_python/templates/use_case/use_case.py.jinja2,sha256=KPtaK1wvZ-7qHdDZyuVfqXTPYwJdNUMPXQZGuKZFA7Y,217
|
|
120
|
-
scaffold_ca_python-0.
|
|
121
|
-
scaffold_ca_python-0.
|
|
122
|
-
scaffold_ca_python-0.
|
|
123
|
-
scaffold_ca_python-0.
|
|
124
|
-
scaffold_ca_python-0.
|
|
124
|
+
scaffold_ca_python-0.4.0.dist-info/METADATA,sha256=7LQgO7e31QQ8vmmP1PfqwXgaWnQRrLIIpengbcw8mf4,7475
|
|
125
|
+
scaffold_ca_python-0.4.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
126
|
+
scaffold_ca_python-0.4.0.dist-info/entry_points.txt,sha256=4PyYg6mNCvAlt2yF_o1-QR2CBXiowYGpNyqcP88tu6I,104
|
|
127
|
+
scaffold_ca_python-0.4.0.dist-info/licenses/LICENSE,sha256=3ZZEP7DK_h3lEjS8r-DvQCec2q7zAqqq7HmSq6cuL0E,1068
|
|
128
|
+
scaffold_ca_python-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|