diff options
Diffstat (limited to 'docs/PROJECT_FLOW.md')
| -rw-r--r-- | docs/PROJECT_FLOW.md | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/docs/PROJECT_FLOW.md b/docs/PROJECT_FLOW.md new file mode 100644 index 0000000..f8f0146 --- /dev/null +++ b/docs/PROJECT_FLOW.md @@ -0,0 +1,133 @@ +# RCM Project Flow + +This diagram shows how a configuration run moves from the entry point through parsing, registration, dependency resolution, and finally execution. + +## High-Level Flow + +```mermaid +flowchart TD + subgraph Entry["Entry Point"] + A[configure / configure_from_scratch] --> B{reset?} + B -->|yes| C[DSL.reset!] + B -->|no| D[DSL.new] + end + + subgraph Init["Bootstrap"] + D --> E[Options.parse!] + E --> F[Config.load!] + F --> G[DSL instance_eval &block] + end + + subgraph Register["Registration"] + G --> H[file / package / touch / ...] + H --> I{conds_met?} + I -->|yes| J[Keyword.new] + I -->|no| Z1[skip] + J --> K[register obj in @@objs] + K --> L{is Resource?} + L -->|yes| M[add to @scheduled] + L -->|no| Z2[done] + end + + subgraph Eval["Evaluation"] + N[evaluate!] --> O[foreach scheduled] + O --> P[Resource.evaluate!] + P --> Q{already evaluated?} + Q -->|yes| Z3[skip] + Q -->|no| R[loop detection check] + R --> S[resolve dependencies] + S --> T[run action / dry-run?] + T --> U[mark evaluated] + end + + G -.-> N +``` + +## Class & Mixin Relationships + +```mermaid +classDiagram + class Keyword { + +id + +dsl + +id_for(name) + } + + class Resource { + +evaluated + +subclass_names + +find(id) + +evaluate!() + } + + class DSL { + +conds_met + +register(obj) + +object!(klass, name) + +evaluate!() + +register_keyword(...) + } + + class Config { + +load!() + +config(key) + } + + class Options { + +parse!() + +option(key) + } + + class Log { + +info(msg) + +debug(msg) + +warn(msg) + } + + class Chained { + +method_missing() + } + + class DryRun { + +do?(message) + } + + class ResourceDependencies { + +requires(*others) + +requires?(*others) + } + + class DependencyEvaluator { + +evaluate!() + } + + Keyword <|-- Resource + DSL --> Keyword : registers + DSL --> Resource : schedules + + Keyword ..> Log : includes + Keyword ..> Options : includes + DSL ..> Config : includes + DSL ..> Options : includes + DSL ..> Log : includes + DSL ..> Chained : includes + + Resource ..> DryRun : includes + Resource ..> ResourceDependencies : includes + Resource ..> DependencyEvaluator : includes +``` + +## What Happens During a Run + +1. **Entry** — `configure(reset: true)` or `configure(reset: false)` is called. +2. **Bootstrap** — `Options.parse!` reads CLI flags (`--debug`, `--dry`, `--hosts`) and `Config.load!` reads `config.toml`. +3. **DSL block** — The user’s configuration block is executed inside the DSL instance via `instance_eval`. +4. **Keyword creation** — Each keyword (`file`, `given`, `notify`, etc.) instantiates a `Keyword` or `Resource` subclass. Names are normalised via `Keyword.id_for`. +5. **Registration** — The generic `register(obj)` stores the object in the class-level `@@objs` hash keyed by `id`. If the object is a `Resource`, it is also appended to `@scheduled`. +6. **Conditionals** — `given { ... }` sets `@conds_met`. When false, subsequent keyword calls are skipped (no object created). +7. **Evaluation** — After the block finishes, `DSL#evaluate!` iterates `@scheduled`. Each `Resource#evaluate!`: + - Checks for dependency loops. + - Recursively evaluates its `requires` dependencies. + - Executes the concrete action (file write, package install, etc.) unless `--dry` is active. + - Marks itself as evaluated. +8. **Dry-run** — The `DryRun#do?` mixin wraps every side-effecting action. In dry mode it logs and returns without touching the system. |
