ratatui_ruby-tea 0.3.1 → 0.4.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +42 -2
  3. data/CHANGELOG.md +76 -0
  4. data/README.md +8 -5
  5. data/doc/concepts/async_work.md +164 -0
  6. data/doc/concepts/commands.md +528 -0
  7. data/doc/concepts/message_processing.md +51 -0
  8. data/doc/contributors/WIP/decomposition_strategies_analysis.md +258 -0
  9. data/doc/contributors/WIP/implementation_plan.md +405 -0
  10. data/doc/contributors/WIP/init_callable_proposal.md +341 -0
  11. data/doc/contributors/WIP/mvu_tea_implementations_research.md +372 -0
  12. data/doc/contributors/WIP/runtime_refactoring_status.md +47 -0
  13. data/doc/contributors/WIP/task.md +36 -0
  14. data/doc/contributors/WIP/v0.4.0_todo.md +468 -0
  15. data/doc/contributors/design/commands_and_outlets.md +11 -1
  16. data/doc/contributors/priorities.md +22 -24
  17. data/examples/app_fractal_dashboard/app.rb +3 -7
  18. data/examples/app_fractal_dashboard/dashboard/base.rb +15 -16
  19. data/examples/app_fractal_dashboard/dashboard/update_helpers.rb +8 -8
  20. data/examples/app_fractal_dashboard/dashboard/update_manual.rb +11 -11
  21. data/examples/app_fractal_dashboard/dashboard/update_router.rb +4 -4
  22. data/examples/app_fractal_dashboard/{bags → fragments}/custom_shell_input.rb +8 -4
  23. data/examples/app_fractal_dashboard/fragments/custom_shell_modal.rb +82 -0
  24. data/examples/app_fractal_dashboard/{bags → fragments}/custom_shell_output.rb +8 -4
  25. data/examples/app_fractal_dashboard/{bags → fragments}/disk_usage.rb +13 -10
  26. data/examples/app_fractal_dashboard/{bags → fragments}/network_panel.rb +12 -12
  27. data/examples/app_fractal_dashboard/{bags → fragments}/ping.rb +12 -8
  28. data/examples/app_fractal_dashboard/{bags → fragments}/stats_panel.rb +12 -12
  29. data/examples/app_fractal_dashboard/{bags → fragments}/system_info.rb +11 -7
  30. data/examples/app_fractal_dashboard/{bags → fragments}/uptime.rb +11 -7
  31. data/examples/verify_readme_usage/README.md +7 -4
  32. data/examples/verify_readme_usage/app.rb +7 -4
  33. data/lib/ratatui_ruby/tea/command/all.rb +71 -0
  34. data/lib/ratatui_ruby/tea/command/batch.rb +79 -0
  35. data/lib/ratatui_ruby/tea/command/custom.rb +1 -1
  36. data/lib/ratatui_ruby/tea/command/http.rb +194 -0
  37. data/lib/ratatui_ruby/tea/command/lifecycle.rb +136 -0
  38. data/lib/ratatui_ruby/tea/command/outlet.rb +59 -27
  39. data/lib/ratatui_ruby/tea/command/wait.rb +82 -0
  40. data/lib/ratatui_ruby/tea/command.rb +245 -64
  41. data/lib/ratatui_ruby/tea/message/all.rb +47 -0
  42. data/lib/ratatui_ruby/tea/message/http_response.rb +63 -0
  43. data/lib/ratatui_ruby/tea/message/system/batch.rb +63 -0
  44. data/lib/ratatui_ruby/tea/message/system/stream.rb +69 -0
  45. data/lib/ratatui_ruby/tea/message/timer.rb +48 -0
  46. data/lib/ratatui_ruby/tea/message.rb +40 -0
  47. data/lib/ratatui_ruby/tea/router.rb +11 -11
  48. data/lib/ratatui_ruby/tea/runtime.rb +320 -185
  49. data/lib/ratatui_ruby/tea/shortcuts.rb +2 -2
  50. data/lib/ratatui_ruby/tea/test_helper.rb +58 -0
  51. data/lib/ratatui_ruby/tea/version.rb +1 -1
  52. data/lib/ratatui_ruby/tea.rb +44 -10
  53. data/rbs_collection.lock.yaml +1 -17
  54. data/sig/concurrent.rbs +72 -0
  55. data/sig/ratatui_ruby/tea/command.rbs +141 -37
  56. data/sig/ratatui_ruby/tea/message.rbs +123 -0
  57. data/sig/ratatui_ruby/tea/router.rbs +1 -1
  58. data/sig/ratatui_ruby/tea/runtime.rbs +39 -6
  59. data/sig/ratatui_ruby/tea/test_helper.rbs +12 -0
  60. data/sig/ratatui_ruby/tea.rbs +24 -4
  61. metadata +63 -11
  62. data/examples/app_fractal_dashboard/bags/custom_shell_modal.rb +0 -73
  63. data/lib/ratatui_ruby/tea/command/cancellation_token.rb +0 -135
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Tea
10
+ module Message
11
+ module System
12
+ # Streaming message from a system command.
13
+ #
14
+ # Streaming commands send incremental output instead of batching. Each line
15
+ # is a separate message, followed by a completion message.
16
+ #
17
+ # This message includes predicates to distinguish stdout, stderr, and
18
+ # completion events for pattern matching workflows.
19
+ #
20
+ # Use it to handle <tt>Command.system(..., stream: true)</tt> output.
21
+ #
22
+ # === Example
23
+ #
24
+ # case msg
25
+ # in { type: :system_stream, envelope: :build, stream: :stdout, content: }
26
+ # model.with(log: model[:log] + content)
27
+ # in { type: :system_stream, envelope: :build, stream: :complete, status: 0 }
28
+ # model.with(success: true)
29
+ # end
30
+ #
31
+ Stream = Data.define(:envelope, :stream, :content, :status) do
32
+ include Predicates
33
+
34
+ # Returns <tt>true</tt> for system stream messages.
35
+ def system?
36
+ true
37
+ end
38
+
39
+ # Returns <tt>true</tt> for stdout messages.
40
+ def stdout?
41
+ stream == :stdout
42
+ end
43
+
44
+ # Returns <tt>true</tt> for stderr messages.
45
+ def stderr?
46
+ stream == :stderr
47
+ end
48
+
49
+ # Returns <tt>true</tt> for complete messages.
50
+ def complete?
51
+ stream == :complete
52
+ end
53
+
54
+ # Deconstructs for pattern matching.
55
+ #
56
+ # Returns a hash with <tt>:type</tt>, <tt>:envelope</tt>, <tt>:stream</tt>,
57
+ # and either <tt>:content</tt> (for stdout/stderr) or <tt>:status</tt> (for complete).
58
+ def deconstruct_keys(_keys)
59
+ if complete?
60
+ { type: :system_stream, envelope:, stream:, status: }
61
+ else
62
+ { type: :system_stream, envelope:, stream:, content: }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Tea
10
+ module Message
11
+ # Response from a timer command.
12
+ #
13
+ # Timer commands fire after a delay. Pattern matching in UPDATE distinguishes
14
+ # multiple timers. Without structured responses, timers return bare symbols—hard
15
+ # to extend with elapsed time or other metadata.
16
+ #
17
+ # This response includes the envelope for routing and elapsed time. Include
18
+ # Predicates for safe predicate calls on any message.
19
+ #
20
+ # Use it to handle +Command.wait+ or +Command.tick+ completions.
21
+ #
22
+ # === Example
23
+ #
24
+ # case msg
25
+ # in { type: :timer, envelope: :dismiss }
26
+ # model.with(notification: nil)
27
+ # in { type: :timer, envelope: :animate, elapsed: }
28
+ # model.with(frame: next_frame(elapsed))
29
+ # end
30
+ #
31
+ Timer = Data.define(:envelope, :elapsed) do
32
+ include Predicates
33
+
34
+ # Returns +true+ for timer responses.
35
+ def timer?
36
+ true
37
+ end
38
+
39
+ # Deconstructs for pattern matching.
40
+ #
41
+ # Returns a hash with <tt>:type</tt>, <tt>:envelope</tt>, and <tt>:elapsed</tt>.
42
+ def deconstruct_keys(_keys)
43
+ { type: :timer, envelope:, elapsed: }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Tea
10
+ # Messages sent from commands to update functions.
11
+ #
12
+ # All built-in response types live here. Each includes the +Predicates+
13
+ # mixin for safe predicate calls.
14
+ module Message
15
+ # Fallback predicate mixin.
16
+ #
17
+ # Returns +false+ for any unknown predicate method (ending in +?+).
18
+ # Include in custom message types for safe predicate calls.
19
+ module Predicates
20
+ # Returns +false+ for unknown predicate methods.
21
+ def method_missing(name, *args, **kwargs, &block)
22
+ return false if name.to_s.end_with?("?") && args.empty? && kwargs.empty?
23
+
24
+ super
25
+ end
26
+
27
+ # Responds to all predicate methods.
28
+ def respond_to_missing?(name, *)
29
+ name.to_s.end_with?("?")
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ require_relative "message/timer"
37
+ require_relative "message/http_response"
38
+ require_relative "message/system/batch"
39
+ require_relative "message/system/stream"
40
+ require_relative "message/all"
@@ -9,16 +9,16 @@ module RatatuiRuby
9
9
  module Tea
10
10
  # Declarative DSL for Fractal Architecture.
11
11
  #
12
- # Large applications decompose into bags. Each bag has its own Model,
13
- # UPDATE, and VIEW. Parent bags route messages to child bags and compose views.
12
+ # Large applications decompose into fragments. Each fragment has its own Model,
13
+ # UPDATE, and VIEW. Parent fragments route messages to child fragments and compose views.
14
14
  # Writing this routing logic by hand is tedious and error-prone.
15
15
  #
16
16
  # Include this module to declare routes and keymaps. Call +from_router+ to
17
17
  # generate an UPDATE lambda that handles routing automatically.
18
18
  #
19
- # A *bag* is a module containing <tt>Model</tt>, <tt>INITIAL</tt>,
20
- # <tt>UPDATE</tt>, and <tt>VIEW</tt> constants. Bags compose: parent bags
21
- # delegate to child bags.
19
+ # A *fragment* is a module containing <tt>Model</tt>, <tt>INITIAL</tt>,
20
+ # <tt>UPDATE</tt>, and <tt>VIEW</tt> constants. Fragments compose: parent fragments
21
+ # delegate to child fragments.
22
22
  #
23
23
  # === Example
24
24
  #
@@ -171,13 +171,13 @@ module RatatuiRuby
171
171
 
172
172
  # Process message and return [model, command] tuple.
173
173
  def call(message, model)
174
- # 1. Try routing prefixed messages to child bags
175
- @routes.each do |prefix, bag|
176
- bag_update = bag.const_get(:UPDATE)
177
- result = Tea.delegate(message, prefix, bag_update, model.public_send(prefix))
174
+ # 1. Try routing prefixed messages to child fragments
175
+ @routes.each do |prefix, fragment|
176
+ fragment_update = fragment.const_get(:Update)
177
+ result = Tea.delegate(message, prefix, fragment_update, model.public_send(prefix))
178
178
  if result
179
- new_bag_model, command = result
180
- return [model.with(prefix => new_bag_model), command] #: [_DataModel, Command::execution?]
179
+ new_fragment_model, command = result
180
+ return [model.with(prefix => new_fragment_model), command] #: [_DataModel, Command::execution?]
181
181
  end
182
182
  end
183
183