rooibos 0.5.0 → 0.6.0
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.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +9 -5
- data/.builds/ruby-3.3.yml +9 -5
- data/.builds/ruby-3.4.yml +9 -5
- data/.builds/ruby-4.0.0.yml +9 -5
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +46 -0
- data/README.md +2 -2
- data/README.rdoc +374 -0
- data/REUSE.toml +5 -0
- data/Rakefile +1 -1
- data/doc/best_practices/forms_and_validation.md +20 -0
- data/doc/best_practices/http_workflows.md +20 -0
- data/doc/best_practices/index.md +26 -0
- data/doc/best_practices/lists_and_tables.md +20 -0
- data/doc/best_practices/modal_dialogs.md +20 -0
- data/doc/best_practices/no_stateful_widgets.md +184 -0
- data/doc/best_practices/orchestration.md +20 -0
- data/doc/best_practices/streaming_data.md +20 -0
- data/doc/contributors/design/commands_and_outlets.md +1 -1
- data/doc/contributors/documentation_plan.md +616 -0
- data/doc/contributors/documentation_stub_audit.md +112 -0
- data/doc/contributors/documentation_style.md +275 -0
- data/doc/contributors/e2e_pty.md +168 -0
- data/doc/contributors/specs/earliest_tutorial_steps_per_story.md +70 -0
- data/doc/contributors/specs/file_browser.md +789 -0
- data/doc/contributors/specs/file_browser_stories.md +774 -0
- data/doc/contributors/specs/tutorials_to_stories.rb +167 -0
- data/doc/contributors/todo/scrollbar.md +118 -0
- data/doc/contributors/tutorial_old/01_project_setup.md +20 -0
- data/doc/contributors/tutorial_old/02_hello_world.md +24 -0
- data/doc/contributors/tutorial_old/03_adding_state.md +26 -0
- data/doc/contributors/tutorial_old/06_organizing_your_code.md +20 -0
- data/doc/contributors/tutorial_old/07_your_first_command.md +21 -0
- data/doc/contributors/tutorial_old/08_the_preview_pane.md +20 -0
- data/doc/contributors/tutorial_old/09_loading_states.md +20 -0
- data/doc/contributors/tutorial_old/10_testing_your_app.md +20 -0
- data/doc/contributors/tutorial_old/11_polish_and_refine.md +20 -0
- data/doc/contributors/tutorial_old/12_going_further.md +20 -0
- data/doc/contributors/tutorial_old/index.md +20 -0
- data/doc/essentials/commands.md +20 -0
- data/doc/essentials/index.md +31 -0
- data/doc/essentials/messages.md +21 -0
- data/doc/essentials/models.md +21 -0
- data/doc/essentials/shortcuts.md +19 -0
- data/doc/essentials/the_elm_architecture.md +24 -0
- data/doc/essentials/the_runtime.md +21 -0
- data/doc/essentials/update_functions.md +20 -0
- data/doc/essentials/views.md +22 -0
- data/doc/getting_started/for_go_developers.md +16 -0
- data/doc/getting_started/for_python_developers.md +16 -0
- data/doc/getting_started/for_react_developers.md +17 -0
- data/doc/getting_started/index.md +52 -0
- data/doc/getting_started/install.md +20 -0
- data/doc/getting_started/quickstart.md +9 -45
- data/doc/getting_started/ruby_primer.md +19 -0
- data/doc/getting_started/why_rooibos.md +20 -0
- data/doc/index.md +79 -11
- data/doc/scaling_up/async_patterns.md +20 -0
- data/doc/scaling_up/command_composition.md +20 -0
- data/doc/scaling_up/custom_commands.md +21 -0
- data/doc/scaling_up/fractal_architecture.md +20 -0
- data/doc/scaling_up/index.md +30 -0
- data/doc/scaling_up/message_routing.md +20 -0
- data/doc/scaling_up/ractor_safety.md +20 -0
- data/doc/scaling_up/testing.md +21 -0
- data/doc/troubleshooting/common_errors.md +20 -0
- data/doc/troubleshooting/debugging.md +21 -0
- data/doc/troubleshooting/index.md +23 -0
- data/doc/troubleshooting/performance.md +20 -0
- data/doc/tutorial/01_project_setup.md +44 -0
- data/doc/tutorial/02_hello_world.md +45 -0
- data/doc/tutorial/03_static_file_list.md +44 -0
- data/doc/tutorial/04_arrow_navigation.md +47 -0
- data/doc/tutorial/05_real_files.md +45 -0
- data/doc/tutorial/06_safe_refactoring.md +21 -0
- data/doc/tutorial/07_red_first_tdd.md +26 -0
- data/doc/tutorial/08_file_metadata.md +42 -0
- data/doc/tutorial/09_text_preview.md +44 -0
- data/doc/tutorial/10_directory_tree.md +42 -0
- data/doc/tutorial/11_pane_focus.md +40 -0
- data/doc/tutorial/12_sorting.md +41 -0
- data/doc/tutorial/13_filtering.md +43 -0
- data/doc/tutorial/14_toggle_hidden.md +41 -0
- data/doc/tutorial/15_text_input_widget.md +43 -0
- data/doc/tutorial/16_rename_files.md +42 -0
- data/doc/tutorial/17_confirmation_dialogs.md +43 -0
- data/doc/tutorial/18_progress_indicators.md +43 -0
- data/doc/tutorial/19_atomic_operations.md +42 -0
- data/doc/tutorial/20_external_editor.md +42 -0
- data/doc/tutorial/21_modal_overlays.md +41 -0
- data/doc/tutorial/22_error_handling.md +43 -0
- data/doc/tutorial/23_terminal_capabilities.md +53 -0
- data/doc/tutorial/24_mouse_events.md +43 -0
- data/doc/tutorial/25_resize_events.md +43 -0
- data/doc/tutorial/26_loading_states.md +42 -0
- data/doc/tutorial/27_performance.md +43 -0
- data/doc/tutorial/28_color_schemes.md +47 -0
- data/doc/tutorial/29_configuration.md +124 -0
- data/doc/tutorial/30_going_further.md +17 -0
- data/doc/tutorial/index.md +17 -0
- data/examples/app_file_browser/app.rb +40 -0
- data/examples/app_fractal_dashboard/dashboard/update_manual.rb +7 -7
- data/examples/app_fractal_dashboard/fragments/custom_shell_input.rb +5 -5
- data/examples/app_fractal_dashboard/fragments/custom_shell_modal.rb +1 -1
- data/examples/app_fractal_dashboard/fragments/disk_usage.rb +2 -2
- data/examples/app_fractal_dashboard/fragments/network_panel.rb +4 -4
- data/examples/app_fractal_dashboard/fragments/ping.rb +2 -2
- data/examples/app_fractal_dashboard/fragments/stats_panel.rb +4 -4
- data/examples/app_fractal_dashboard/fragments/system_info.rb +2 -2
- data/examples/app_fractal_dashboard/fragments/uptime.rb +2 -2
- data/examples/verify_website_first_app/app.rb +85 -0
- data/examples/verify_website_hello_mvu/app.rb +31 -0
- data/examples/widget_command_system/app.rb +15 -13
- data/exe/rooibos +10 -0
- data/generate_tutorial_stubs.rb +126 -0
- data/lib/rooibos/cli/commands/new.rb +373 -0
- data/lib/rooibos/cli/commands/run.rb +98 -0
- data/lib/rooibos/cli.rb +78 -0
- data/lib/rooibos/command/all.rb +25 -20
- data/lib/rooibos/command/batch.rb +26 -25
- data/lib/rooibos/command/custom.rb +84 -1
- data/lib/rooibos/command/http.rb +59 -55
- data/lib/rooibos/command/lifecycle.rb +5 -5
- data/lib/rooibos/command/open.rb +86 -0
- data/lib/rooibos/command/outlet.rb +105 -3
- data/lib/rooibos/command/wait.rb +5 -5
- data/lib/rooibos/command.rb +57 -74
- data/lib/rooibos/message/batch.rb +39 -0
- data/lib/rooibos/message/canceled.rb +51 -0
- data/lib/rooibos/message/error.rb +48 -0
- data/lib/rooibos/message/open.rb +30 -0
- data/lib/rooibos/message.rb +84 -4
- data/lib/rooibos/router.rb +11 -14
- data/lib/rooibos/runtime.rb +40 -43
- data/lib/rooibos/shortcuts.rb +47 -0
- data/lib/rooibos/test_helper.rb +71 -6
- data/lib/rooibos/version.rb +1 -1
- data/lib/rooibos/welcome.rb +237 -0
- data/lib/rooibos.rb +4 -3
- data/mise.toml +1 -1
- data/rbs_collection.lock.yaml +2 -2
- data/sig/concurrent.rbs +3 -0
- data/sig/gem.rbs +20 -0
- data/sig/rooibos/cli.rbs +42 -0
- data/sig/rooibos/command.rbs +48 -0
- data/sig/rooibos/message.rbs +60 -0
- data/sig/rooibos/shortcuts.rbs +14 -0
- data/sig/rooibos/test_helper.rbs +6 -2
- data/sig/rooibos/welcome.rbs +75 -0
- data/tasks/install.rake +29 -0
- data/tasks/resources/build.yml.erb +2 -0
- metadata +272 -38
- data/doc/concepts/application_architecture.md +0 -197
- data/doc/concepts/application_testing.md +0 -49
- data/doc/concepts/async_work.md +0 -164
- data/doc/concepts/commands.md +0 -530
- data/doc/concepts/message_processing.md +0 -51
- data/doc/contributors/WIP/decomposition_strategies_analysis.md +0 -258
- data/doc/contributors/WIP/implementation_plan.md +0 -409
- data/doc/contributors/WIP/init_callable_proposal.md +0 -344
- data/doc/contributors/WIP/runtime_refactoring_status.md +0 -47
- data/doc/contributors/WIP/task.md +0 -36
- data/doc/contributors/WIP/v0.4.0_todo.md +0 -468
- data/doc/contributors/kit-no-outlet.md +0 -238
- data/doc/contributors/priorities.md +0 -38
- data/doc/images/.gitkeep +0 -0
- data/exe/.gitkeep +0 -0
- /data/doc/contributors/{WIP → design}/mvu_tea_implementations_research.md +0 -0
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
|
|
4
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
5
|
-
-->
|
|
6
|
-
|
|
7
|
-
# Init Callable Implementation Plan
|
|
8
|
-
|
|
9
|
-
## Problem Statement
|
|
10
|
-
|
|
11
|
-
The current codebase uses **three different names** for initial state:
|
|
12
|
-
- `MODEL` (simple examples: README, verify_readme_usage)
|
|
13
|
-
- `INITIAL` (fractal fragments: dashboard examples)
|
|
14
|
-
- `init:` (runtime parameter with different semantics)
|
|
15
|
-
|
|
16
|
-
The planned v0.4.0 transition (`INITIAL` → `Initial` PascalCase) would create visual collision with `Model`, where both look like type names despite different purposes (type vs instance).
|
|
17
|
-
|
|
18
|
-
**Critical Finding**: Research of 15+ MVU/TEA frameworks shows **zero** use static constants for initialization. All use callables/functions.
|
|
19
|
-
|
|
20
|
-
## Solution: Init Callable
|
|
21
|
-
|
|
22
|
-
Replace static `INITIAL`/`MODEL` constants with `Init` callable that:
|
|
23
|
-
1. Returns `[model, command]` tuple (or DWIM variants)
|
|
24
|
-
2. Accepts flags/props for parameterization
|
|
25
|
-
3. Enables parent-to-child initialization data flow
|
|
26
|
-
4. Supports initial command dispatch alongside state
|
|
27
|
-
|
|
28
|
-
## Proposed Changes
|
|
29
|
-
|
|
30
|
-
### Core API
|
|
31
|
-
|
|
32
|
-
#### Fragment Structure (v0.4.0)
|
|
33
|
-
```ruby
|
|
34
|
-
module MyFragment
|
|
35
|
-
Model = Data.define(:field1, :field2)
|
|
36
|
-
|
|
37
|
-
# NEW: Init callable (optional, defaults to Model.new)
|
|
38
|
-
Init = ->(flags_hash = {}) do
|
|
39
|
-
model = Model.new(...)
|
|
40
|
-
command = some_initial_command # or nil
|
|
41
|
-
[model, command]
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
Update = ->(message, model) { ... }
|
|
45
|
-
View = ->(model, tui) { ... }
|
|
46
|
-
end
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
#### DWIM Return Values
|
|
50
|
-
```ruby
|
|
51
|
-
# All valid return formats:
|
|
52
|
-
Init = -> { Model.new(...) } # Just model
|
|
53
|
-
Init = -> { [Model.new(...), nil] } # Explicit tuple, no command
|
|
54
|
-
Init = -> { [Model.new(...), some_cmd] } # With command
|
|
55
|
-
Init = -> { Command.exit } # Just command (edge case)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
#### Normalization Helper
|
|
59
|
-
```ruby
|
|
60
|
-
module Rooibos
|
|
61
|
-
# Normalize Init return to [model, command] tuple
|
|
62
|
-
# Uses DWIM logic like Update (rooibos_command? detection)
|
|
63
|
-
def self.normalize_init(result)
|
|
64
|
-
case result
|
|
65
|
-
when Command then [nil, result] # Just command
|
|
66
|
-
in [model, command] then [model, command] # Already tuple
|
|
67
|
-
else
|
|
68
|
-
if result.respond_to?(:rooibos_command?) && result.rooibos_command?
|
|
69
|
-
[nil, result] # Command without array wrapper
|
|
70
|
-
else
|
|
71
|
-
[result, nil] # Just model
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
### Fractal Composition
|
|
81
|
-
|
|
82
|
-
#### Parent Init Calls Child Init
|
|
83
|
-
|
|
84
|
-
```ruby
|
|
85
|
-
|
|
86
|
-
module Dashboard
|
|
87
|
-
Model = Data.define(:stats, :network, :theme)
|
|
88
|
-
|
|
89
|
-
Init = ->(theme: :dark, env: {}) do
|
|
90
|
-
# Call child Inits with props
|
|
91
|
-
stats_model, stats_cmd = StatsPanel::Init.(theme: theme)
|
|
92
|
-
network_model, network_cmd = NetworkPanel::Init.(theme: theme)
|
|
93
|
-
|
|
94
|
-
model = Model.new(
|
|
95
|
-
stats: stats_model,
|
|
96
|
-
network: network_model,
|
|
97
|
-
theme: theme
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
command = Command.batch(
|
|
101
|
-
Rooibos.route(stats_cmd, :stats),
|
|
102
|
-
Rooibos.route(network_cmd, :network)
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
[model, command]
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# View composition - UNCHANGED (still manual)
|
|
109
|
-
View = ->(model, tui) do
|
|
110
|
-
tui.vertical_layout(
|
|
111
|
-
children: [
|
|
112
|
-
StatsPanel::View.call(model.stats, tui),
|
|
113
|
-
NetworkPanel::View.call(model.network, tui)
|
|
114
|
-
]
|
|
115
|
-
)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Update routing - UNCHANGED (Router DSL)
|
|
119
|
-
include Rooibos::Router
|
|
120
|
-
route :stats, to: StatsPanel
|
|
121
|
-
route :network, to: NetworkPanel
|
|
122
|
-
Update = from_router
|
|
123
|
-
end
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**Key Point**: Only `Update` uses Router DSL. `Init` and `View` remain explicit/manual composition.
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
### Runtime Integration
|
|
131
|
-
|
|
132
|
-
#### Current API (Backward Compatible)
|
|
133
|
-
|
|
134
|
-
```ruby
|
|
135
|
-
# Still works (old style)
|
|
136
|
-
Rooibos.run(
|
|
137
|
-
model: MyApp::INITIAL,
|
|
138
|
-
view: MyApp::VIEW,
|
|
139
|
-
update: MyApp::UPDATE
|
|
140
|
-
)
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
#### New API (Fragment-First)
|
|
144
|
-
|
|
145
|
-
```ruby
|
|
146
|
-
# New style (preferred)
|
|
147
|
-
Rooibos.run(
|
|
148
|
-
fragment: MyApp,
|
|
149
|
-
argv: ARGV,
|
|
150
|
-
env: ENV
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
# Equivalent to:
|
|
154
|
-
# model, init_cmd = MyApp::Init.(argv: ARGV, env: ENV)
|
|
155
|
-
# Rooibos.run(model: model, view: MyApp::View, update: MyApp::Update, init: init_cmd)
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
#### Runtime Implementation
|
|
159
|
-
```ruby
|
|
160
|
-
module Rooibos
|
|
161
|
-
def self.run(fragment: nil, model: nil, view: nil, update: nil, argv: [], env: {}, init: nil)
|
|
162
|
-
if fragment
|
|
163
|
-
# New style: fragment-first
|
|
164
|
-
init_callable = fragment.const_defined?(:Init) ? fragment::Init : -> { fragment::Model.new }
|
|
165
|
-
init_result = init_callable.call(argv: argv, env: env)
|
|
166
|
-
model, init_cmd = normalize_init(init_result)
|
|
167
|
-
view = fragment::View
|
|
168
|
-
update = fragment::Update
|
|
169
|
-
init = init_cmd # Override init param with Init-generated command
|
|
170
|
-
else
|
|
171
|
-
# Old style: explicit parameters (backward compatible)
|
|
172
|
-
# model, view, update, init already provided
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
Runtime.run(model: model, view: view, update: update, init: init)
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## Migration Strategy
|
|
183
|
-
|
|
184
|
-
### v0.4.0: Breaking Change (Zero Users, Zero Cruft)
|
|
185
|
-
|
|
186
|
-
Since the project has **zero external users**, we will make this a **breaking change in v0.4.0**:
|
|
187
|
-
|
|
188
|
-
- ✅ `Init` callable is the **only** way to define initial state
|
|
189
|
-
- ❌ `INITIAL` and `MODEL` constants are **removed** (no backward compatibility)
|
|
190
|
-
- ✅ `Init` is **optional** (defaults to `-> { Model.new }` if not defined)
|
|
191
|
-
- ✅ All examples and documentation use `Init` pattern
|
|
192
|
-
- ✅ Clean slate, no migration debt
|
|
193
|
-
|
|
194
|
-
**Rationale**: Adding backward compatibility for a non-existent user base creates unnecessary code complexity and testing burden. Break now while we can.
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Implementation Checklist
|
|
199
|
-
|
|
200
|
-
### Core Implementation
|
|
201
|
-
- [ ] **`Rooibos.normalize_init` helper**
|
|
202
|
-
- [ ] Implement DWIM logic with `rooibos_command?` detection
|
|
203
|
-
- [ ] Handle all return formats: model, command, `[model, cmd]`
|
|
204
|
-
- [ ] Tests for all DWIM variants
|
|
205
|
-
|
|
206
|
-
- [ ] **Runtime changes**
|
|
207
|
-
- [ ] Add `fragment:`, `argv:`, `env:` parameters to `Rooibos.run`
|
|
208
|
-
- [ ] Auto-call `fragment::Init` when `fragment:` provided
|
|
209
|
-
- [ ] Default `Init` to `-> { Model.new }` if not defined
|
|
210
|
-
- [ ] Maintain backward compatibility with `model:`/`view:`/`update:` params
|
|
211
|
-
- [ ] Tests for both old and new API styles
|
|
212
|
-
|
|
213
|
-
### Example Migrations
|
|
214
|
-
- [ ] **Simple example** (verify_readme_usage)
|
|
215
|
-
- [ ] Replace `MODEL` constant with `Init` callable
|
|
216
|
-
- [ ] Update README code samples
|
|
217
|
-
- [ ] Verify tests still pass
|
|
218
|
-
|
|
219
|
-
- [ ] **Fractal example** (app_fractal_dashboard)
|
|
220
|
-
- [ ] Convert all fragment `INITIAL` to `Init`
|
|
221
|
-
- [ ] Update parent Init to call child Inits with props
|
|
222
|
-
- [ ] Compose initial commands with `Command.batch`
|
|
223
|
-
- [ ] Verify all dashboard variants (base, router, manual, helpers) work
|
|
224
|
-
|
|
225
|
-
### Documentation
|
|
226
|
-
- [ ] **Update AGENTS.md**
|
|
227
|
-
- [ ] Change Fragment definition to use `Init` instead of `INITIAL`
|
|
228
|
-
- [ ] Update terminology section
|
|
229
|
-
|
|
230
|
-
- [ ] **Create concept documentation files**
|
|
231
|
-
- [ ] `doc/init.md` (full documentation)
|
|
232
|
-
- [ ] Follow documentation style guide (Alexandrian Context-Problem-Solution)
|
|
233
|
-
- [ ] Explain what Init is and why it exists (vs static constants)
|
|
234
|
-
- [ ] Document Init at root level (runtime integration)
|
|
235
|
-
- [ ] Document Init at non-root level (fractal composition)
|
|
236
|
-
- [ ] Show parameterization with flags/props
|
|
237
|
-
- [ ] Document DWIM return values
|
|
238
|
-
- [ ] Show Rooibos.normalize_init usage
|
|
239
|
-
- [ ] Multiple complete examples (simple, fractal, with commands)
|
|
240
|
-
- [ ] Link to related concepts (Fragment, Model, Update, Command)
|
|
241
|
-
- [ ] Create stubs for concept doc series (H1 + "TODO" only):
|
|
242
|
-
- [ ] `doc/fragment.md`
|
|
243
|
-
- [ ] `doc/model.md`
|
|
244
|
-
- [ ] `doc/view.md`
|
|
245
|
-
- [ ] `doc/update.md`
|
|
246
|
-
- [ ] `doc/command.md`
|
|
247
|
-
- [ ] `doc/message.md`
|
|
248
|
-
- [ ] `doc/output.md`
|
|
249
|
-
|
|
250
|
-
- [ ] **Update design_for_v0.4.0.md**
|
|
251
|
-
- [ ] Add Init Callable section
|
|
252
|
-
- [ ] Document DWIM behavior
|
|
253
|
-
- [ ] Document `Rooibos.normalize_init` helper
|
|
254
|
-
- [ ] Add footnote on Iced pattern inspiration
|
|
255
|
-
- [ ] Add footnote on OutMsg pattern (future consideration)
|
|
256
|
-
|
|
257
|
-
- [ ] **Create migration guide**
|
|
258
|
-
- [ ] Before/after examples
|
|
259
|
-
- [ ] Deprecation timeline
|
|
260
|
-
- [ ] Common patterns
|
|
261
|
-
|
|
262
|
-
- [ ] **Update RDoc**
|
|
263
|
-
- [ ] Document `Rooibos.normalize_init`
|
|
264
|
-
- [ ] Document new `Rooibos.run` signature
|
|
265
|
-
- [ ] Update Fragment examples throughout
|
|
266
|
-
|
|
267
|
-
### RBS Type Signatures
|
|
268
|
-
- [ ] **Update Rooibos module RBS**
|
|
269
|
-
```ruby
|
|
270
|
-
module Rooibos
|
|
271
|
-
def self.run: (
|
|
272
|
-
?fragment: Module,
|
|
273
|
-
?model: untyped,
|
|
274
|
-
?view: ^(untyped, untyped) -> Widget,
|
|
275
|
-
?update: ^(untyped, untyped) -> untyped,
|
|
276
|
-
?init: ^() -> untyped,
|
|
277
|
-
?argv: Array[String],
|
|
278
|
-
?env: Hash[String, String]
|
|
279
|
-
) -> void
|
|
280
|
-
|
|
281
|
-
def self.normalize_init: (untyped result) -> [untyped, Command?]
|
|
282
|
-
end
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### Testing
|
|
286
|
-
- [ ] **Unit tests for `Rooibos.normalize_init`**
|
|
287
|
-
- [ ] Returns `[model, nil]` for model-only
|
|
288
|
-
- [ ] Returns `[nil, cmd]` for command-only
|
|
289
|
-
- [ ] Returns `[model, cmd]` for tuple
|
|
290
|
-
- [ ] Handles `rooibos_command?` detection
|
|
291
|
-
|
|
292
|
-
- [ ] **Integration tests for `Rooibos.run`**
|
|
293
|
-
- [ ] Fragment-first API works
|
|
294
|
-
- [ ] Old API still works (backward compat)
|
|
295
|
-
- [ ] Init commands are dispatched
|
|
296
|
-
- [ ] ARGV/ENV passed to Init
|
|
297
|
-
- [ ] Default Init works when not defined
|
|
298
|
-
|
|
299
|
-
- [ ] **Fractal composition tests**
|
|
300
|
-
- [ ] Parent Init calls child Inits
|
|
301
|
-
- [ ] Props passed to children
|
|
302
|
-
- [ ] Commands batched correctly
|
|
303
|
-
- [ ] Router still routes messages correctly
|
|
304
|
-
|
|
305
|
-
### CHANGELOG
|
|
306
|
-
- [ ] Add to `[UNRELEASED]` section:
|
|
307
|
-
```markdown
|
|
308
|
-
### Breaking Changes
|
|
309
|
-
- **REMOVED**: `INITIAL` and `MODEL` constants
|
|
310
|
-
- Use `Init` callable instead: `Init = -> { [Model.new(...), nil] }`
|
|
311
|
-
- `Init` is optional (defaults to `-> { Model.new }`)
|
|
312
|
-
|
|
313
|
-
### Added
|
|
314
|
-
- **Init Callable Pattern**: Fragments support `Init` callable for parameterized initialization
|
|
315
|
-
- Returns `[model, command]` tuple (DWIM supported)
|
|
316
|
-
- Accepts flags/props for parent-to-child data flow
|
|
317
|
-
- `Rooibos.normalize_init` helper for composing child Inits
|
|
318
|
-
- **Fragment-First Runtime API**: `Rooibos.run(fragment: MyApp, argv: ARGV, env: ENV)`
|
|
319
|
-
- **Concept Documentation**: Created `doc/init.md` and series stubs
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
|
-
## Breaking Changes
|
|
325
|
-
|
|
326
|
-
**v0.4.0 is a BREAKING RELEASE**:
|
|
327
|
-
|
|
328
|
-
- ❌ **REMOVED**: `INITIAL` and `MODEL` constants (use `Init` callable instead)
|
|
329
|
-
- ✅ **NEW**: `Init` callable is the only supported initialization pattern
|
|
330
|
-
- ✅ **NEW**: Runtime `fragment:` parameter for fragment-first API
|
|
331
|
-
|
|
332
|
-
**Rationale**: Zero external users means zero migration burden. Ship clean architecture without legacy cruft.
|
|
333
|
-
|
|
334
|
-
---
|
|
335
|
-
|
|
336
|
-
## Open Questions
|
|
337
|
-
|
|
338
|
-
None - all design decisions finalized with user.
|
|
339
|
-
|
|
340
|
-
---
|
|
341
|
-
|
|
342
|
-
## Design Notes (Contributor Reference)
|
|
343
|
-
|
|
344
|
-
### Concept Documentation Series
|
|
345
|
-
|
|
346
|
-
`doc/init.md` is the **first** in a series of concept documentation pages, one for each core TEA concept:
|
|
347
|
-
- `doc/init.md` (this implementation)
|
|
348
|
-
- `doc/fragment.md` (future)
|
|
349
|
-
- `doc/model.md` (future)
|
|
350
|
-
- `doc/view.md` (future)
|
|
351
|
-
- `doc/update.md` (future)
|
|
352
|
-
- `doc/command.md` (future)
|
|
353
|
-
- `doc/message.md` (future)
|
|
354
|
-
- `doc/output.md` (future)
|
|
355
|
-
|
|
356
|
-
Each follows the Alexandrian Context-Problem-Solution pattern per `doc/contributors/documentation_style.md`.
|
|
357
|
-
|
|
358
|
-
### Why Not Auto-Init in Router DSL?
|
|
359
|
-
|
|
360
|
-
The Router DSL is **message-only** by design. It handles `Update` function routing, not initialization or view composition:
|
|
361
|
-
|
|
362
|
-
```ruby
|
|
363
|
-
route :stats, to: StatsPanel # Only routes messages to StatsPanel::Update
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
Both `Init` and `View` remain **explicit composition** (manual), which provides:
|
|
367
|
-
- ✅ Full control over initialization order and props
|
|
368
|
-
- ✅ Clear, explicit data flow
|
|
369
|
-
- ✅ Flexibility for conditional initialization
|
|
370
|
-
- ✅ Consistency with View composition pattern
|
|
371
|
-
|
|
372
|
-
### Terminology Inspirations
|
|
373
|
-
|
|
374
|
-
This pattern draws from multiple proven MVU implementations:
|
|
375
|
-
- **Elm**: `init : () -> (Model, Cmd Msg)` tuple return
|
|
376
|
-
- **Iced (Rust)**: `fn new(flags: Flags) -> (State, Command)` parameterized init
|
|
377
|
-
- **Bubble Tea (Go)**: `func (m Model) Init() tea.Cmd` method-based init
|
|
378
|
-
- **Hyperapp (JS)**: `init: [state, ...effects]` array-based DWIM
|
|
379
|
-
|
|
380
|
-
See `doc/contributors/mvu_research_notes.md` for detailed analysis.
|
|
381
|
-
|
|
382
|
-
### Future: OutMsg Pattern
|
|
383
|
-
|
|
384
|
-
Elm's "Translator Pattern" / "OutMsg Pattern" allows children to emit events to parents beyond state updates. Not needed for v0.4.0, but may be considered for future:
|
|
385
|
-
|
|
386
|
-
```ruby
|
|
387
|
-
# Potential future enhancement
|
|
388
|
-
Update = ->(msg, model) do
|
|
389
|
-
case msg
|
|
390
|
-
in [:data_loaded, data]
|
|
391
|
-
[[model.with(data: data), nil], [:parent_event_here]]
|
|
392
|
-
# ^^^^^^^^^^^^^^^^^^^^ OutMsg for parent
|
|
393
|
-
end
|
|
394
|
-
end
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
See `doc/contributors/outmsg_pattern_notes.md` for details.
|
|
398
|
-
|
|
399
|
-
---
|
|
400
|
-
|
|
401
|
-
## Success Criteria
|
|
402
|
-
|
|
403
|
-
- [ ] All existing tests pass
|
|
404
|
-
- [ ] `bundle exec agent_rake` passes (0 warnings)
|
|
405
|
-
- [ ] Both old and new API styles work
|
|
406
|
-
- [ ] Fractal dashboard example uses Init pattern
|
|
407
|
-
- [ ] Simple example uses Init pattern
|
|
408
|
-
- [ ] Documentation is comprehensive and clear
|
|
409
|
-
- [ ] Migration path is documented
|