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
@@ -0,0 +1,34 @@
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
+ module Router
8
+ # Collection of forward rules.
9
+ class Forwards < Data
10
+ include Registry[Forward]
11
+
12
+ attr_reader rules: Array[Forward]
13
+
14
+ def initialize: (?rules: Array[Forward]) -> void
15
+
16
+ # Adds a forward rule matching class instances.
17
+ def add_instances_of: (Class klass, ?to: route_target?, ?as: Symbol?, ?broadcast: bool, ?broadcast_to: Array[route_target]?) -> void
18
+
19
+ # Adds a forward rule matching key events.
20
+ def add_events: (Symbol | Array[Symbol] keys, ?to: route_target?, ?as: Symbol?, ?guard: guard?, ?when: guard?, ?unless: guard?) -> void
21
+
22
+ # Adds a forward rule matching routed messages by envelope.
23
+ def add_routed: (Symbol | Array[Symbol] envelopes, ?to: route_target?, ?as: Symbol?) -> void
24
+
25
+ # Adds a forward rule matching a custom predicate.
26
+ def add_custom: (guard predicate, ?to: route_target?, ?as: Symbol?) -> void
27
+
28
+ private
29
+
30
+ # Resolves broadcast options into a targets value.
31
+ def resolve_targets: (to: route_target?, broadcast: bool, broadcast_to: Array[route_target]?) -> Forward::targets
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
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
+ module Router
8
+ # Normalizes guard options into a callable.
9
+ class Guard < Data
10
+ attr_reader callable: guard?
11
+
12
+ def self.from: (?guard: guard?, ?when: guard?, ?unless: guard?) -> Guard
13
+
14
+ # Effective arity of the guard (2 if no callable).
15
+ def arity: () -> Integer
16
+
17
+ # Evaluates the guard. Returns true if no callable.
18
+ def call: (message, _DataModel) -> boolish
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
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
+ module Router
8
+ # Observe rule - matches messages, handles, and continues processing.
9
+ class Observe < Data
10
+ include Rule
11
+
12
+ attr_reader predicate: predicate
13
+ attr_reader action: action
14
+ attr_reader guard: Guard
15
+
16
+ # Applies the action and returns a Transition.
17
+ def apply: (message, _DataModel, Routes) -> Transition
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
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
+ module Router
8
+ # Collection of observe rules.
9
+ class Observes < Data
10
+ include Registry[Observe]
11
+
12
+ attr_reader rules: Array[Observe]
13
+ attr_reader actions: Actions
14
+
15
+ def initialize: (?rules: Array[Observe], actions: Actions) -> void
16
+
17
+ # Adds an observe rule matching key events.
18
+ def add_events: (Symbol | Array[Symbol] keys, handler_or_action handler, ?guard: guard?, ?when: guard?, ?unless: guard?) -> void
19
+
20
+ # Adds an observe rule matching routed messages by envelope.
21
+ def add_routed: (Symbol envelope, handler_or_action handler, ?guard: guard?) -> void
22
+
23
+ # Adds an observe rule matching class instances.
24
+ def add_instances_of: (Class klass, handler_or_action handler, ?guard: guard?) -> void
25
+
26
+ # Adds a catch-all observe rule.
27
+ def add_all: (handler_or_action handler, ?guard: guard?, ?when: guard?, ?unless: guard?) -> void
28
+
29
+ # Adds an observe rule matching a custom predicate.
30
+ def add_custom: (guard predicate, handler_or_action handler, ?guard: guard?, ?when: guard?, ?unless: guard?) -> void
31
+
32
+ private
33
+
34
+ # Resolves a Symbol action name to its Action, or wraps a lambda.
35
+ def resolve: (handler_or_action handler) -> action
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
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
+ module Router
8
+ # Otherwise rule - routes unhandled messages to a fragment.
9
+ class Otherwise < Data
10
+ include Rule
11
+
12
+ attr_reader target: route_target
13
+ attr_reader guard: Guard
14
+ attr_reader predicate: Predicate::Always
15
+
16
+ def initialize: (target: route_target, ?guard: Guard?) -> void
17
+
18
+ # Routes the message through the target route.
19
+ def apply: (message, _DataModel, Routes) -> Transition
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
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
+ module Router
8
+ # Collection of otherwise rules.
9
+ class Otherwises < Data
10
+ include Registry[Otherwise]
11
+
12
+ attr_reader rules: Array[Otherwise]
13
+
14
+ def initialize: (?rules: Array[Otherwise]) -> void
15
+
16
+ # Adds an otherwise rule with optional guard.
17
+ def add: (route_to: route_target, ?guard: guard?, ?when: guard?, ?unless: guard?) -> void
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,51 @@
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
+ module Router
8
+ # Predicate classes for message matching.
9
+ module Predicate
10
+ # Matches key events by symbol.
11
+ class Events < Data
12
+ attr_reader keys: Array[Symbol]
13
+
14
+ def initialize: (keys: Symbol | Array[Symbol]) -> void
15
+ def arity: () -> Integer
16
+ def call: (message, _DataModel) -> boolish
17
+ end
18
+
19
+ # Matches routed messages by single envelope.
20
+ class Routed < Data
21
+ attr_reader envelope: Symbol
22
+
23
+ def arity: () -> Integer
24
+ def call: (message, _DataModel) -> boolish
25
+ end
26
+
27
+ # Matches routed messages by any of the given envelopes.
28
+ class RoutedEnvelopes < Data
29
+ attr_reader envelopes: Array[Symbol]
30
+
31
+ def initialize: (envelopes: Symbol | Array[Symbol]) -> void
32
+ def arity: () -> Integer
33
+ def call: (message, _DataModel) -> boolish
34
+ end
35
+
36
+ # Matches messages by class.
37
+ class InstancesOf < Data
38
+ attr_reader klass: Class
39
+
40
+ def arity: () -> Integer
41
+ def call: (message, _DataModel) -> boolish
42
+ end
43
+
44
+ # Matches all messages.
45
+ class Always < Data
46
+ def arity: () -> Integer
47
+ def call: (message, _DataModel) -> boolish
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,20 @@
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
+ module Router
8
+ # Receive rule - matches messages and delegates to action.
9
+ class Receive < Data
10
+ include Rule
11
+
12
+ attr_reader predicate: predicate
13
+ attr_reader action: action
14
+ attr_reader guard: Guard
15
+
16
+ # Applies the action and returns a Transition.
17
+ def apply: (message, _DataModel, Routes) -> Transition
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
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
+ module Router
8
+ # Collection of receive rules.
9
+ class Receives < Data
10
+ include Registry[Receive]
11
+
12
+ attr_reader rules: Array[Receive]
13
+ attr_reader actions: Actions
14
+
15
+ def initialize: (?rules: Array[Receive], actions: Actions) -> void
16
+
17
+ # Adds a receive rule matching key events.
18
+ def add_events: (Symbol | Array[Symbol] keys, handler_or_action handler, ?guard: guard?, ?when: guard?, ?unless: guard?) -> void
19
+
20
+ # Adds a receive rule matching routed messages by envelope.
21
+ def add_routed: (Symbol envelope, handler_or_action handler, ?guard: guard?) -> void
22
+
23
+ # Adds a receive rule matching class instances.
24
+ def add_instances_of: (Class klass, handler_or_action handler, ?guard: guard?) -> void
25
+
26
+ # Adds a catch-all receive rule.
27
+ def add_all: (handler_or_action handler, ?guard: guard?, ?when: guard?, ?unless: guard?) -> void
28
+
29
+ # Adds a receive rule matching a custom predicate.
30
+ def add_custom: (guard predicate, handler_or_action handler, ?guard: guard?) -> void
31
+
32
+ private
33
+
34
+ # Resolves a Symbol action name to its Action, or wraps a lambda.
35
+ def resolve: (handler_or_action handler) -> action
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
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
+ module Router
8
+ # Common behavior for Rule collections.
9
+ # Including classes must define `rules` as a Data member.
10
+ module Registry[T]
11
+ include Enumerable[T]
12
+
13
+ # The underlying rules collection (provided by including Data class).
14
+ def rules: () -> Array[T]
15
+
16
+ # Appends a rule to the collection.
17
+ def add: (T rule) -> Array[T]
18
+
19
+ # Iterates over all rules.
20
+ def each: () { (T) -> void } -> Array[T]
21
+ | () -> Enumerator[T, Array[T]]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
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
+ module Router
8
+ # A configured route to a child fragment.
9
+ class Route < Data
10
+ # Route prefix symbol (model accessor name).
11
+ attr_reader prefix: Symbol
12
+
13
+ # Fragment module with Update callable.
14
+ attr_reader fragment: Module
15
+
16
+ # Optional custom model reader.
17
+ attr_reader read: (^(_DataModel) -> _DataModel)?
18
+
19
+ # Optional custom model writer.
20
+ attr_reader write: (^(_DataModel, _DataModel) -> _DataModel)?
21
+
22
+ def self.new: (
23
+ prefix: Symbol,
24
+ fragment: Module,
25
+ ?read: (^(_DataModel) -> _DataModel)?,
26
+ ?write: (^(_DataModel, _DataModel) -> _DataModel)?
27
+ ) -> instance
28
+
29
+ def initialize: (
30
+ prefix: Symbol,
31
+ fragment: Module,
32
+ ?read: (^(_DataModel) -> _DataModel)?,
33
+ ?write: (^(_DataModel, _DataModel) -> _DataModel)?
34
+ ) -> void
35
+
36
+ # Delegates message to child fragment and merges result.
37
+ def delegate: (message msg, _DataModel model) -> [_DataModel, Command::execution?]
38
+
39
+ private
40
+
41
+ def extract: (_DataModel model) -> _DataModel
42
+
43
+ def merge: (_DataModel model, _DataModel new_child) -> _DataModel
44
+ end
45
+ end
46
+ end
@@ -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
+ module Router
8
+ # Encapsulates dispatch logic - given frozen flow objects, processes messages.
9
+ class RouterUpdate < Data
10
+ attr_reader inward: Flow::Inward
11
+ attr_reader outward: Flow::Outward
12
+
13
+ def initialize: (
14
+ inward: Flow::Inward,
15
+ outward: Flow::Outward
16
+ ) -> void
17
+
18
+ # Dispatches a message through the router pipeline.
19
+ def call: (message, _DataModel) -> [_DataModel, Command::execution?]
20
+
21
+ private
22
+
23
+ # Handles inward dispatch (non-bubbled messages).
24
+ def dispatch_inward: (message, _DataModel) -> [_DataModel, Command::execution?]
25
+
26
+ # Extracts bubbles from a Command::Batch and processes them.
27
+ def extract_bubbles_from_batch: (Transition transition) -> Transition
28
+
29
+ # Handles outward dispatch (bubbled messages).
30
+ def dispatch_outward: (message, _DataModel) -> [_DataModel, Command::execution?]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
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
+ module Router
8
+ # Collection of routes - offers add/fetch/broadcast interface.
9
+ class Routes
10
+ include Enumerable[Route]
11
+
12
+ @routes: Set[Route]
13
+
14
+ def initialize: (?Set[Route] routes) -> void
15
+
16
+ # Adds a route to the collection. Returns the route for capture.
17
+ def add: (Route route) -> Route
18
+
19
+ # Resolves a target (Module, Symbol, or Route) to a Route.
20
+ def for: (route_target identifier) -> Route?
21
+
22
+ # Iterates over all routes.
23
+ def each: () { (Route) -> void } -> Set[Route]
24
+ | () -> Enumerator[Route, Set[Route]]
25
+
26
+ # Returns a subset of routes for the given targets.
27
+ def subset: (Array[route_target] targets) -> Routes
28
+
29
+ # Broadcasts a message to all routes, accumulating transitions.
30
+ def broadcast: (message msg, _DataModel model) -> Transition
31
+
32
+ # Broadcasts to a specific subset of routes.
33
+ def broadcast_to: (Array[route_target] targets, message msg, _DataModel model) -> Transition
34
+
35
+ private
36
+
37
+ # Finds the unique route matching the block, raises on ambiguity.
38
+ def unique: (route_target identifier) { (Route) -> boolish } -> Route?
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,36 @@
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
+ module Router
8
+ # Predicate type: callable matching (message, model).
9
+ type predicate = Predicate::Events | Predicate::Routed | Predicate::RoutedEnvelopes
10
+ | Predicate::InstancesOf | Predicate::Always
11
+ | ^(message, _DataModel) -> boolish
12
+
13
+ # Common behavior for routing rules (Forward, Receive, Observe, Otherwise).
14
+ module Rule
15
+ # Provided by including Data class: the message-matching predicate.
16
+ def predicate: () -> predicate
17
+
18
+ # Provided by including Data class: the guard condition.
19
+ def guard: () -> Guard
20
+
21
+ # Provided by including Data class: applies the rule to a message.
22
+ def apply: (message, _DataModel, Routes) -> Transition
23
+
24
+ # Tests predicate and guard.
25
+ def matches?: (Configuration config) -> boolish
26
+
27
+ # Matches and applies if matching. Returns Transition or nil.
28
+ def apply_if_matches: (Configuration config, Routes routes) -> Transition?
29
+
30
+ private
31
+
32
+ def predicate_matches?: (Configuration config) -> boolish
33
+ def guard_passes?: (Configuration config) -> boolish
34
+ end
35
+ end
36
+ end