Coverage for src/lexigram/web/cli/generators/controller.py: 39%

28 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 04:37 +0800

1"""Controller generator.""" 

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6 

7from lexigram.codegen import GenerationResult, GeneratorBase, parse_fields 

8 

9 

10class ControllerGenerator(GeneratorBase): 

11 """Generate a controller class with CRUD endpoints.""" 

12 

13 def __init__(self, output_dir: str | Path = "src/controllers") -> None: 

14 super().__init__(output_dir=output_dir) 

15 

16 def generate( 

17 self, 

18 name: str, 

19 *, 

20 fields_str: str | None = None, 

21 path: str | None = None, 

22 doc: str | None = None, 

23 dry_run: bool = False, 

24 force: bool = False, 

25 **options: object, 

26 ) -> GenerationResult: 

27 name = self._strip_type_suffix(name, "Controller") 

28 model_name = self._to_snake_case(name) 

29 resource_name = self._pluralize(model_name) 

30 api_path = path or f"/{resource_name}" 

31 fields = parse_fields(fields_str or "") 

32 file_path = self.output_dir / f"{model_name}_controller.py" 

33 content = self.render_template( 

34 "controller.py.jinja2", 

35 { 

36 "class_name": self._to_pascal_case(name), 

37 "model_name": model_name, 

38 "resource_name": resource_name, 

39 "resource_path": api_path.strip("/"), 

40 "doc": doc, 

41 "required_fields": [field.name for field in fields if field.required], 

42 "fields": [ 

43 { 

44 "name": field.name, 

45 "type": field.type, 

46 "required": field.required, 

47 } 

48 for field in fields 

49 ], 

50 }, 

51 ) 

52 return self.write_file(file_path, content, dry_run=dry_run, force=force) 

53 

54 @staticmethod 

55 def _strip_type_suffix(name: str, suffix: str) -> str: 

56 if name.endswith(suffix) and len(name) > len(suffix): 

57 return name[: -len(suffix)] 

58 return name 

59 

60 @staticmethod 

61 def _pluralize(value: str) -> str: 

62 if value.endswith("y") and value[-2:-1] not in {"a", "e", "i", "o", "u"}: 

63 return f"{value[:-1]}ies" 

64 if value.endswith("s"): 

65 return value 

66 return f"{value}s" 

67 

68 

69__all__ = ["ControllerGenerator"]