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.
Files changed (217) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSES/BSD-2-Clause.txt +9 -0
  3. data/REUSE.toml +5 -0
  4. data/exe/.gitkeep +0 -0
  5. data/lib/rooibos/cli/commands/new.rb +24 -0
  6. data/lib/rooibos/command/batch.rb +10 -0
  7. data/lib/rooibos/command/bubble.rb +34 -0
  8. data/lib/rooibos/command/custom.rb +3 -2
  9. data/lib/rooibos/command/deliver.rb +50 -0
  10. data/lib/rooibos/command/http.rb +1 -1
  11. data/lib/rooibos/command/lifecycle.rb +3 -1
  12. data/lib/rooibos/command/outlet.rb +19 -9
  13. data/lib/rooibos/command.rb +107 -3
  14. data/lib/rooibos/configuration.rb +29 -0
  15. data/lib/rooibos/message/bubbled.rb +29 -0
  16. data/lib/rooibos/message.rb +24 -6
  17. data/lib/rooibos/router/action.rb +36 -0
  18. data/lib/rooibos/router/flow/dispatch.rb +39 -0
  19. data/lib/rooibos/router/flow/inward.rb +41 -0
  20. data/lib/rooibos/router/flow/outward.rb +44 -0
  21. data/lib/rooibos/router/guard.rb +56 -0
  22. data/lib/rooibos/router/predicate.rb +65 -0
  23. data/lib/rooibos/router/registry/actions.rb +41 -0
  24. data/lib/rooibos/router/registry/forwards.rb +58 -0
  25. data/lib/rooibos/router/registry/observes.rb +57 -0
  26. data/lib/rooibos/router/registry/otherwises.rb +29 -0
  27. data/lib/rooibos/router/registry/receives.rb +57 -0
  28. data/lib/rooibos/router/registry/routes.rb +59 -0
  29. data/lib/rooibos/router/registry.rb +26 -0
  30. data/lib/rooibos/router/route.rb +42 -0
  31. data/lib/rooibos/router/router_update.rb +53 -0
  32. data/lib/rooibos/router/rule/forward.rb +39 -0
  33. data/lib/rooibos/router/rule/observe.rb +22 -0
  34. data/lib/rooibos/router/rule/otherwise.rb +26 -0
  35. data/lib/rooibos/router/rule/receive.rb +22 -0
  36. data/lib/rooibos/router/rule.rb +40 -0
  37. data/lib/rooibos/router.rb +424 -438
  38. data/lib/rooibos/runtime.rb +37 -52
  39. data/lib/rooibos/test_helper.rb +22 -0
  40. data/lib/rooibos/transition.rb +92 -0
  41. data/lib/rooibos/version.rb +1 -1
  42. data/lib/rooibos.rb +2 -57
  43. data/sig/rooibos/cli.rbs +1 -0
  44. data/sig/rooibos/command.rbs +44 -0
  45. data/sig/rooibos/configuration.rbs +20 -0
  46. data/sig/rooibos/message.rbs +12 -0
  47. data/sig/rooibos/router/action.rbs +33 -0
  48. data/sig/rooibos/router/actions.rbs +27 -0
  49. data/sig/rooibos/router/flow/dispatch.rbs +29 -0
  50. data/sig/rooibos/router/flow/inward.rbs +37 -0
  51. data/sig/rooibos/router/flow/outward.rbs +36 -0
  52. data/sig/rooibos/router/forward.rbs +35 -0
  53. data/sig/rooibos/router/forwards.rbs +34 -0
  54. data/sig/rooibos/router/guard.rbs +21 -0
  55. data/sig/rooibos/router/observe.rbs +20 -0
  56. data/sig/rooibos/router/observes.rbs +38 -0
  57. data/sig/rooibos/router/otherwise.rbs +22 -0
  58. data/sig/rooibos/router/otherwises.rbs +20 -0
  59. data/sig/rooibos/router/predicate.rbs +51 -0
  60. data/sig/rooibos/router/receive.rbs +20 -0
  61. data/sig/rooibos/router/receives.rbs +38 -0
  62. data/sig/rooibos/router/registry.rbs +24 -0
  63. data/sig/rooibos/router/route.rbs +46 -0
  64. data/sig/rooibos/router/router_update.rbs +33 -0
  65. data/sig/rooibos/router/routes.rbs +41 -0
  66. data/sig/rooibos/router/rule.rbs +36 -0
  67. data/sig/rooibos/router.rbs +216 -161
  68. data/sig/rooibos/runtime.rbs +0 -1
  69. data/sig/rooibos/test_helper.rbs +6 -0
  70. data/sig/rooibos/transition.rbs +33 -0
  71. data/sig/rooibos.rbs +0 -10
  72. metadata +144 -198
  73. data/.builds/ruby-3.2.yml +0 -55
  74. data/.builds/ruby-3.3.yml +0 -55
  75. data/.builds/ruby-3.4.yml +0 -55
  76. data/.builds/ruby-4.0.0.yml +0 -55
  77. data/.pre-commit-config.yaml +0 -16
  78. data/.rubocop.yml +0 -8
  79. data/AGENTS.md +0 -108
  80. data/CHANGELOG.md +0 -308
  81. data/README.md +0 -183
  82. data/README.rdoc +0 -374
  83. data/Rakefile +0 -16
  84. data/Steepfile +0 -13
  85. data/doc/best_practices/forms_and_validation.md +0 -20
  86. data/doc/best_practices/http_workflows.md +0 -20
  87. data/doc/best_practices/index.md +0 -26
  88. data/doc/best_practices/lists_and_tables.md +0 -20
  89. data/doc/best_practices/modal_dialogs.md +0 -20
  90. data/doc/best_practices/no_stateful_widgets.md +0 -184
  91. data/doc/best_practices/orchestration.md +0 -20
  92. data/doc/best_practices/streaming_data.md +0 -20
  93. data/doc/contributors/design/commands_and_outlets.md +0 -214
  94. data/doc/contributors/design/mvu_tea_implementations_research.md +0 -373
  95. data/doc/contributors/documentation_plan.md +0 -616
  96. data/doc/contributors/documentation_stub_audit.md +0 -112
  97. data/doc/contributors/documentation_style.md +0 -275
  98. data/doc/contributors/e2e_pty.md +0 -168
  99. data/doc/contributors/maybe_stateful_router.md +0 -56
  100. data/doc/contributors/specs/earliest_tutorial_steps_per_story.md +0 -70
  101. data/doc/contributors/specs/file_browser.md +0 -789
  102. data/doc/contributors/specs/file_browser_stories.md +0 -784
  103. data/doc/contributors/specs/tutorials_to_stories.rb +0 -167
  104. data/doc/contributors/todo/scrollbar.md +0 -118
  105. data/doc/contributors/tutorial_old/01_project_setup.md +0 -20
  106. data/doc/contributors/tutorial_old/02_hello_world.md +0 -24
  107. data/doc/contributors/tutorial_old/03_adding_state.md +0 -26
  108. data/doc/contributors/tutorial_old/06_organizing_your_code.md +0 -20
  109. data/doc/contributors/tutorial_old/07_your_first_command.md +0 -21
  110. data/doc/contributors/tutorial_old/08_the_preview_pane.md +0 -20
  111. data/doc/contributors/tutorial_old/09_loading_states.md +0 -20
  112. data/doc/contributors/tutorial_old/10_testing_your_app.md +0 -20
  113. data/doc/contributors/tutorial_old/11_polish_and_refine.md +0 -20
  114. data/doc/contributors/tutorial_old/12_going_further.md +0 -20
  115. data/doc/contributors/tutorial_old/index.md +0 -20
  116. data/doc/custom.css +0 -22
  117. data/doc/essentials/commands.md +0 -20
  118. data/doc/essentials/index.md +0 -31
  119. data/doc/essentials/messages.md +0 -21
  120. data/doc/essentials/models.md +0 -21
  121. data/doc/essentials/shortcuts.md +0 -19
  122. data/doc/essentials/the_elm_architecture.md +0 -24
  123. data/doc/essentials/the_runtime.md +0 -21
  124. data/doc/essentials/update_functions.md +0 -20
  125. data/doc/essentials/views.md +0 -22
  126. data/doc/getting_started/for_go_developers.md +0 -16
  127. data/doc/getting_started/for_python_developers.md +0 -16
  128. data/doc/getting_started/for_rails_developers.md +0 -17
  129. data/doc/getting_started/for_ratatui_ruby_developers.md +0 -17
  130. data/doc/getting_started/for_react_developers.md +0 -17
  131. data/doc/getting_started/index.md +0 -52
  132. data/doc/getting_started/install.md +0 -20
  133. data/doc/getting_started/quickstart.md +0 -20
  134. data/doc/getting_started/ruby_primer.md +0 -19
  135. data/doc/getting_started/why_rooibos.md +0 -20
  136. data/doc/images/verify_readme_usage.png +0 -0
  137. data/doc/images/widget_cmd_exec.png +0 -0
  138. data/doc/index.md +0 -93
  139. data/doc/scaling_up/async_patterns.md +0 -20
  140. data/doc/scaling_up/command_composition.md +0 -20
  141. data/doc/scaling_up/custom_commands.md +0 -21
  142. data/doc/scaling_up/fractal_architecture.md +0 -20
  143. data/doc/scaling_up/index.md +0 -30
  144. data/doc/scaling_up/message_routing.md +0 -20
  145. data/doc/scaling_up/ractor_safety.md +0 -20
  146. data/doc/scaling_up/testing.md +0 -21
  147. data/doc/troubleshooting/common_errors.md +0 -20
  148. data/doc/troubleshooting/debugging.md +0 -21
  149. data/doc/troubleshooting/index.md +0 -23
  150. data/doc/troubleshooting/performance.md +0 -20
  151. data/doc/tutorial/01_project_setup.md +0 -44
  152. data/doc/tutorial/02_hello_world.md +0 -45
  153. data/doc/tutorial/03_static_file_list.md +0 -44
  154. data/doc/tutorial/04_arrow_navigation.md +0 -47
  155. data/doc/tutorial/05_real_files.md +0 -45
  156. data/doc/tutorial/06_safe_refactoring.md +0 -21
  157. data/doc/tutorial/07_red_first_tdd.md +0 -26
  158. data/doc/tutorial/08_file_metadata.md +0 -42
  159. data/doc/tutorial/09_text_preview.md +0 -44
  160. data/doc/tutorial/10_directory_tree.md +0 -42
  161. data/doc/tutorial/11_pane_focus.md +0 -40
  162. data/doc/tutorial/12_sorting.md +0 -41
  163. data/doc/tutorial/13_filtering.md +0 -43
  164. data/doc/tutorial/14_toggle_hidden.md +0 -41
  165. data/doc/tutorial/15_text_input_widget.md +0 -43
  166. data/doc/tutorial/16_rename_files.md +0 -42
  167. data/doc/tutorial/17_confirmation_dialogs.md +0 -43
  168. data/doc/tutorial/18_progress_indicators.md +0 -43
  169. data/doc/tutorial/19_atomic_operations.md +0 -42
  170. data/doc/tutorial/20_external_editor.md +0 -42
  171. data/doc/tutorial/21_modal_overlays.md +0 -41
  172. data/doc/tutorial/22_error_handling.md +0 -43
  173. data/doc/tutorial/23_terminal_capabilities.md +0 -53
  174. data/doc/tutorial/24_mouse_events.md +0 -43
  175. data/doc/tutorial/25_resize_events.md +0 -43
  176. data/doc/tutorial/26_loading_states.md +0 -42
  177. data/doc/tutorial/27_performance.md +0 -43
  178. data/doc/tutorial/28_color_schemes.md +0 -47
  179. data/doc/tutorial/29_configuration.md +0 -124
  180. data/doc/tutorial/30_going_further.md +0 -17
  181. data/doc/tutorial/index.md +0 -17
  182. data/examples/app_fractal_dashboard/README.md +0 -60
  183. data/examples/app_fractal_dashboard/app.rb +0 -63
  184. data/examples/app_fractal_dashboard/dashboard/base.rb +0 -73
  185. data/examples/app_fractal_dashboard/dashboard/update_helpers.rb +0 -86
  186. data/examples/app_fractal_dashboard/dashboard/update_manual.rb +0 -87
  187. data/examples/app_fractal_dashboard/dashboard/update_router.rb +0 -43
  188. data/examples/app_fractal_dashboard/fragments/custom_shell_input.rb +0 -81
  189. data/examples/app_fractal_dashboard/fragments/custom_shell_modal.rb +0 -82
  190. data/examples/app_fractal_dashboard/fragments/custom_shell_output.rb +0 -90
  191. data/examples/app_fractal_dashboard/fragments/disk_usage.rb +0 -47
  192. data/examples/app_fractal_dashboard/fragments/network_panel.rb +0 -45
  193. data/examples/app_fractal_dashboard/fragments/ping.rb +0 -47
  194. data/examples/app_fractal_dashboard/fragments/stats_panel.rb +0 -45
  195. data/examples/app_fractal_dashboard/fragments/system_info.rb +0 -47
  196. data/examples/app_fractal_dashboard/fragments/uptime.rb +0 -47
  197. data/examples/tutorial/01/app.rb +0 -50
  198. data/examples/tutorial/02/app.rb +0 -64
  199. data/examples/tutorial/03/app.rb +0 -91
  200. data/examples/tutorial/06_safe_refactoring/app.rb +0 -124
  201. data/examples/verify_readme_usage/README.md +0 -54
  202. data/examples/verify_readme_usage/app.rb +0 -47
  203. data/examples/verify_website_first_app/app.rb +0 -85
  204. data/examples/verify_website_hello_mvu/app.rb +0 -31
  205. data/examples/widget_command_system/README.md +0 -70
  206. data/examples/widget_command_system/app.rb +0 -134
  207. data/generate_tutorial_stubs.rb +0 -126
  208. data/mise.toml +0 -8
  209. data/rbs_collection.lock.yaml +0 -108
  210. data/rbs_collection.yaml +0 -15
  211. data/tasks/example_viewer.html.erb +0 -172
  212. data/tasks/install.rake +0 -29
  213. data/tasks/resources/build.yml.erb +0 -55
  214. data/tasks/resources/index.html.erb +0 -44
  215. data/tasks/resources/rubies.yml +0 -7
  216. data/tasks/steep.rake +0 -11
  217. /data/{vendor/goodcop/base.yml → lib/rooibos/rubocop.yml} +0 -0
@@ -4,171 +4,226 @@
4
4
  #++
5
5
 
6
6
  module Rooibos
7
- # Declarative DSL for Fractal Architecture.
8
- module Router
9
- def self.included: (Class base) -> void
10
-
11
- # Duck type for Data-like models that support `with`.
7
+ # Duck type for Data-like models that support `with` and `public_send`.
12
8
  interface _DataModel
13
9
  def public_send: (Symbol, *Object) -> Object
14
10
  def with: (**Object) -> self
15
11
  end
16
12
 
17
- # Interface for fractal child modules (fragments).
18
- # Routed modules must have an UPDATE constant that handles child messages.
19
- # NOTE: We use Module here because RBS interfaces can't declare class methods.
20
-
21
- # Configuration for key handlers.
22
- class KeyHandlerConfig < Data
23
- attr_reader handler: (^() -> Command::execution?)?
24
- attr_reader action: Symbol?
25
- attr_reader route: Symbol?
26
- attr_reader guard: (^(_DataModel) -> bool)?
27
-
28
- def initialize: (?handler: (^() -> Command::execution?)?, ?action: Symbol?, ?route: Symbol?, ?guard: (^(_DataModel) -> bool)?) -> void
29
- end
30
-
31
- # Configuration for scroll handlers (no coordinates).
32
- class ScrollHandlerConfig < Data
33
- attr_reader handler: (^() -> Command::execution?)?
34
- attr_reader action: Symbol?
35
-
36
- def initialize: (?handler: (^() -> Command::execution?)?, ?action: Symbol?) -> void
37
- end
38
-
39
- # Configuration for click handlers (x, y coordinates).
40
- class ClickHandlerConfig < Data
41
- attr_reader handler: (^(Integer, Integer) -> Command::execution?)?
42
- attr_reader action: Symbol?
43
-
44
- def initialize: (?handler: (^(Integer, Integer) -> Command::execution?)?, ?action: Symbol?) -> void
45
- end
46
-
47
- # Class methods added when Router is included.
48
- module ClassMethods : Module
49
- @routes: Hash[Symbol, Module]
50
- @actions: Hash[Symbol, ^() -> Command::execution?]
51
- @routed_actions: Hash[Symbol, Module]
52
- @key_handlers: Hash[Symbol, KeyHandlerConfig]
53
- @scroll_handlers: Hash[Symbol, ScrollHandlerConfig]
54
- @click_handler: ClickHandlerConfig?
55
-
56
- def route: (Symbol | String fragment_model_instance_attr, to: Module) -> void
57
- def routes: () -> Hash[Symbol, Module]
58
-
59
- # Action: multiple syntax forms supported
60
- def action: (
61
- ?(Symbol | String | ^() -> Command::execution?) name,
62
- ?(^() -> Command::execution? | Module) value,
63
- ?keymap: (Symbol | Array[Symbol])?,
64
- ?key: (Symbol | Array[Symbol])?,
65
- ?keys: (Symbol | Array[Symbol])?,
66
- ?mousemap: (Symbol | Array[Symbol])?,
67
- **(^() -> Command::execution? | Module) kwargs
68
- ) -> void
69
- def actions: () -> Hash[Symbol, ^() -> Command::execution?]
70
- def routed_actions: () -> Hash[Symbol, Module]
71
-
72
- private def register_action: (Symbol name, (^() -> Command::execution? | Module) value) -> void
73
-
74
- def keymap: () { (KeymapBuilder) [self: KeymapBuilder] -> void } -> void
75
- def key_handlers: () -> Hash[Symbol, KeyHandlerConfig]
76
-
77
- def mousemap: () { (MousemapBuilder) [self: MousemapBuilder] -> void } -> void
78
- def scroll_handlers: () -> Hash[Symbol, ScrollHandlerConfig]
79
- def click_handler: () -> ClickHandlerConfig?
80
-
81
- # Returns UPDATE callable that handles routing.
82
- # Uses is_a? checks for Event::Key and Event::Mouse type narrowing.
83
- # Model must be Data-like with `with` and field accessors.
84
- def from_router: () -> RouterUpdate
85
- end
86
-
87
- # UPDATE callable returned by from_router with proper typing.
88
- # Implements #call to satisfy Proc-like interfaces.
89
- class RouterUpdate
90
- @routes: Hash[Symbol, Module]
91
- @actions: Hash[Symbol, ^() -> Command::execution?]
92
- @routed_actions: Hash[Symbol, Module]
93
- @key_handlers: Hash[Symbol, KeyHandlerConfig]
94
- @scroll_handlers: Hash[Symbol, ScrollHandlerConfig]
95
- @click_handler: ClickHandlerConfig?
96
-
97
- def initialize: (
98
- routes: Hash[Symbol, Module],
99
- actions: Hash[Symbol, ^() -> Command::execution?],
100
- routed_actions: Hash[Symbol, Module],
101
- key_handlers: Hash[Symbol, KeyHandlerConfig],
102
- scroll_handlers: Hash[Symbol, ScrollHandlerConfig],
103
- click_handler: ClickHandlerConfig?
104
- ) -> void
105
-
106
- # Process message and return [model, command] tuple.
107
- def call: (
108
- (RatatuiRuby::Event::Key | RatatuiRuby::Event::Mouse | Array[Object]) message,
109
- _DataModel model
110
- ) -> [_DataModel, Command::execution?]
111
- end
112
-
113
- # Builder for keymap DSL.
114
- class KeymapBuilder
115
- @handlers: Hash[String, KeyHandlerConfig]
116
- @guard_stack: Array[^(_DataModel) -> bool]
117
-
118
- attr_reader handlers: Hash[String, KeyHandlerConfig]
119
-
120
- def initialize: () -> void
121
-
122
- # Variadic key binding: accepts multiple keys, optional action kwarg, hash bindings
123
- def key: (
124
- *(String | Symbol | ^() -> Command::execution? | Hash[Symbol, (^() -> Command::execution?) | Symbol]) args,
125
- ?action: Symbol?,
126
- ?route: Symbol?,
127
- ?when: (^(_DataModel) -> bool)?,
128
- ?if: (^(_DataModel) -> bool)?,
129
- ?only: (^(_DataModel) -> bool)?,
130
- ?guard: (^(_DataModel) -> bool)?,
131
- ?unless: (^(_DataModel) -> bool)?,
132
- ?except: (^(_DataModel) -> bool)?,
133
- ?skip: (^(_DataModel) -> bool)?,
134
- **(^() -> Command::execution? | Symbol) bindings
135
- ) -> void
136
-
137
- # Alias for key (reads better with multiple keys)
138
- alias keys key
139
-
140
- def only: (
141
- ?when: (^(_DataModel) -> bool)?,
142
- ?if: (^(_DataModel) -> bool)?,
143
- ?only: (^(_DataModel) -> bool)?,
144
- ?guard: (^(_DataModel) -> bool)?
145
- ) { () -> void } -> void
146
-
147
- def skip: (
148
- ?when: (^(_DataModel) -> bool)?,
149
- ?if: (^(_DataModel) -> bool)?,
150
- ?skip: (^(_DataModel) -> bool)?,
151
- ?guard: (^(_DataModel) -> bool)?
152
- ) { () -> void } -> void
153
-
154
- private
155
-
156
- def with_guard: ((^(_DataModel) -> bool)?) { () -> void } -> void
157
- def register_key_handler: ((Symbol | String) key_name, (^() -> Command::execution? | Symbol)? handler, (Symbol | String | ^() -> Command::execution?)? action_name, Symbol? route, (^(_DataModel) -> bool)? guard) -> void
158
- end
159
-
160
- # Builder for mousemap DSL.
161
- class MousemapBuilder
162
- @scroll_handlers: Hash[Symbol, ScrollHandlerConfig]
163
- @click_handler: ClickHandlerConfig?
164
-
165
- attr_reader scroll_handlers: Hash[Symbol, ScrollHandlerConfig]
166
- attr_reader click_handler: ClickHandlerConfig?
167
-
168
- def initialize: () -> void
169
-
170
- def click: ((^(Integer, Integer) -> Command::execution?) | Symbol handler_or_action) -> void
171
- def scroll: (:up | :down direction, (^() -> Command::execution?) | Symbol handler_or_action) -> void
13
+ # Fractal routing DSL for composing hierarchical updates.
14
+ module Router
15
+ # Sentinel type for broadcasting to all routes.
16
+ type all_routes_sentinel = Object
17
+
18
+ # Handler callable - zero or two argument form.
19
+ type handler = ^(message, _DataModel) -> Runtime::update_result
20
+ | ^() -> Runtime::update_result
21
+
22
+ # Handler or action name symbol.
23
+ type handler_or_action = handler | Symbol
24
+
25
+ # Guard predicate callable.
26
+ type guard = ^(message, _DataModel) -> boolish
27
+
28
+ # Route target: model attribute symbol, fragment module, or Route.
29
+ type route_target = Symbol | Module | Route
30
+
31
+ # Sentinel for "all routes" broadcast.
32
+ ALL_ROUTES: all_routes_sentinel
33
+
34
+ # Module inclusion hook.
35
+ def self.included: (Class base) -> void
36
+
37
+ # Class methods added when Router is included.
38
+ module ClassMethods : Module
39
+ @routes: Routes
40
+ @actions: Actions
41
+ @forwards: Forwards
42
+ @receives: Receives
43
+ @observes: Observes
44
+ @otherwises: Otherwises
45
+ @_scoped_target: route_target?
46
+
47
+ # Assembles all declarations into a frozen RouterUpdate callable.
48
+ def from_router: () -> ^(message, _DataModel) -> [_DataModel, Command::execution?]
49
+
50
+ # Declares a child route binding a nested fragment to a model slice.
51
+ def route: (
52
+ ?Symbol? prefix,
53
+ to: Module,
54
+ ?read: (^(_DataModel) -> _DataModel)?,
55
+ ?write: (^(_DataModel, _DataModel) -> _DataModel)?
56
+ ) -> Route
57
+
58
+ # Forwards all instances of a class to routes.
59
+ def forward_instances_of: (
60
+ Class klass,
61
+ ?to: route_target?,
62
+ ?as: Symbol?,
63
+ ?broadcast: bool,
64
+ ?broadcast_to: Array[route_target]?
65
+ ) -> void
66
+
67
+ # Defines a named action referenceable by symbol.
68
+ def action: (?Symbol? name, ?handler? handler_arg) -> void
69
+
70
+ # Handles matching key events. Stops further processing.
71
+ def receive_events: (
72
+ Symbol | Array[Symbol] keys,
73
+ handler_or_action handler,
74
+ ?guard: guard?,
75
+ ?when: guard?,
76
+ ?unless: guard?
77
+ ) -> void
78
+
79
+ # Handles matching routed messages. Stops further processing.
80
+ def receive_routed: (
81
+ Symbol envelope,
82
+ handler_or_action handler,
83
+ ?guard: guard?
84
+ ) -> void
85
+
86
+ # Handles matching class instances. Stops further processing.
87
+ def receive_instances_of: (
88
+ Class klass,
89
+ handler_or_action handler,
90
+ ?guard: guard?
91
+ ) -> void
92
+
93
+ # Handles any message. Stops further processing.
94
+ def receive_all: (
95
+ handler_or_action handler,
96
+ ?guard: guard?,
97
+ ?when: guard?,
98
+ ?unless: guard?
99
+ ) -> void
100
+
101
+ # Handles messages matching a custom predicate. Stops further processing.
102
+ def receive: (
103
+ guard predicate,
104
+ handler_or_action handler,
105
+ ?guard: guard?
106
+ ) -> void
107
+
108
+ # Alias for receive_events.
109
+ alias intercept_events receive_events
110
+ # Alias for receive_routed.
111
+ alias intercept_routed receive_routed
112
+ # Alias for receive_instances_of.
113
+ alias intercept_instances_of receive_instances_of
114
+ # Alias for receive_all.
115
+ alias intercept_all receive_all
116
+ # Alias for receive.
117
+ alias intercept receive
118
+
119
+ # Routes matching key events to a declared route.
120
+ def forward_events: (
121
+ Symbol | Array[Symbol] keys,
122
+ ?to: route_target?,
123
+ ?as: Symbol?,
124
+ ?guard: guard?,
125
+ ?when: guard?,
126
+ ?unless: guard?
127
+ ) -> void
128
+
129
+ # Routes matching routed messages to a declared route.
130
+ def forward_routed: (
131
+ Symbol | Array[Symbol] envelopes,
132
+ ?to: route_target?,
133
+ ?as: Symbol?
134
+ ) -> void
135
+
136
+ # Routes messages matching a custom predicate.
137
+ def forward: (
138
+ guard predicate,
139
+ ?to: route_target?,
140
+ ?as: Symbol?
141
+ ) -> void
142
+
143
+ # Observes matching key events. Does not stop further processing.
144
+ def observe_events: (
145
+ Symbol | Array[Symbol] keys,
146
+ handler_or_action handler,
147
+ ?guard: guard?,
148
+ ?when: guard?,
149
+ ?unless: guard?
150
+ ) -> void
151
+
152
+ # Observes matching routed messages. Does not stop further processing.
153
+ def observe_routed: (
154
+ Symbol envelope,
155
+ handler_or_action handler,
156
+ ?guard: guard?
157
+ ) -> void
158
+
159
+ # Observes matching class instances. Does not stop further processing.
160
+ def observe_instances_of: (
161
+ Class klass,
162
+ handler_or_action handler,
163
+ ?guard: guard?
164
+ ) -> void
165
+
166
+ # Observes any message. Does not stop further processing.
167
+ def observe_all: (
168
+ handler_or_action handler,
169
+ ?guard: guard?,
170
+ ?when: guard?,
171
+ ?unless: guard?
172
+ ) -> void
173
+
174
+ # Observes messages matching a custom predicate. Does not stop further processing.
175
+ def observe: (
176
+ guard predicate,
177
+ handler_or_action handler,
178
+ ?guard: guard?,
179
+ ?when: guard?,
180
+ ?unless: guard?
181
+ ) -> void
182
+
183
+ # Catches unhandled messages as a router-level fallback.
184
+ def otherwise: (
185
+ ?route_to: route_target,
186
+ ?guard: guard?,
187
+ ?when: guard?,
188
+ ?unless: guard?
189
+ ) -> void
190
+
191
+ private
192
+
193
+ # Route registry for this router.
194
+ def routes: () -> Routes
195
+
196
+ # Action registry for this router.
197
+ def actions: () -> Actions
198
+
199
+ # Forward rule registry for this router.
200
+ def forwards: () -> Forwards
201
+
202
+ # Receive rule registry for this router.
203
+ def receives: () -> Receives
204
+
205
+ # Observe rule registry for this router.
206
+ def observes: () -> Observes
207
+
208
+ # Otherwise rule registry for this router.
209
+ def otherwises: () -> Otherwises
210
+
211
+ # Routes any message to a declared route.
212
+ def forward_all: (
213
+ ?to: route_target?,
214
+ ?guard: guard?,
215
+ ?when: guard?,
216
+ ?unless: guard?
217
+ ) -> void
218
+
219
+ # Scopes a positive guard over declarations.
220
+ def only: (?when: guard?, ?if: guard?) ?{ () -> void } -> void
221
+
222
+ # Scopes a negative guard over declarations.
223
+ def skip: (?when: guard?, ?if: guard?) ?{ () -> void } -> void
224
+
225
+ # Scopes forward target for a block.
226
+ def route_to: (route_target target) ?{ () -> void } -> void
227
+ end
172
228
  end
173
229
  end
174
- end
@@ -59,7 +59,6 @@ module Rooibos
59
59
  private
60
60
 
61
61
  def self.validate_view_return!: (renderable? widget) -> void
62
- def self.normalize_update_return: [Model] (update_result? result, Model? previous_model) -> [Model?, Command::execution?]
63
62
  def self.validate_ractor_shareable!: [T] (T object, String name) -> void
64
63
  def self.fragment_from_kwargs: (Module? root_fragment, ?model: untyped, ?view: untyped, ?update: untyped, ?command: untyped) -> Module
65
64
  def self.fragment_invariant!: (String param) -> void
@@ -10,5 +10,11 @@ module Rooibos
10
10
  # Fails if any Command::Error is present in the messages array.
11
11
  # Relies on Minitest's flunk method being available in the including class.
12
12
  def assert_no_errors: (Array[untyped] messages, ?String? msg) -> void
13
+
14
+ # Shared fixtures for Ractor-shareable testing patterns
15
+ ClearView: ^(untyped, untyped) -> void
16
+ ExitOnQUpdate: ^(untyped, untyped) -> [untyped, Command::Exit?]
17
+ ExitOnAnyKeyUpdate: ^(untyped, untyped) -> [untyped, Command::Exit]
13
18
  end
14
19
  end
20
+
@@ -0,0 +1,33 @@
1
+ #--
2
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ # SPDX-License-Identifier: LGPL-3.0-or-later
4
+ #++
5
+
6
+ module Rooibos
7
+ # Transition represents a normalized [model, command] tuple from Update.
8
+ class Transition < Data
9
+ attr_reader model: _DataModel
10
+ attr_reader command: Command::execution?
11
+
12
+ # Normalizes a DWIM return value into a Transition.
13
+ def self.from: (update_result result, _DataModel previous_model) -> Transition
14
+
15
+ # Creates an initial Transition with no command.
16
+ def self.initial: (_DataModel model) -> Transition
17
+
18
+ # Returns new Transition with updated model, or self if nil.
19
+ def with_model: (_DataModel? new_model) -> Transition
20
+
21
+ # Returns new Transition with updated command, or self if nil.
22
+ def with_command: (Command::execution? new_command) -> Transition
23
+
24
+ # Returns new Transition with command added (batched if existing).
25
+ def with_added_command: (Command::execution? new_command) -> Transition
26
+
27
+ # Returns new Transition with command added via Separate (for observe).
28
+ def with_separate_command: (Command::execution? new_command) -> Transition
29
+
30
+ # Converts to [model, command] tuple.
31
+ def to_a: () -> [_DataModel, Command::execution?]
32
+ end
33
+ end
data/sig/rooibos.rbs CHANGED
@@ -30,16 +30,6 @@ module Rooibos
30
30
  ?command: Command::execution?
31
31
  ) -> Model
32
32
 
33
- # Wraps a command with a routing prefix.
34
- def self.route: (Command::execution command, Symbol prefix) -> Command::Mapped
35
-
36
- # Delegates a prefixed message to a child fragment's UPDATE.
37
- def self.delegate: (
38
- untyped message,
39
- Symbol prefix,
40
- ^(Array[untyped]?, untyped) -> [untyped, Command::execution?] child_update,
41
- untyped child_model
42
- ) -> ([untyped, Command::execution?] | nil)
43
33
 
44
34
  # Normalizes Init callable return value to [model, command] tuple.
45
35
  def self.normalize_init: [Model] (Runtime::init_result result) -> [Model?, Command::execution?]