rooibos 0.6.2 → 0.7.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/LICENSES/BSD-2-Clause.txt +9 -0
- data/REUSE.toml +5 -0
- data/exe/.gitkeep +0 -0
- data/lib/rooibos/cli/commands/new.rb +24 -0
- data/lib/rooibos/command/batch.rb +10 -0
- data/lib/rooibos/command/bubble.rb +34 -0
- data/lib/rooibos/command/custom.rb +3 -2
- data/lib/rooibos/command/deliver.rb +50 -0
- data/lib/rooibos/command/http.rb +1 -1
- data/lib/rooibos/command/lifecycle.rb +3 -1
- data/lib/rooibos/command/outlet.rb +19 -9
- data/lib/rooibos/command.rb +107 -3
- data/lib/rooibos/configuration.rb +29 -0
- data/lib/rooibos/message/bubbled.rb +29 -0
- data/lib/rooibos/message.rb +24 -6
- data/lib/rooibos/router/action.rb +36 -0
- data/lib/rooibos/router/flow/dispatch.rb +39 -0
- data/lib/rooibos/router/flow/inward.rb +41 -0
- data/lib/rooibos/router/flow/outward.rb +44 -0
- data/lib/rooibos/router/guard.rb +56 -0
- data/lib/rooibos/router/predicate.rb +65 -0
- data/lib/rooibos/router/registry/actions.rb +41 -0
- data/lib/rooibos/router/registry/forwards.rb +58 -0
- data/lib/rooibos/router/registry/observes.rb +57 -0
- data/lib/rooibos/router/registry/otherwises.rb +29 -0
- data/lib/rooibos/router/registry/receives.rb +57 -0
- data/lib/rooibos/router/registry/routes.rb +59 -0
- data/lib/rooibos/router/registry.rb +26 -0
- data/lib/rooibos/router/route.rb +42 -0
- data/lib/rooibos/router/router_update.rb +53 -0
- data/lib/rooibos/router/rule/forward.rb +39 -0
- data/lib/rooibos/router/rule/observe.rb +22 -0
- data/lib/rooibos/router/rule/otherwise.rb +26 -0
- data/lib/rooibos/router/rule/receive.rb +22 -0
- data/lib/rooibos/router/rule.rb +40 -0
- data/lib/rooibos/router.rb +424 -438
- data/lib/rooibos/runtime.rb +37 -52
- data/lib/rooibos/test_helper.rb +22 -0
- data/lib/rooibos/transition.rb +92 -0
- data/lib/rooibos/version.rb +1 -1
- data/lib/rooibos.rb +2 -57
- data/sig/rooibos/cli.rbs +1 -0
- data/sig/rooibos/command.rbs +44 -0
- data/sig/rooibos/configuration.rbs +20 -0
- data/sig/rooibos/message.rbs +12 -0
- data/sig/rooibos/router/action.rbs +33 -0
- data/sig/rooibos/router/actions.rbs +27 -0
- data/sig/rooibos/router/flow/dispatch.rbs +29 -0
- data/sig/rooibos/router/flow/inward.rbs +37 -0
- data/sig/rooibos/router/flow/outward.rbs +36 -0
- data/sig/rooibos/router/forward.rbs +35 -0
- data/sig/rooibos/router/forwards.rbs +34 -0
- data/sig/rooibos/router/guard.rbs +21 -0
- data/sig/rooibos/router/observe.rbs +20 -0
- data/sig/rooibos/router/observes.rbs +38 -0
- data/sig/rooibos/router/otherwise.rbs +22 -0
- data/sig/rooibos/router/otherwises.rbs +20 -0
- data/sig/rooibos/router/predicate.rbs +51 -0
- data/sig/rooibos/router/receive.rbs +20 -0
- data/sig/rooibos/router/receives.rbs +38 -0
- data/sig/rooibos/router/registry.rbs +24 -0
- data/sig/rooibos/router/route.rbs +46 -0
- data/sig/rooibos/router/router_update.rbs +33 -0
- data/sig/rooibos/router/routes.rbs +41 -0
- data/sig/rooibos/router/rule.rbs +36 -0
- data/sig/rooibos/router.rbs +216 -161
- data/sig/rooibos/runtime.rbs +0 -1
- data/sig/rooibos/test_helper.rbs +6 -0
- data/sig/rooibos/transition.rbs +33 -0
- data/sig/rooibos.rbs +0 -10
- metadata +144 -198
- data/.builds/ruby-3.2.yml +0 -55
- data/.builds/ruby-3.3.yml +0 -55
- data/.builds/ruby-3.4.yml +0 -55
- data/.builds/ruby-4.0.0.yml +0 -55
- data/.pre-commit-config.yaml +0 -16
- data/.rubocop.yml +0 -8
- data/AGENTS.md +0 -108
- data/CHANGELOG.md +0 -308
- data/README.md +0 -183
- data/README.rdoc +0 -374
- data/Rakefile +0 -16
- data/Steepfile +0 -13
- data/doc/best_practices/forms_and_validation.md +0 -20
- data/doc/best_practices/http_workflows.md +0 -20
- data/doc/best_practices/index.md +0 -26
- data/doc/best_practices/lists_and_tables.md +0 -20
- data/doc/best_practices/modal_dialogs.md +0 -20
- data/doc/best_practices/no_stateful_widgets.md +0 -184
- data/doc/best_practices/orchestration.md +0 -20
- data/doc/best_practices/streaming_data.md +0 -20
- data/doc/contributors/design/commands_and_outlets.md +0 -214
- data/doc/contributors/design/mvu_tea_implementations_research.md +0 -373
- data/doc/contributors/documentation_plan.md +0 -616
- data/doc/contributors/documentation_stub_audit.md +0 -112
- data/doc/contributors/documentation_style.md +0 -275
- data/doc/contributors/e2e_pty.md +0 -168
- data/doc/contributors/maybe_stateful_router.md +0 -56
- data/doc/contributors/specs/earliest_tutorial_steps_per_story.md +0 -70
- data/doc/contributors/specs/file_browser.md +0 -789
- data/doc/contributors/specs/file_browser_stories.md +0 -784
- data/doc/contributors/specs/tutorials_to_stories.rb +0 -167
- data/doc/contributors/todo/scrollbar.md +0 -118
- data/doc/contributors/tutorial_old/01_project_setup.md +0 -20
- data/doc/contributors/tutorial_old/02_hello_world.md +0 -24
- data/doc/contributors/tutorial_old/03_adding_state.md +0 -26
- data/doc/contributors/tutorial_old/06_organizing_your_code.md +0 -20
- data/doc/contributors/tutorial_old/07_your_first_command.md +0 -21
- data/doc/contributors/tutorial_old/08_the_preview_pane.md +0 -20
- data/doc/contributors/tutorial_old/09_loading_states.md +0 -20
- data/doc/contributors/tutorial_old/10_testing_your_app.md +0 -20
- data/doc/contributors/tutorial_old/11_polish_and_refine.md +0 -20
- data/doc/contributors/tutorial_old/12_going_further.md +0 -20
- data/doc/contributors/tutorial_old/index.md +0 -20
- data/doc/custom.css +0 -22
- data/doc/essentials/commands.md +0 -20
- data/doc/essentials/index.md +0 -31
- data/doc/essentials/messages.md +0 -21
- data/doc/essentials/models.md +0 -21
- data/doc/essentials/shortcuts.md +0 -19
- data/doc/essentials/the_elm_architecture.md +0 -24
- data/doc/essentials/the_runtime.md +0 -21
- data/doc/essentials/update_functions.md +0 -20
- data/doc/essentials/views.md +0 -22
- data/doc/getting_started/for_go_developers.md +0 -16
- data/doc/getting_started/for_python_developers.md +0 -16
- data/doc/getting_started/for_rails_developers.md +0 -17
- data/doc/getting_started/for_ratatui_ruby_developers.md +0 -17
- data/doc/getting_started/for_react_developers.md +0 -17
- data/doc/getting_started/index.md +0 -52
- data/doc/getting_started/install.md +0 -20
- data/doc/getting_started/quickstart.md +0 -20
- data/doc/getting_started/ruby_primer.md +0 -19
- data/doc/getting_started/why_rooibos.md +0 -20
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_cmd_exec.png +0 -0
- data/doc/index.md +0 -93
- data/doc/scaling_up/async_patterns.md +0 -20
- data/doc/scaling_up/command_composition.md +0 -20
- data/doc/scaling_up/custom_commands.md +0 -21
- data/doc/scaling_up/fractal_architecture.md +0 -20
- data/doc/scaling_up/index.md +0 -30
- data/doc/scaling_up/message_routing.md +0 -20
- data/doc/scaling_up/ractor_safety.md +0 -20
- data/doc/scaling_up/testing.md +0 -21
- data/doc/troubleshooting/common_errors.md +0 -20
- data/doc/troubleshooting/debugging.md +0 -21
- data/doc/troubleshooting/index.md +0 -23
- data/doc/troubleshooting/performance.md +0 -20
- data/doc/tutorial/01_project_setup.md +0 -44
- data/doc/tutorial/02_hello_world.md +0 -45
- data/doc/tutorial/03_static_file_list.md +0 -44
- data/doc/tutorial/04_arrow_navigation.md +0 -47
- data/doc/tutorial/05_real_files.md +0 -45
- data/doc/tutorial/06_safe_refactoring.md +0 -21
- data/doc/tutorial/07_red_first_tdd.md +0 -26
- data/doc/tutorial/08_file_metadata.md +0 -42
- data/doc/tutorial/09_text_preview.md +0 -44
- data/doc/tutorial/10_directory_tree.md +0 -42
- data/doc/tutorial/11_pane_focus.md +0 -40
- data/doc/tutorial/12_sorting.md +0 -41
- data/doc/tutorial/13_filtering.md +0 -43
- data/doc/tutorial/14_toggle_hidden.md +0 -41
- data/doc/tutorial/15_text_input_widget.md +0 -43
- data/doc/tutorial/16_rename_files.md +0 -42
- data/doc/tutorial/17_confirmation_dialogs.md +0 -43
- data/doc/tutorial/18_progress_indicators.md +0 -43
- data/doc/tutorial/19_atomic_operations.md +0 -42
- data/doc/tutorial/20_external_editor.md +0 -42
- data/doc/tutorial/21_modal_overlays.md +0 -41
- data/doc/tutorial/22_error_handling.md +0 -43
- data/doc/tutorial/23_terminal_capabilities.md +0 -53
- data/doc/tutorial/24_mouse_events.md +0 -43
- data/doc/tutorial/25_resize_events.md +0 -43
- data/doc/tutorial/26_loading_states.md +0 -42
- data/doc/tutorial/27_performance.md +0 -43
- data/doc/tutorial/28_color_schemes.md +0 -47
- data/doc/tutorial/29_configuration.md +0 -124
- data/doc/tutorial/30_going_further.md +0 -17
- data/doc/tutorial/index.md +0 -17
- data/examples/app_fractal_dashboard/README.md +0 -60
- data/examples/app_fractal_dashboard/app.rb +0 -63
- data/examples/app_fractal_dashboard/dashboard/base.rb +0 -73
- data/examples/app_fractal_dashboard/dashboard/update_helpers.rb +0 -86
- data/examples/app_fractal_dashboard/dashboard/update_manual.rb +0 -87
- data/examples/app_fractal_dashboard/dashboard/update_router.rb +0 -43
- data/examples/app_fractal_dashboard/fragments/custom_shell_input.rb +0 -81
- data/examples/app_fractal_dashboard/fragments/custom_shell_modal.rb +0 -82
- data/examples/app_fractal_dashboard/fragments/custom_shell_output.rb +0 -90
- data/examples/app_fractal_dashboard/fragments/disk_usage.rb +0 -47
- data/examples/app_fractal_dashboard/fragments/network_panel.rb +0 -45
- data/examples/app_fractal_dashboard/fragments/ping.rb +0 -47
- data/examples/app_fractal_dashboard/fragments/stats_panel.rb +0 -45
- data/examples/app_fractal_dashboard/fragments/system_info.rb +0 -47
- data/examples/app_fractal_dashboard/fragments/uptime.rb +0 -47
- data/examples/tutorial/01/app.rb +0 -50
- data/examples/tutorial/02/app.rb +0 -64
- data/examples/tutorial/03/app.rb +0 -91
- data/examples/tutorial/06_safe_refactoring/app.rb +0 -124
- data/examples/verify_readme_usage/README.md +0 -54
- data/examples/verify_readme_usage/app.rb +0 -47
- data/examples/verify_website_first_app/app.rb +0 -85
- data/examples/verify_website_hello_mvu/app.rb +0 -31
- data/examples/widget_command_system/README.md +0 -70
- data/examples/widget_command_system/app.rb +0 -134
- data/generate_tutorial_stubs.rb +0 -126
- data/mise.toml +0 -8
- data/rbs_collection.lock.yaml +0 -108
- data/rbs_collection.yaml +0 -15
- data/tasks/example_viewer.html.erb +0 -172
- data/tasks/install.rake +0 -29
- data/tasks/resources/build.yml.erb +0 -55
- data/tasks/resources/index.html.erb +0 -44
- data/tasks/resources/rubies.yml +0 -7
- data/tasks/steep.rake +0 -11
- /data/{vendor/goodcop/base.yml → lib/rooibos/rubocop.yml} +0 -0
|
@@ -1,373 +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
|
-
# MVU/TEA Implementation Research
|
|
8
|
-
|
|
9
|
-
## Summary
|
|
10
|
-
|
|
11
|
-
Research on Model-View-Update (MVU) / The Elm Architecture (TEA) implementations across 15+ frameworks and languages, focusing on initialization patterns.
|
|
12
|
-
|
|
13
|
-
## Complete List of MVU/TEA Implementations
|
|
14
|
-
|
|
15
|
-
### 1. **Elm** (Original - JavaScript/Web)
|
|
16
|
-
- **Language**: Elm
|
|
17
|
-
- **Domain**: Web applications
|
|
18
|
-
- **Init Pattern**: `init : () -> (Model, Cmd Msg)`
|
|
19
|
-
- Takes no arguments (unit type)
|
|
20
|
-
- Returns tuple of `(Model, Cmd Msg)`
|
|
21
|
-
- The `Model` is the initial state
|
|
22
|
-
- The `Cmd Msg` is an initial command/effect
|
|
23
|
-
|
|
24
|
-
### 2. **Bubble Tea** (Go/TUI)
|
|
25
|
-
- **Language**: Go
|
|
26
|
-
- **Domain**: Terminal UIs
|
|
27
|
-
- **Repository**: charmbracelet/bubbletea
|
|
28
|
-
- **Init Pattern**: `func (m Model) Init() tea.Cmd`
|
|
29
|
-
- Method on model struct
|
|
30
|
-
- Returns initial command (`tea.Cmd`)
|
|
31
|
-
- Model itself is initialized before `Init()` is called
|
|
32
|
-
- Can return `nil` if no initial command needed
|
|
33
|
-
|
|
34
|
-
### 3. **TCA (The Composable Architecture)** (Swift)
|
|
35
|
-
- **Language**: Swift
|
|
36
|
-
- **Domain**: iOS/macOS apps
|
|
37
|
-
- **Repository**: pointfreeco/swift-composable-architecture
|
|
38
|
-
- **Init Pattern**: `Store(initialState: State, reducer:)`
|
|
39
|
-
- Initial state passed to Store constructor
|
|
40
|
-
- Reducers handle all state transitions
|
|
41
|
-
- Heavy use of property wrappers for integration with SwiftUI
|
|
42
|
-
|
|
43
|
-
### 4. **Flutter Bloc** (Dart/Flutter)
|
|
44
|
-
- **Language**: Dart
|
|
45
|
-
- **Domain**: Mobile (iOS/Android), Web, Desktop
|
|
46
|
-
- **Init Pattern**: Constructor-based
|
|
47
|
-
```dart
|
|
48
|
-
class MyBloc extends Bloc<Event, State> {
|
|
49
|
-
MyBloc() : super(InitialState()) { ... }
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
- Initial state passed to `super()` in constructor
|
|
53
|
-
- Can dispatch events in constructor for async initialization
|
|
54
|
-
- Often uses separate `Initial` state class
|
|
55
|
-
|
|
56
|
-
### 5. **Android MVI** (Kotlin/Android)
|
|
57
|
-
- **Language**: Kotlin
|
|
58
|
-
- **Domain**: Android apps
|
|
59
|
-
- **Init Pattern**: ViewModel with StateFlow
|
|
60
|
-
```kotlin
|
|
61
|
-
private val _state = MutableStateFlow(UiState.initial())
|
|
62
|
-
val state: StateFlow<UiState> = _state
|
|
63
|
-
```
|
|
64
|
-
- Initial state typically from a factory method or default constructor
|
|
65
|
-
- ViewModel initializes `StateFlow` with initial state
|
|
66
|
-
- Intents sent to ViewModel via Channel or SharedFlow
|
|
67
|
-
|
|
68
|
-
### 6. **Redux** (JavaScript/React)
|
|
69
|
-
- **Language**: JavaScript/TypeScript
|
|
70
|
-
- **Domain**: Web (primarily React, but library-agnostic)
|
|
71
|
-
- **Init Pattern**: Reducer default state
|
|
72
|
-
```javascript
|
|
73
|
-
// Redux Toolkit
|
|
74
|
-
const slice = createSlice({
|
|
75
|
-
name: 'counter',
|
|
76
|
-
initialState: { value: 0 },
|
|
77
|
-
reducers: { ... }
|
|
78
|
-
})
|
|
79
|
-
```
|
|
80
|
-
- Initial state defined in slice or as reducer default parameter
|
|
81
|
-
- Not strictly MVU (no built-in effects), but similar
|
|
82
|
-
- Redux Thunk/Saga add effect handling
|
|
83
|
-
|
|
84
|
-
### 7. **Iced** (Rust/GUI)
|
|
85
|
-
- **Language**: Rust
|
|
86
|
-
- **Domain**: Cross-platform GUI
|
|
87
|
-
- **Init Pattern**: `Application::new()` trait method
|
|
88
|
-
```rust
|
|
89
|
-
impl Application for MyApp {
|
|
90
|
-
fn new(_flags: Flags) -> (Self, Command<Message>) {
|
|
91
|
-
(initial_state, initial_command)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
- Returns tuple `(State, Command)`
|
|
96
|
-
- Accepts `flags` parameter for runtime context
|
|
97
|
-
- **NOTABLE**: Flags pattern allows parameterization!
|
|
98
|
-
|
|
99
|
-
### 8. **Elmish** (F#/Fable)
|
|
100
|
-
- **Language**: F#
|
|
101
|
-
- **Domain**: Web (compiles to JavaScript via Fable)
|
|
102
|
-
- **Init Pattern**: `init : unit -> Model * Cmd<Msg>`
|
|
103
|
-
- F# implementation of Elm Architecture
|
|
104
|
-
- Returns tuple of Model and Cmd
|
|
105
|
-
- Often used with React for rendering
|
|
106
|
-
|
|
107
|
-
### 9. **Hyperapp** (JavaScript/Web)
|
|
108
|
-
- **Language**: JavaScript
|
|
109
|
-
- **Domain**: Web
|
|
110
|
-
- **Init Pattern**:
|
|
111
|
-
```javascript
|
|
112
|
-
app({
|
|
113
|
-
init: initialState, // or [state, ...effects]
|
|
114
|
-
view: ...,
|
|
115
|
-
node: ...
|
|
116
|
-
})
|
|
117
|
-
```
|
|
118
|
-
- Can be plain object for state only
|
|
119
|
-
- Can be array `[state, ...effects]` to run effects on startup
|
|
120
|
-
- Very lightweight (1KB)
|
|
121
|
-
|
|
122
|
-
### 10. **Meiosis** (JavaScript/Web)
|
|
123
|
-
- **Language**: JavaScript
|
|
124
|
-
- **Domain**: Web (view-library agnostic)
|
|
125
|
-
- **Init Pattern**:
|
|
126
|
-
```javascript
|
|
127
|
-
meiosis.run({
|
|
128
|
-
initialModel: { ... },
|
|
129
|
-
renderer: ...,
|
|
130
|
-
rootComponent: ...
|
|
131
|
-
})
|
|
132
|
-
```
|
|
133
|
-
- Emphasizes plain functions and objects
|
|
134
|
-
- Works with Flyd or Mithril Stream
|
|
135
|
-
- Very flexible, minimal abstraction
|
|
136
|
-
|
|
137
|
-
### 11. **SAM Pattern** (JavaScript)
|
|
138
|
-
- **Language**: JavaScript
|
|
139
|
-
- **Domain**: Web
|
|
140
|
-
- **Init Pattern**: State predicate initializes state machine
|
|
141
|
-
- Based on TLA+ (Temporal Logic of Actions)
|
|
142
|
-
- Model holds data, State is representation
|
|
143
|
-
- Init is a state predicate for initial conditions
|
|
144
|
-
|
|
145
|
-
### 12. **Fabulous** (F#/MAUI)
|
|
146
|
-
- **Language**: F#
|
|
147
|
-
- **Domain**: Mobile (via .NET MAUI)
|
|
148
|
-
- **Init Pattern**: MVU pattern for F#
|
|
149
|
-
- Similar to Elmish
|
|
150
|
-
- `init : unit -> Model * Cmd<Msg>`
|
|
151
|
-
|
|
152
|
-
### 13. **BlazorMVU** (.NET/Blazor)
|
|
153
|
-
- **Language**: C#
|
|
154
|
-
- **Domain**: Web (Blazor)
|
|
155
|
-
- **Init Pattern**: Inspired by Elm and F# MVU
|
|
156
|
-
- Brings MVU to C# ecosystem
|
|
157
|
-
- Initial state typically in component initialization
|
|
158
|
-
|
|
159
|
-
### 14. **MauiReactor** (.NET/MAUI)
|
|
160
|
-
- **Language**: C#
|
|
161
|
-
- **Domain**: Cross-platform mobile/desktop
|
|
162
|
-
- **Init Pattern**: MVU for .NET MAUI
|
|
163
|
-
- State-driven UI updates
|
|
164
|
-
- Functional approach to MAUI development
|
|
165
|
-
|
|
166
|
-
### 15. **MVUX** (.NET)
|
|
167
|
-
- **Language**: C#
|
|
168
|
-
- **Domain**: Cross-platform (Uno Platform)
|
|
169
|
-
- **Init Pattern**: Model-View-Update eXtended
|
|
170
|
-
- Extends MVU with data binding
|
|
171
|
-
- Immutable models
|
|
172
|
-
- Feed-based reactive updates
|
|
173
|
-
|
|
174
|
-
### 16. **ngx-mvu (Angular MVU)** (TypeScript/Angular)
|
|
175
|
-
- **Language**: TypeScript
|
|
176
|
-
- **Domain**: Web (Angular)
|
|
177
|
-
- **Init Pattern**: Applies Elm Architecture to Angular
|
|
178
|
-
- Structured approach to Angular apps
|
|
179
|
-
- Similar update/model/view separation
|
|
180
|
-
|
|
181
|
-
## Initialization Patterns Analysis
|
|
182
|
-
|
|
183
|
-
### Pattern 1: Tuple Return (Most Common)
|
|
184
|
-
**Frameworks**: Elm, Bubble Tea, Iced, Elmish, Hyperapp (array variant)
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
init : Flags -> (Model, Command)
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
**Characteristics**:
|
|
191
|
-
- Returns both initial state AND initial effect/command
|
|
192
|
-
- Highly composable (can combine child inits)
|
|
193
|
-
- **Flags/context parameter** for runtime initialization
|
|
194
|
-
|
|
195
|
-
**Example (Iced - Rust)**:
|
|
196
|
-
```rust
|
|
197
|
-
fn new(flags: Flags) -> (MyApp, Command<Message>) {
|
|
198
|
-
let initial_state = MyApp { count: flags.initial_count };
|
|
199
|
-
let initial_cmd = Command::none();
|
|
200
|
-
(initial_state, initial_cmd)
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Pattern 2: Method on Model
|
|
205
|
-
**Frameworks**: Bubble Tea
|
|
206
|
-
|
|
207
|
-
```go
|
|
208
|
-
func (m Model) Init() tea.Cmd {
|
|
209
|
-
return fetchDataCmd()
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
**Characteristics**:
|
|
214
|
-
- Model constructed first, then `Init()` called
|
|
215
|
-
- Only returns command, state already set
|
|
216
|
-
- Less composable (harder to combine states)
|
|
217
|
-
|
|
218
|
-
### Pattern 3: Constructor/Factory
|
|
219
|
-
**Frameworks**: Flutter Bloc, Android MVI, Redux, TCA
|
|
220
|
-
|
|
221
|
-
```dart
|
|
222
|
-
MyBloc() : super(InitialState()) {
|
|
223
|
-
// optional: dispatch initial events
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
**Characteristics**:
|
|
228
|
-
- Initial state in constructor parameter
|
|
229
|
-
- Effects/commands dispatched separately (if at all)
|
|
230
|
-
- OOP-style initialization
|
|
231
|
-
|
|
232
|
-
### Pattern 4: Property/Config Object
|
|
233
|
-
**Frameworks**: Hyperapp, Meiosis
|
|
234
|
-
|
|
235
|
-
```javascript
|
|
236
|
-
app({
|
|
237
|
-
init: { count: 0 }, // or [state, effect1, effect2]
|
|
238
|
-
view: ...,
|
|
239
|
-
update: ...
|
|
240
|
-
})
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
**Characteristics**:
|
|
244
|
-
- Declarative initialization
|
|
245
|
-
- Can combine state + effects in array format
|
|
246
|
-
- Very flexible
|
|
247
|
-
|
|
248
|
-
## Key Findings for Rooibos
|
|
249
|
-
|
|
250
|
-
### ✅ **Flags/Props Pattern is Proven**
|
|
251
|
-
**Iced (Rust)** explicitly uses a `flags` parameter in `new()`:
|
|
252
|
-
```rust
|
|
253
|
-
fn new(flags: Flags) -> (State, Command) { ... }
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
This directly supports your proposal for:
|
|
257
|
-
- Root fragments: `Init.(argv:, env:)`
|
|
258
|
-
- Child fragments: `Init.(theme: :dark, debug: true)`
|
|
259
|
-
|
|
260
|
-
### ✅ **Tuple Return (Model, Command) is Standard**
|
|
261
|
-
Almost all functional MVU implementations return `(state, command)`:
|
|
262
|
-
- Elm: `(Model, Cmd Msg)`
|
|
263
|
-
- Iced: `(State, Command<Message>)`
|
|
264
|
-
- Elmish: `(Model, Cmd<Msg>)`
|
|
265
|
-
- Hyperapp: `[state, ...effects]` (array variant)
|
|
266
|
-
|
|
267
|
-
Your proposal aligns perfectly: `Init = ->(flags) { [model, command] }`
|
|
268
|
-
|
|
269
|
-
### ✅ **DWIM Return Values**
|
|
270
|
-
Hyperapp allows both:
|
|
271
|
-
- `init: state` (no command)
|
|
272
|
-
- `init: [state, cmd1, cmd2]` (with effects)
|
|
273
|
-
|
|
274
|
-
This supports your DWIM proposal:
|
|
275
|
-
```ruby
|
|
276
|
-
Init = -> { Model.new(...) } # Just model
|
|
277
|
-
Init = -> { [Model.new(...), some_cmd] } # With command
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### ⚠️ **Composition is Critical**
|
|
281
|
-
The OOP frameworks (Bloc, MVI, TCA) struggle with composition:
|
|
282
|
-
- Hard to combine child states in parent's init
|
|
283
|
-
- Usually rely on dependency injection or external configuration
|
|
284
|
-
|
|
285
|
-
Functional MVU frameworks excel here:
|
|
286
|
-
```elm
|
|
287
|
-
-- Elm example
|
|
288
|
-
init flags =
|
|
289
|
-
let
|
|
290
|
-
(childModel1, childCmd1) = Child1.init flags.theme
|
|
291
|
-
(childModel2, childCmd2) = Child2.init flags.debug
|
|
292
|
-
in
|
|
293
|
-
( { child1 = childModel1, child2 = childModel2 }
|
|
294
|
-
, Cmd.batch [Cmd.map Child1Msg childCmd1, Cmd.map Child2Msg childCmd2]
|
|
295
|
-
)
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
Your proposal enables this exact pattern in Ruby!
|
|
299
|
-
|
|
300
|
-
### 📊 **No Framework Uses Static Constants**
|
|
301
|
-
**CRITICAL**: Not a single MVU framework uses a static constant for initial state in the way we currently do with `INITIAL` or `MODEL`.
|
|
302
|
-
|
|
303
|
-
All use one of:
|
|
304
|
-
1. **Callable with flags** (Elm, Iced, Elmish)
|
|
305
|
-
2 **Method on instance** (Bubble Tea)
|
|
306
|
-
3. **Constructor parameter** (Bloc, MVI, TCA)
|
|
307
|
-
4. **Config property** (Hyperapp, Meiosis)
|
|
308
|
-
|
|
309
|
-
The static constant pattern appears to be **unique to our current implementation** and is unsupported by the broader MVU ecosystem.
|
|
310
|
-
|
|
311
|
-
## Recommendations
|
|
312
|
-
|
|
313
|
-
### 1. **Adopt `Init` Callable with Flags**
|
|
314
|
-
Most aligned with functional MVU tradition (Elm, Iced, Elmish).
|
|
315
|
-
|
|
316
|
-
```ruby
|
|
317
|
-
Init = ->(theme: :dark, env: {}) do
|
|
318
|
-
[Model.new(theme: theme), nil]
|
|
319
|
-
end
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### 2. **Support Tuple Return**
|
|
323
|
-
Follow Elm/Iced pattern: `[model, command]`
|
|
324
|
-
|
|
325
|
-
### 3. **Enable DWIM**
|
|
326
|
-
Like Hyperapp, support both:
|
|
327
|
-
- `Model.new(...)` (no command)
|
|
328
|
-
- `[Model.new(...), cmd]` (with command)
|
|
329
|
-
|
|
330
|
-
### 4. **Fractal Composition Example**
|
|
331
|
-
From Elm/Iced patterns:
|
|
332
|
-
|
|
333
|
-
```ruby
|
|
334
|
-
|
|
335
|
-
module Dashboard
|
|
336
|
-
Init = ->(theme: :dark) do
|
|
337
|
-
stats_model, stats_cmd = StatsPanel::Init.(theme: theme)
|
|
338
|
-
network_model, network_cmd = NetworkPanel::Init.(theme: theme)
|
|
339
|
-
|
|
340
|
-
model = Model.new(stats: stats_model, network: network_model)
|
|
341
|
-
command = Command.batch(
|
|
342
|
-
Rooibos.route(stats_cmd, :stats),
|
|
343
|
-
Rooibos.route(network_cmd, :network)
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
[model, command]
|
|
347
|
-
end
|
|
348
|
-
end
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### 5. **Runtime Integration**
|
|
352
|
-
From Iced/Elm patterns:
|
|
353
|
-
|
|
354
|
-
```ruby
|
|
355
|
-
Rooibos.run(
|
|
356
|
-
fragment: App,
|
|
357
|
-
argv: ARGV,
|
|
358
|
-
env: ENV
|
|
359
|
-
)
|
|
360
|
-
# Internally calls: model, cmd = App::Init.(argv: ARGV, env: ENV)
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
## Conclusion
|
|
364
|
-
|
|
365
|
-
Your `Init` callable proposal is **strongly validated** by existing MVU/TEA implementations:
|
|
366
|
-
|
|
367
|
-
1. ✅ Flags/props for parameterization (Iced, Elm)
|
|
368
|
-
2. ✅ Tuple return of `(model, command)` (Elm, Iced, Elmish)
|
|
369
|
-
3. ✅ DWIM flexibility (Hyperapp)
|
|
370
|
-
4. ✅ Composition-first (all functional MVU)
|
|
371
|
-
5. ❌ Static constants are **not used** by any major framework
|
|
372
|
-
|
|
373
|
-
The pattern is battle-tested across **15+ implementations** in production systems ranging from web apps to mobile to desktop GUIs.
|