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.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +9 -5
  3. data/.builds/ruby-3.3.yml +9 -5
  4. data/.builds/ruby-3.4.yml +9 -5
  5. data/.builds/ruby-4.0.0.yml +9 -5
  6. data/AGENTS.md +1 -1
  7. data/CHANGELOG.md +46 -0
  8. data/README.md +2 -2
  9. data/README.rdoc +374 -0
  10. data/REUSE.toml +5 -0
  11. data/Rakefile +1 -1
  12. data/doc/best_practices/forms_and_validation.md +20 -0
  13. data/doc/best_practices/http_workflows.md +20 -0
  14. data/doc/best_practices/index.md +26 -0
  15. data/doc/best_practices/lists_and_tables.md +20 -0
  16. data/doc/best_practices/modal_dialogs.md +20 -0
  17. data/doc/best_practices/no_stateful_widgets.md +184 -0
  18. data/doc/best_practices/orchestration.md +20 -0
  19. data/doc/best_practices/streaming_data.md +20 -0
  20. data/doc/contributors/design/commands_and_outlets.md +1 -1
  21. data/doc/contributors/documentation_plan.md +616 -0
  22. data/doc/contributors/documentation_stub_audit.md +112 -0
  23. data/doc/contributors/documentation_style.md +275 -0
  24. data/doc/contributors/e2e_pty.md +168 -0
  25. data/doc/contributors/specs/earliest_tutorial_steps_per_story.md +70 -0
  26. data/doc/contributors/specs/file_browser.md +789 -0
  27. data/doc/contributors/specs/file_browser_stories.md +774 -0
  28. data/doc/contributors/specs/tutorials_to_stories.rb +167 -0
  29. data/doc/contributors/todo/scrollbar.md +118 -0
  30. data/doc/contributors/tutorial_old/01_project_setup.md +20 -0
  31. data/doc/contributors/tutorial_old/02_hello_world.md +24 -0
  32. data/doc/contributors/tutorial_old/03_adding_state.md +26 -0
  33. data/doc/contributors/tutorial_old/06_organizing_your_code.md +20 -0
  34. data/doc/contributors/tutorial_old/07_your_first_command.md +21 -0
  35. data/doc/contributors/tutorial_old/08_the_preview_pane.md +20 -0
  36. data/doc/contributors/tutorial_old/09_loading_states.md +20 -0
  37. data/doc/contributors/tutorial_old/10_testing_your_app.md +20 -0
  38. data/doc/contributors/tutorial_old/11_polish_and_refine.md +20 -0
  39. data/doc/contributors/tutorial_old/12_going_further.md +20 -0
  40. data/doc/contributors/tutorial_old/index.md +20 -0
  41. data/doc/essentials/commands.md +20 -0
  42. data/doc/essentials/index.md +31 -0
  43. data/doc/essentials/messages.md +21 -0
  44. data/doc/essentials/models.md +21 -0
  45. data/doc/essentials/shortcuts.md +19 -0
  46. data/doc/essentials/the_elm_architecture.md +24 -0
  47. data/doc/essentials/the_runtime.md +21 -0
  48. data/doc/essentials/update_functions.md +20 -0
  49. data/doc/essentials/views.md +22 -0
  50. data/doc/getting_started/for_go_developers.md +16 -0
  51. data/doc/getting_started/for_python_developers.md +16 -0
  52. data/doc/getting_started/for_react_developers.md +17 -0
  53. data/doc/getting_started/index.md +52 -0
  54. data/doc/getting_started/install.md +20 -0
  55. data/doc/getting_started/quickstart.md +9 -45
  56. data/doc/getting_started/ruby_primer.md +19 -0
  57. data/doc/getting_started/why_rooibos.md +20 -0
  58. data/doc/index.md +79 -11
  59. data/doc/scaling_up/async_patterns.md +20 -0
  60. data/doc/scaling_up/command_composition.md +20 -0
  61. data/doc/scaling_up/custom_commands.md +21 -0
  62. data/doc/scaling_up/fractal_architecture.md +20 -0
  63. data/doc/scaling_up/index.md +30 -0
  64. data/doc/scaling_up/message_routing.md +20 -0
  65. data/doc/scaling_up/ractor_safety.md +20 -0
  66. data/doc/scaling_up/testing.md +21 -0
  67. data/doc/troubleshooting/common_errors.md +20 -0
  68. data/doc/troubleshooting/debugging.md +21 -0
  69. data/doc/troubleshooting/index.md +23 -0
  70. data/doc/troubleshooting/performance.md +20 -0
  71. data/doc/tutorial/01_project_setup.md +44 -0
  72. data/doc/tutorial/02_hello_world.md +45 -0
  73. data/doc/tutorial/03_static_file_list.md +44 -0
  74. data/doc/tutorial/04_arrow_navigation.md +47 -0
  75. data/doc/tutorial/05_real_files.md +45 -0
  76. data/doc/tutorial/06_safe_refactoring.md +21 -0
  77. data/doc/tutorial/07_red_first_tdd.md +26 -0
  78. data/doc/tutorial/08_file_metadata.md +42 -0
  79. data/doc/tutorial/09_text_preview.md +44 -0
  80. data/doc/tutorial/10_directory_tree.md +42 -0
  81. data/doc/tutorial/11_pane_focus.md +40 -0
  82. data/doc/tutorial/12_sorting.md +41 -0
  83. data/doc/tutorial/13_filtering.md +43 -0
  84. data/doc/tutorial/14_toggle_hidden.md +41 -0
  85. data/doc/tutorial/15_text_input_widget.md +43 -0
  86. data/doc/tutorial/16_rename_files.md +42 -0
  87. data/doc/tutorial/17_confirmation_dialogs.md +43 -0
  88. data/doc/tutorial/18_progress_indicators.md +43 -0
  89. data/doc/tutorial/19_atomic_operations.md +42 -0
  90. data/doc/tutorial/20_external_editor.md +42 -0
  91. data/doc/tutorial/21_modal_overlays.md +41 -0
  92. data/doc/tutorial/22_error_handling.md +43 -0
  93. data/doc/tutorial/23_terminal_capabilities.md +53 -0
  94. data/doc/tutorial/24_mouse_events.md +43 -0
  95. data/doc/tutorial/25_resize_events.md +43 -0
  96. data/doc/tutorial/26_loading_states.md +42 -0
  97. data/doc/tutorial/27_performance.md +43 -0
  98. data/doc/tutorial/28_color_schemes.md +47 -0
  99. data/doc/tutorial/29_configuration.md +124 -0
  100. data/doc/tutorial/30_going_further.md +17 -0
  101. data/doc/tutorial/index.md +17 -0
  102. data/examples/app_file_browser/app.rb +40 -0
  103. data/examples/app_fractal_dashboard/dashboard/update_manual.rb +7 -7
  104. data/examples/app_fractal_dashboard/fragments/custom_shell_input.rb +5 -5
  105. data/examples/app_fractal_dashboard/fragments/custom_shell_modal.rb +1 -1
  106. data/examples/app_fractal_dashboard/fragments/disk_usage.rb +2 -2
  107. data/examples/app_fractal_dashboard/fragments/network_panel.rb +4 -4
  108. data/examples/app_fractal_dashboard/fragments/ping.rb +2 -2
  109. data/examples/app_fractal_dashboard/fragments/stats_panel.rb +4 -4
  110. data/examples/app_fractal_dashboard/fragments/system_info.rb +2 -2
  111. data/examples/app_fractal_dashboard/fragments/uptime.rb +2 -2
  112. data/examples/verify_website_first_app/app.rb +85 -0
  113. data/examples/verify_website_hello_mvu/app.rb +31 -0
  114. data/examples/widget_command_system/app.rb +15 -13
  115. data/exe/rooibos +10 -0
  116. data/generate_tutorial_stubs.rb +126 -0
  117. data/lib/rooibos/cli/commands/new.rb +373 -0
  118. data/lib/rooibos/cli/commands/run.rb +98 -0
  119. data/lib/rooibos/cli.rb +78 -0
  120. data/lib/rooibos/command/all.rb +25 -20
  121. data/lib/rooibos/command/batch.rb +26 -25
  122. data/lib/rooibos/command/custom.rb +84 -1
  123. data/lib/rooibos/command/http.rb +59 -55
  124. data/lib/rooibos/command/lifecycle.rb +5 -5
  125. data/lib/rooibos/command/open.rb +86 -0
  126. data/lib/rooibos/command/outlet.rb +105 -3
  127. data/lib/rooibos/command/wait.rb +5 -5
  128. data/lib/rooibos/command.rb +57 -74
  129. data/lib/rooibos/message/batch.rb +39 -0
  130. data/lib/rooibos/message/canceled.rb +51 -0
  131. data/lib/rooibos/message/error.rb +48 -0
  132. data/lib/rooibos/message/open.rb +30 -0
  133. data/lib/rooibos/message.rb +84 -4
  134. data/lib/rooibos/router.rb +11 -14
  135. data/lib/rooibos/runtime.rb +40 -43
  136. data/lib/rooibos/shortcuts.rb +47 -0
  137. data/lib/rooibos/test_helper.rb +71 -6
  138. data/lib/rooibos/version.rb +1 -1
  139. data/lib/rooibos/welcome.rb +237 -0
  140. data/lib/rooibos.rb +4 -3
  141. data/mise.toml +1 -1
  142. data/rbs_collection.lock.yaml +2 -2
  143. data/sig/concurrent.rbs +3 -0
  144. data/sig/gem.rbs +20 -0
  145. data/sig/rooibos/cli.rbs +42 -0
  146. data/sig/rooibos/command.rbs +48 -0
  147. data/sig/rooibos/message.rbs +60 -0
  148. data/sig/rooibos/shortcuts.rbs +14 -0
  149. data/sig/rooibos/test_helper.rbs +6 -2
  150. data/sig/rooibos/welcome.rbs +75 -0
  151. data/tasks/install.rake +29 -0
  152. data/tasks/resources/build.yml.erb +2 -0
  153. metadata +272 -38
  154. data/doc/concepts/application_architecture.md +0 -197
  155. data/doc/concepts/application_testing.md +0 -49
  156. data/doc/concepts/async_work.md +0 -164
  157. data/doc/concepts/commands.md +0 -530
  158. data/doc/concepts/message_processing.md +0 -51
  159. data/doc/contributors/WIP/decomposition_strategies_analysis.md +0 -258
  160. data/doc/contributors/WIP/implementation_plan.md +0 -409
  161. data/doc/contributors/WIP/init_callable_proposal.md +0 -344
  162. data/doc/contributors/WIP/runtime_refactoring_status.md +0 -47
  163. data/doc/contributors/WIP/task.md +0 -36
  164. data/doc/contributors/WIP/v0.4.0_todo.md +0 -468
  165. data/doc/contributors/kit-no-outlet.md +0 -238
  166. data/doc/contributors/priorities.md +0 -38
  167. data/doc/images/.gitkeep +0 -0
  168. data/exe/.gitkeep +0 -0
  169. /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