Prohlížeč zdrojového kódu
docs/specs/examples-runner.md
# Spec: Examples Runner
- **Status:** active
- **Created:** 2026-03-05
- **Related code:** `app/services/scenario_runner.rb`, `app/controllers/examples_controller.rb`, `app/views/examples/`
## Overview
Visitors can interactively run code examples from the detail page. Each example may define one or more scenarios, each with a set of dropdown inputs. The user selects parameter values and submits; the example code executes server-side and the result appears inline without a full page reload.
## Behaviour
### Scenarios and inputs
- An example may define zero or more named scenarios.
- Each scenario has a name, an optional description, and zero or more inputs.
- Each input has a label and a fixed list of allowed options; only dropdown selection is possible — no free-text entry. The input's variable name is shown alongside the label so visitors can see which local variable the selected value is bound to.
- Each scenario is displayed as a separate card on the example detail page, with its own form and result area.
### Submitting a scenario
- The user selects a value for each input from the dropdown and clicks "Spustit".
- The form submits via Turbo Frame; only the result area for that scenario updates — the rest of the page is unchanged.
- The server validates that the selected value for each input is one of the allowed options. Any invalid or missing value results in a `422 Unprocessable Entity` response.
### Boilerplate code display
- Each scenario card shows its `boilerplate_file` content as a syntax-highlighted, read-only code block above the input form.
### Execution
- If the example has a `source_file`, it is loaded into an anonymous module first; the scenario's `boilerplate_file` snippet is then evaluated inside the same module context.
- If there is no `source_file`, the boilerplate snippet is evaluated in a fresh anonymous module — it must be fully self-contained.
- Input values are pre-bound as local variables matching each input's `name` field before the boilerplate snippet is evaluated.
- Output is captured by overriding `puts` within the execution context; the snippet uses `puts` like ordinary Ruby. Captured output becomes the displayed result string.
- Execution is capped at a 5-second timeout. If exceeded, a Czech error message is shown.
- Any Ruby exception during execution is caught; a Czech-language error string including the exception class and message is shown.
- On success, the captured output and execution duration (in seconds, 4 decimal places) are displayed.
### Result display
- Success: output text in a styled box, followed by a completion time.
- Failure (timeout or exception): error message in a styled error box.
## Implementation Notes
- `ScenarioRunner` loads `source_file` into an anonymous `Module` via `module_eval`, so example classes do not leak into the global namespace.
- Input locals are prepended to the snippet as literal assignment statements (`name = "Alice"\n...`) before `module_eval`, keeping the snippet source readable without metaprogramming.
- `$stdout` is replaced with a `StringIO` instance during snippet evaluation and restored in an `ensure` block; `puts` in the snippet writes to the captured buffer naturally.
- Execution uses `Timeout.timeout(5)`; `Timeout::Error` and all other exceptions are rescued separately.
- `ScenarioRunner.run` takes `source_file:` (may be `nil`), `boilerplate_file:`, and `inputs:` — no `entry_class` or `entry_method`. When `source_file` is `nil`, the module_eval of the source file is skipped.
- The Turbo Frame tag for each scenario result is `scenario_result_<index>`; the form targets the same frame so only that area re-renders.
- Input validation in the controller checks both presence and membership in `input.options` before calling `ScenarioRunner`.
## Tests
- `spec/services/scenario_runner_spec.rb` — puts output capture, local variable binding, exception handling, timeout, module isolation
## See Also
- [Examples System](examples.md) — defines the example structure, metadata, and registry the runner operates on.
- [Rich Example Content](example-content.md) — scenario forms are embedded via placeholder tags in the example's Markdown content.
- [Styling and CSS](styling.md) — `.result-box` styles the execution output area; `.code-block` styles inline code display.
## Change Log
- 2026-03-05: Initial retrospective spec.
- 2026-03-09: Prospective update — boilerplate snippet model; removed entry_class/entry_method; added boilerplate_file per scenario, local-variable input binding, puts-capture output.
- 2026-03-23: Prospective update — `source_file` is optional; runner skips source loading when nil.