scaffold-ca-python 0.3.0__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_use_case.py +23 -13
- scaffold_ca_python/core/module_locator.py +19 -2
- scaffold_ca_python/core/name_utils.py +8 -0
- scaffold_ca_python/factory/simple/delete_module_factory.py +1 -1
- scaffold_ca_python/factory/simple/use_case_factory.py +4 -4
- scaffold_ca_python/templates/use_case/test_use_case.py.jinja2 +1 -1
- {scaffold_ca_python-0.3.0.dist-info → scaffold_ca_python-0.4.0.dist-info}/METADATA +2 -2
- {scaffold_ca_python-0.3.0.dist-info → scaffold_ca_python-0.4.0.dist-info}/RECORD +11 -11
- {scaffold_ca_python-0.3.0.dist-info → scaffold_ca_python-0.4.0.dist-info}/WHEEL +0 -0
- {scaffold_ca_python-0.3.0.dist-info → scaffold_ca_python-0.4.0.dist-info}/entry_points.txt +0 -0
- {scaffold_ca_python-0.3.0.dist-info → scaffold_ca_python-0.4.0.dist-info}/licenses/LICENSE +0 -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,6 +4,7 @@ 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
|
|
|
@@ -11,7 +12,7 @@ def find_module_targets(project_root: Path, pkg: str, snake: str) -> list[tuple[
|
|
|
11
12
|
"""Return ``(src_path, test_path | None)`` pairs for every match of *snake*.
|
|
12
13
|
|
|
13
14
|
Searches in order: domain/model/<snake>/ (package), domain/model/<snake>.py (legacy flat),
|
|
14
|
-
domain/usecase, driven_adapters,
|
|
15
|
+
domain/usecase (``<base>_usecase.py`` first, then legacy ``<snake>.py``), driven_adapters,
|
|
15
16
|
entry_points, helpers.
|
|
16
17
|
"""
|
|
17
18
|
src_root = project_root / "src" / pkg
|
|
@@ -24,9 +25,25 @@ def find_module_targets(project_root: Path, pkg: str, snake: str) -> list[tuple[
|
|
|
24
25
|
model_pkg_test = tests_root / "domain" / "model" / snake
|
|
25
26
|
candidates.append((model_pkg, model_pkg_test if model_pkg_test.exists() else None))
|
|
26
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
|
+
|
|
27
44
|
for src_rel, test_rel in [
|
|
28
45
|
(src_root / "domain" / "model" / f"{snake}.py", tests_root / "domain" / "model" / f"test_{snake}.py"),
|
|
29
|
-
|
|
46
|
+
*usecase_pairs,
|
|
30
47
|
]:
|
|
31
48
|
if src_rel.exists():
|
|
32
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)
|
|
@@ -22,7 +22,7 @@ class DeleteModuleFactory(ModuleFactory):
|
|
|
22
22
|
|
|
23
23
|
Finds and deletes modules across:
|
|
24
24
|
- domain/model/<snake>/ (package) or domain/model/<snake>.py (legacy)
|
|
25
|
-
- domain/usecase/<snake>.py
|
|
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>/
|
|
@@ -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,
|
|
@@ -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
|
|
@@ -128,7 +128,7 @@ scaffold gm --name order --dry-run
|
|
|
128
128
|
|
|
129
129
|
### scaffold guc
|
|
130
130
|
|
|
131
|
-
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`.
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
134
|
scaffold guc --name create_order
|
|
@@ -11,15 +11,15 @@ scaffold_ca_python/commands/generate_helper.py,sha256=O98ua5Fj3judGxF_vmTYbm_j-q
|
|
|
11
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
45
|
scaffold_ca_python/factory/simple/model_factory.py,sha256=YdUpBvGgUrz4zzXMG2p8d6PWit62B9esjLZG49dT5PU,2467
|
|
46
|
-
scaffold_ca_python/factory/simple/use_case_factory.py,sha256=
|
|
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
|
|
@@ -119,10 +119,10 @@ scaffold_ca_python/templates/project/application/config/container.py.jinja2,sha2
|
|
|
119
119
|
scaffold_ca_python/templates/project/application/config/driven_adapters_container.py.jinja2,sha256=WhRpupvLP8tOJgp4IJ0A5RitntfRPVFjCDUjhAhBt8U,464
|
|
120
120
|
scaffold_ca_python/templates/project/application/config/resource_container.py.jinja2,sha256=k3wy8uSD7GJZNEf379fv2lMEzdq5eaNqmoaOw9Jq1vs,643
|
|
121
121
|
scaffold_ca_python/templates/project/application/config/usecases_container.py.jinja2,sha256=3fEHe6mkXkDHlfO5nhLqzLB7ccTpqwtjHZfLyfysbro,527
|
|
122
|
-
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
|
|
123
123
|
scaffold_ca_python/templates/use_case/use_case.py.jinja2,sha256=KPtaK1wvZ-7qHdDZyuVfqXTPYwJdNUMPXQZGuKZFA7Y,217
|
|
124
|
-
scaffold_ca_python-0.
|
|
125
|
-
scaffold_ca_python-0.
|
|
126
|
-
scaffold_ca_python-0.
|
|
127
|
-
scaffold_ca_python-0.
|
|
128
|
-
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
|