Skip to content

use_cases: add generic CRUD base classes and build-time generator#122

Merged
absoludity merged 5 commits into
masterfrom
91-generated-CRUD-1
Mar 25, 2026
Merged

use_cases: add generic CRUD base classes and build-time generator#122
absoludity merged 5 commits into
masterfrom
91-generated-CRUD-1

Conversation

@absoludity
Copy link
Copy Markdown
Collaborator

@absoludity absoludity commented Mar 24, 2026

Implements the generator infrastructure described in ADR 008 (revised in #121), with two small changes:

generic_crud.py provides four base classes — GetUseCase, ListUseCase, CreateUseCase, UpdateUseCase — plus EntityNotFoundError. Each base class holds the repository and implements the core logic (_get_by_id, _list_all, _create, _update_by_id). Generated subclasses are thin wrappers that declare typed Request/Response and call through.

Change 1: I've added a total_count to the response of the ListUseCase as it seemed generic enough and a downstream project was having to override the class to add it, which seemed superfluous.
Change 2: Not related to the generator infrastructure, but rather updating a doctrine test - I'll comment inline.

generate_crud.py is a CLI script that takes entity/repo metadata and emits a crud_{entity_snake}.py file into a specified .generated/ directory. Uses f-string templates and ruff format. Downstream projects add a make generate-crud target that invokes it; julee itself has no CRUD use cases to generate.

Also fixes the remaining pytest calls in the Makefile that were missing uv run (same issue as the black/ruff fix on the previous branch).

Closes #91 (partially — the generate step wiring is in downstream uses)

@absoludity absoludity requested a review from monkeypants March 24, 2026 04:18
Comment on lines +200 to +206
def _imported_class_names(directory: Path) -> set[str]:
"""Return names imported into any non-private file in directory.

Scans import statements (not class definitions) so that re-exported
Request/Response classes satisfy doctrine checks even when they are
defined outside the use_cases directory (e.g. in _generated/).
"""
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change to the doctrine test was required as, once we have the generated classes, the actual call-sites were no longer needing to define the class but rather are just importing the generated one. With this change, the doctrine is satisfied if it's imported.

@absoludity absoludity changed the base branch from 91-generated-CRUD-0 to master March 25, 2026 23:21
The introspector previously only detected Request/Response classes via
AST class definitions. With build-time code generation, these classes
may be defined in _generated/ and imported (with or without aliasing)
into the use_cases files that re-export them.

Augment _parse_bounded_context_cached to also scan import statements in
use_cases files, collecting locally-bound names ending with Request or
Response that are not already found as class definitions. UseCase imports
are intentionally excluded — they appear as injected dependencies, not
as definitions, so they must remain class-definition-only.
total_count is always len(entities) and is a near-universal convention
in REST list responses. Including it in the generated base removes the
need for hand-rolled subclasses solely to add this field.
@absoludity absoludity force-pushed the 91-generated-CRUD-1 branch from 8c1a0dd to 1ed5f91 Compare March 25, 2026 23:22
@absoludity absoludity merged commit 09d6cec into master Mar 25, 2026
@absoludity absoludity deleted the 91-generated-CRUD-1 branch March 25, 2026 23:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

008 - Generic CRUD UseCase Generators should further consider code generation scripts

1 participant