rooibos 0.6.0 → 0.6.1
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/.builds/ruby-3.2.yml +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/CHANGELOG.md +11 -0
- data/doc/getting_started/for_rails_developers.md +17 -0
- data/doc/getting_started/for_ratatui_ruby_developers.md +17 -0
- data/lib/rooibos/command/all.rb +60 -12
- data/lib/rooibos/command/batch.rb +36 -10
- data/lib/rooibos/command/http.rb +64 -2
- data/lib/rooibos/command/open.rb +13 -6
- data/lib/rooibos/command/wait.rb +4 -1
- data/lib/rooibos/command.rb +57 -15
- data/lib/rooibos/version.rb +1 -1
- data/sig/concurrent.rbs +1 -0
- data/sig/rooibos/command.rbs +11 -7
- data/sig/rooibos/message.rbs +6 -2
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16b78b8c6fabd42cda3c443da5f6487f043f33190ac7bb8d2190add134d154be
|
|
4
|
+
data.tar.gz: 790da4bb69ca1f711fe3ae5dc41c09f18d88db4986258716db7b6db6060e21a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d306617ae1c2532c8bd105941beea50d4715840c0a087ec0199863e5ea089f7db1cdb9e9dea2a1921dec4b783c9f953963fe89f2a41d981101cc751b0ac518bf
|
|
7
|
+
data.tar.gz: 483be60cb25f8025bf222d8e63ba5d784695678aac93d883a0fa9ac83ba53832103e4f3c6be5b3908509d3b6b1f0d3572afa83de77e30398a12e7a56a9b25497
|
data/.builds/ruby-3.2.yml
CHANGED
data/.builds/ruby-3.3.yml
CHANGED
data/.builds/ruby-3.4.yml
CHANGED
data/.builds/ruby-4.0.0.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
21
21
|
|
|
22
22
|
### Removed
|
|
23
23
|
|
|
24
|
+
## [0.6.1] - 2026-01-26
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
### Removed
|
|
33
|
+
|
|
24
34
|
## [0.6.0] - 2026-01-25
|
|
25
35
|
|
|
26
36
|
### Added
|
|
@@ -250,6 +260,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
250
260
|
- **First Release**: Empty release of `rooibos`, a Ruby implementation of The Elm Architecture (TEA) for `ratatui_ruby`. Scaffolding generated by `ratatui_ruby-devtools`.
|
|
251
261
|
|
|
252
262
|
[Unreleased]: https://git.sr.ht/~kerrick/rooibos/refs/HEAD
|
|
263
|
+
[0.6.1]: https://git.sr.ht/~kerrick/rooibos/refs/v0.6.1
|
|
253
264
|
[0.6.0]: https://git.sr.ht/~kerrick/rooibos/refs/v0.6.0
|
|
254
265
|
[0.5.0]: https://git.sr.ht/~kerrick/rooibos/refs/v0.5.0
|
|
255
266
|
[0.4.0]: https://git.sr.ht/~kerrick/rooibos/refs/v0.4.0
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# For Rails Developers
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
By the end of this guide, you will:
|
|
10
|
+
|
|
11
|
+
- Understand how MVC is similar to and different from MVU
|
|
12
|
+
- Understand why TUIs run a continuous loop instead of request/response
|
|
13
|
+
- Explain how Commands handle async work off the main thread
|
|
14
|
+
- Translate "thinking in resources" to "thinking in state transitions"
|
|
15
|
+
- See side-by-side comparisons of Rails patterns and their Rooibos equivalents
|
|
16
|
+
|
|
17
|
+
> ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# For RatatuiRuby Developers
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
By the end of this guide, you will:
|
|
10
|
+
|
|
11
|
+
- Understand what Rooibos adds on top of RatatuiRuby
|
|
12
|
+
- Organize growing codebases with fragments instead of large case statements
|
|
13
|
+
- Migrate imperative draw calls to a declarative View function
|
|
14
|
+
- Convert poll_event handling to message-driven Update
|
|
15
|
+
- Use the same RatatuiRuby widgets, layouts, and styles you already know
|
|
16
|
+
|
|
17
|
+
> ⚠️ **This page is a stub.** Help us write it! See the [Documentation Plan](../contributors/documentation_plan.md) and [Style Guide](../contributors/documentation_style.md).
|
data/lib/rooibos/command/all.rb
CHANGED
|
@@ -7,22 +7,63 @@
|
|
|
7
7
|
|
|
8
8
|
module Rooibos
|
|
9
9
|
module Command
|
|
10
|
-
#
|
|
11
|
-
|
|
10
|
+
# Aggregates parallel commands and returns all results together.
|
|
11
|
+
#
|
|
12
|
+
# Dashboards load user profiles, settings, and stats before rendering.
|
|
13
|
+
# Fetching sequentially is slow. Fire-and-forget batches lose correlation
|
|
14
|
+
# between commands and their results.
|
|
15
|
+
#
|
|
16
|
+
# This command runs children in parallel and collects their results into
|
|
17
|
+
# a single <tt>Message::All</tt> response. Pattern-match on the envelope
|
|
18
|
+
# to correlate results. Each result appears in the same order as commands.
|
|
19
|
+
#
|
|
20
|
+
# Use it for coordinated fetches where you need all results before proceeding.
|
|
21
|
+
#
|
|
22
|
+
# Prefer the <tt>Command.all</tt> factory method for convenience.
|
|
23
|
+
#
|
|
24
|
+
# === Example
|
|
25
|
+
#
|
|
26
|
+
# # Using the factory method (recommended)
|
|
27
|
+
# Command.all(:dashboard,
|
|
28
|
+
# Command.http(:get, "/users", :_),
|
|
29
|
+
# Command.http(:get, "/stats", :_),
|
|
30
|
+
# )
|
|
31
|
+
#
|
|
32
|
+
# # Using the class directly
|
|
33
|
+
# All.new(:dashboard,
|
|
34
|
+
# Command.http(:get, "/users", :_),
|
|
35
|
+
# Command.http(:get, "/stats", :_),
|
|
36
|
+
# )
|
|
37
|
+
#
|
|
38
|
+
# # Pattern-match on the aggregated result
|
|
39
|
+
# def update(message, model)
|
|
40
|
+
# case message
|
|
41
|
+
# in { type: :all, envelope: :dashboard, results: [users, stats] }
|
|
42
|
+
# model.with(users:, stats:, loading: false)
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
class All < Data.define(:envelope, :commands, :nested)
|
|
12
46
|
include Custom
|
|
13
47
|
|
|
14
48
|
class << self
|
|
15
49
|
undef_method :new
|
|
16
50
|
|
|
51
|
+
# Creates an aggregating parallel command.
|
|
52
|
+
#
|
|
53
|
+
# [tag] Symbol to tag the result message.
|
|
54
|
+
# [args] Commands to run in parallel. Pass as multiple arguments
|
|
55
|
+
# or a single array.
|
|
56
|
+
#
|
|
57
|
+
# === Example
|
|
58
|
+
#
|
|
59
|
+
# All.new(:dashboard,
|
|
60
|
+
# Command.http(:get, "/users", :_),
|
|
61
|
+
# Command.http(:get, "/stats", :_),
|
|
62
|
+
# )
|
|
17
63
|
def new(tag, *args)
|
|
18
|
-
# DWIM:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
nested = true
|
|
22
|
-
else
|
|
23
|
-
commands = args
|
|
24
|
-
nested = false
|
|
25
|
-
end
|
|
64
|
+
# DWIM: flatten single-array arg to support both call patterns
|
|
65
|
+
nested = args.size == 1 && args.first.is_a?(Array)
|
|
66
|
+
commands = [args].flatten(2)
|
|
26
67
|
|
|
27
68
|
if RatatuiRuby::Debug.enabled?
|
|
28
69
|
commands.each do |cmd|
|
|
@@ -40,11 +81,18 @@ module Rooibos
|
|
|
40
81
|
end
|
|
41
82
|
end
|
|
42
83
|
|
|
43
|
-
# Executes
|
|
84
|
+
# Executes all child commands in parallel and aggregates results.
|
|
85
|
+
#
|
|
86
|
+
# Sends <tt>Message::All</tt> when all children complete. Results appear
|
|
87
|
+
# in the same order as commands. If canceled, sends <tt>Message::Canceled</tt>.
|
|
88
|
+
#
|
|
89
|
+
# [out] Outlet for sending messages.
|
|
90
|
+
# [token] Cancellation token from the runtime.
|
|
44
91
|
def call(out, token)
|
|
45
92
|
# Early return for empty commands - prevents hang from zip_futures([])
|
|
46
93
|
if commands.empty?
|
|
47
|
-
|
|
94
|
+
results = [] #: Array[Object]
|
|
95
|
+
response = Message::All.new(envelope:, results: results.freeze, nested:)
|
|
48
96
|
out.put(Ractor.make_shareable(response))
|
|
49
97
|
return
|
|
50
98
|
end
|
|
@@ -20,18 +20,29 @@ module Rooibos
|
|
|
20
20
|
# Use it for parallel fetches, concurrent refreshes, or any work that
|
|
21
21
|
# does not need coordinated results.
|
|
22
22
|
#
|
|
23
|
+
# Prefer the <tt>Command.batch</tt> factory method for convenience.
|
|
24
|
+
#
|
|
23
25
|
# === Example
|
|
24
26
|
#
|
|
27
|
+
# # Using the factory method (recommended)
|
|
28
|
+
# Command.batch(
|
|
29
|
+
# Command.http(:get, "/users", :users),
|
|
30
|
+
# Command.http(:get, "/stats", :stats),
|
|
31
|
+
# )
|
|
32
|
+
#
|
|
33
|
+
# # Using the class directly
|
|
34
|
+
# Batch.new(
|
|
35
|
+
# Command.http(:get, "/users", :users),
|
|
36
|
+
# Command.http(:get, "/stats", :stats),
|
|
37
|
+
# )
|
|
38
|
+
#
|
|
39
|
+
# # Handle each response independently
|
|
25
40
|
# def update(msg, model)
|
|
26
41
|
# case msg
|
|
27
|
-
# in :
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# )
|
|
32
|
-
# [model.with(loading: true), batch]
|
|
33
|
-
# in :users | :stats
|
|
34
|
-
# [model.with(msg => data), nil]
|
|
42
|
+
# in { type: :http, envelope: :users, body: }
|
|
43
|
+
# model.with(users: JSON.parse(body))
|
|
44
|
+
# in { type: :http, envelope: :stats, body: }
|
|
45
|
+
# model.with(stats: JSON.parse(body))
|
|
35
46
|
# end
|
|
36
47
|
# end
|
|
37
48
|
class Batch < Data.define(:commands) do
|
|
@@ -40,7 +51,15 @@ module Rooibos
|
|
|
40
51
|
class << self
|
|
41
52
|
undef_method :new
|
|
42
53
|
|
|
43
|
-
#
|
|
54
|
+
# Creates a parallel batch command.
|
|
55
|
+
#
|
|
56
|
+
# [args] Commands to run in parallel. Pass as multiple arguments
|
|
57
|
+
# or a single array.
|
|
58
|
+
#
|
|
59
|
+
# === Example
|
|
60
|
+
#
|
|
61
|
+
# Batch.new(cmd1, cmd2, cmd3)
|
|
62
|
+
# Batch.new([cmd1, cmd2, cmd3])
|
|
44
63
|
def new(*args)
|
|
45
64
|
# DWIM: accept (cmd1, cmd2) or ([cmd1, cmd2])
|
|
46
65
|
commands = (args.size == 1 && args.first.is_a?(Array)) ? args.first : args
|
|
@@ -61,7 +80,14 @@ module Rooibos
|
|
|
61
80
|
end
|
|
62
81
|
end
|
|
63
82
|
|
|
64
|
-
#
|
|
83
|
+
# Executes all child commands in parallel.
|
|
84
|
+
#
|
|
85
|
+
# Each child sends its results independently via the runtime.
|
|
86
|
+
# When all complete, sends <tt>Message::Batch</tt>. If canceled,
|
|
87
|
+
# sends <tt>Message::Canceled</tt> instead.
|
|
88
|
+
#
|
|
89
|
+
# [out] Outlet for sending messages.
|
|
90
|
+
# [token] Cancellation token from the runtime.
|
|
65
91
|
def call(out, token)
|
|
66
92
|
handles = commands.map { |cmd| out.standing(cmd, token) }
|
|
67
93
|
out.wait(*handles, token:)
|
data/lib/rooibos/command/http.rb
CHANGED
|
@@ -14,13 +14,64 @@ module Rooibos
|
|
|
14
14
|
# New code should use Rooibos::Message::HttpResponse.
|
|
15
15
|
HttpResponse = Message::HttpResponse
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
|
|
17
|
+
# Performs HTTP requests and sends the response as a message.
|
|
18
|
+
#
|
|
19
|
+
# Applications fetch data from APIs. Users expect responsive interfaces
|
|
20
|
+
# while requests complete. Managing HTTP connections, timeouts, and
|
|
21
|
+
# threading manually is error-prone.
|
|
22
|
+
#
|
|
23
|
+
# This command executes HTTP requests off the main thread. The runtime
|
|
24
|
+
# dispatches it and routes the response back to your update function
|
|
25
|
+
# as a <tt>Message::HttpResponse</tt>.
|
|
26
|
+
#
|
|
27
|
+
# Use it to fetch API data, post forms, or interact with web services.
|
|
28
|
+
#
|
|
29
|
+
# Prefer the <tt>Command.http</tt> factory method for convenience.
|
|
30
|
+
# The constructor supports flexible DWIM (Do What I Mean) arity.
|
|
31
|
+
#
|
|
32
|
+
# === Example
|
|
33
|
+
#
|
|
34
|
+
# # Using the factory method (recommended)
|
|
35
|
+
# Command.http(:get, "/api/users", :users)
|
|
36
|
+
# Command.http(get: "/api/users", envelope: :users)
|
|
37
|
+
# Command.http(:post, "/api/users", '{"name":"Jo"}', :created)
|
|
38
|
+
#
|
|
39
|
+
# # Using the class directly
|
|
40
|
+
# Http.new(:get, "/api/users", :users)
|
|
41
|
+
#
|
|
42
|
+
# # Pattern-match on the response
|
|
43
|
+
# def update(message, model)
|
|
44
|
+
# case message
|
|
45
|
+
# in { type: :http, envelope: :users, status: 200, body: }
|
|
46
|
+
# model.with(users: JSON.parse(body))
|
|
47
|
+
# in { type: :http, envelope: :users, error: }
|
|
48
|
+
# model.with(error:)
|
|
49
|
+
# end
|
|
50
|
+
# end
|
|
51
|
+
class Http < Data.define(:method, :url, :envelope, :headers, :body, :timeout, :parser)
|
|
19
52
|
include Custom
|
|
20
53
|
|
|
21
54
|
class << self
|
|
22
55
|
undef_method :new
|
|
23
56
|
|
|
57
|
+
# Creates an HTTP request command.
|
|
58
|
+
#
|
|
59
|
+
# Supports flexible DWIM arity for convenience:
|
|
60
|
+
# <tt>Http.new("url")</tt>:: GET, URL as envelope
|
|
61
|
+
# <tt>Http.new("url", :tag)</tt>:: GET, custom envelope
|
|
62
|
+
# <tt>Http.new(:post, "url")</tt>:: POST, URL as envelope
|
|
63
|
+
# <tt>Http.new(:post, "url", :tag)</tt>:: POST, custom envelope
|
|
64
|
+
# <tt>Http.new(:post, "url", "body", :tag)</tt>:: POST with body
|
|
65
|
+
# <tt>Http.new(get: "url")</tt>:: keyword shortcut
|
|
66
|
+
#
|
|
67
|
+
# [method] HTTP method symbol: <tt>:get</tt>, <tt>:post</tt>,
|
|
68
|
+
# <tt>:put</tt>, <tt>:patch</tt>, or <tt>:delete</tt>.
|
|
69
|
+
# [url] Request URL (String).
|
|
70
|
+
# [envelope] Symbol to tag the response message.
|
|
71
|
+
# [headers] Optional hash of HTTP headers.
|
|
72
|
+
# [body] Optional request body (String).
|
|
73
|
+
# [timeout] Optional timeout in seconds (default 10).
|
|
74
|
+
# [parser] Optional callable to transform response body.
|
|
24
75
|
def new(*args, method: nil, url: nil, envelope: nil, headers: nil, body: nil, timeout: nil, parser: nil,
|
|
25
76
|
get: nil, post: nil, put: nil, patch: nil, delete: nil
|
|
26
77
|
)
|
|
@@ -134,6 +185,17 @@ module Rooibos
|
|
|
134
185
|
end
|
|
135
186
|
private_class_method :parse_dwim_args
|
|
136
187
|
|
|
188
|
+
# Executes the HTTP request and sends the response.
|
|
189
|
+
#
|
|
190
|
+
# Sends <tt>Message::HttpResponse</tt> with status, body, and headers.
|
|
191
|
+
# On network errors, sends the same message type with <tt>error</tt>
|
|
192
|
+
# populated instead.
|
|
193
|
+
#
|
|
194
|
+
# Note: Ruby's <tt>Net::HTTP</tt> blocks until completion. Cancellation
|
|
195
|
+
# cannot interrupt a request in progress. The grace period is 0.
|
|
196
|
+
#
|
|
197
|
+
# [out] Outlet for sending messages.
|
|
198
|
+
# [token] Cancellation token from the runtime.
|
|
137
199
|
def call(out, token)
|
|
138
200
|
return if token.canceled?
|
|
139
201
|
|
data/lib/rooibos/command/open.rb
CHANGED
|
@@ -16,24 +16,31 @@ module Rooibos
|
|
|
16
16
|
# platform-specific commands.
|
|
17
17
|
#
|
|
18
18
|
# This command detects the platform and runs the appropriate opener:
|
|
19
|
-
#
|
|
19
|
+
# <tt>open</tt> on macOS, <tt>xdg-open</tt> on Linux, <tt>start</tt> on Windows.
|
|
20
20
|
#
|
|
21
|
-
# On success (exit 0), sends
|
|
22
|
-
# On failure (non-zero), sends
|
|
21
|
+
# On success (exit 0), sends <tt>Message::Open</tt>.
|
|
22
|
+
# On failure (non-zero), sends <tt>Message::Error</tt>.
|
|
23
|
+
#
|
|
24
|
+
# Prefer the <tt>Command.open</tt> factory method for convenience.
|
|
23
25
|
#
|
|
24
26
|
# === Example
|
|
25
27
|
#
|
|
28
|
+
# # Using the factory method (recommended)
|
|
29
|
+
# Command.open(model.selected_file)
|
|
30
|
+
# Command.open("https://rooibos.run")
|
|
31
|
+
#
|
|
32
|
+
# # Using the class directly
|
|
33
|
+
# Open.new(path: model.selected_file, envelope: model.selected_file)
|
|
34
|
+
#
|
|
35
|
+
# # Pattern-match on the response
|
|
26
36
|
# def update(msg, model)
|
|
27
37
|
# case msg
|
|
28
|
-
# in :view_clicked
|
|
29
|
-
# [model, Command.open(model.selected_file)]
|
|
30
38
|
# in { type: :open, envelope: path }
|
|
31
39
|
# model.with(status: "Opened #{path}")
|
|
32
40
|
# in { type: :error, envelope: path }
|
|
33
41
|
# model.with(error: "Could not open #{path}")
|
|
34
42
|
# end
|
|
35
43
|
# end
|
|
36
|
-
#
|
|
37
44
|
class Open < Data.define(:path, :envelope)
|
|
38
45
|
include Custom
|
|
39
46
|
|
data/lib/rooibos/command/wait.rb
CHANGED
|
@@ -20,6 +20,9 @@ module Rooibos
|
|
|
20
20
|
#
|
|
21
21
|
# Use it for delayed actions, debounced inputs, or animation loops.
|
|
22
22
|
#
|
|
23
|
+
# Prefer the <tt>Command.wait</tt> or <tt>Command.tick</tt> factory
|
|
24
|
+
# methods for convenience. Both are aliases for the same behavior.
|
|
25
|
+
#
|
|
23
26
|
# === Example: Notification dismissal
|
|
24
27
|
#
|
|
25
28
|
# def update(msg, model)
|
|
@@ -44,7 +47,7 @@ module Rooibos
|
|
|
44
47
|
# [model.with(frame:), Command.tick(0.1, :animate)]
|
|
45
48
|
# end
|
|
46
49
|
# end
|
|
47
|
-
Wait
|
|
50
|
+
class Wait < Data.define(:seconds, :envelope)
|
|
48
51
|
include Custom
|
|
49
52
|
|
|
50
53
|
# Cooperative cancellation needs no grace period.
|
data/lib/rooibos/command.rb
CHANGED
|
@@ -36,9 +36,21 @@ module Rooibos
|
|
|
36
36
|
# # No side effect
|
|
37
37
|
# [model, nil]
|
|
38
38
|
module Command
|
|
39
|
-
#
|
|
39
|
+
# Terminates the application.
|
|
40
40
|
#
|
|
41
|
-
#
|
|
41
|
+
# Users press a key or click a button to quit. The update function returns
|
|
42
|
+
# a command, and the runtime executes it. Termination is special: the
|
|
43
|
+
# runtime detects this sentinel before dispatching and breaks the loop.
|
|
44
|
+
#
|
|
45
|
+
# Prefer the <tt>Command.exit</tt> factory method for convenience.
|
|
46
|
+
#
|
|
47
|
+
# === Example
|
|
48
|
+
#
|
|
49
|
+
# # Using the factory method (recommended)
|
|
50
|
+
# [model, Command.exit]
|
|
51
|
+
#
|
|
52
|
+
# # Using the class directly
|
|
53
|
+
# [model, Exit.new]
|
|
42
54
|
class Exit < Data.define
|
|
43
55
|
include Custom
|
|
44
56
|
|
|
@@ -85,14 +97,25 @@ module Rooibos
|
|
|
85
97
|
cancellation
|
|
86
98
|
end
|
|
87
99
|
|
|
88
|
-
#
|
|
100
|
+
# Cancels a running command.
|
|
101
|
+
#
|
|
102
|
+
# Long-running commands (WebSocket listeners, database pollers) run until
|
|
103
|
+
# stopped. Stopping them requires signaling from outside the command. The
|
|
104
|
+
# runtime tracks active commands by their object identity and routes cancel
|
|
105
|
+
# requests.
|
|
106
|
+
#
|
|
107
|
+
# This type carries the handle (command object) to cancel. The runtime
|
|
108
|
+
# pattern-matches on <tt>Command::Cancel</tt> and signals the token.
|
|
109
|
+
#
|
|
110
|
+
# Prefer the <tt>Command.cancel</tt> factory method for convenience.
|
|
111
|
+
#
|
|
112
|
+
# === Example
|
|
89
113
|
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
# active commands by their object identity and routes cancel requests.
|
|
114
|
+
# # Using the factory method (recommended)
|
|
115
|
+
# [model, Command.cancel(model.active_fetch)]
|
|
93
116
|
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
117
|
+
# # Using the class directly
|
|
118
|
+
# [model, Cancel.new(handle: model.active_fetch)]
|
|
96
119
|
class Cancel < Data.define(:handle)
|
|
97
120
|
include Custom
|
|
98
121
|
include Message::Predicates
|
|
@@ -133,20 +156,29 @@ module Rooibos
|
|
|
133
156
|
#
|
|
134
157
|
# Use it to run builds, lint files, execute scripts, or invoke any CLI tool.
|
|
135
158
|
#
|
|
159
|
+
# Prefer the <tt>Command.system</tt> factory method for convenience.
|
|
160
|
+
#
|
|
136
161
|
# === Batch Mode (default)
|
|
137
162
|
#
|
|
138
163
|
# A single message arrives when the command finishes:
|
|
139
|
-
# <tt>
|
|
164
|
+
# <tt>Message::System::Batch</tt> with <tt>stdout</tt>, <tt>stderr</tt>, <tt>status</tt>.
|
|
140
165
|
#
|
|
141
166
|
# === Streaming Mode
|
|
142
167
|
#
|
|
143
|
-
#
|
|
144
|
-
#
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
#
|
|
168
|
+
# <tt>Message::System::Stream</tt> messages arrive incrementally:
|
|
169
|
+
# <tt>stream: :stdout</tt>:: for each stdout chunk
|
|
170
|
+
# <tt>stream: :stderr</tt>:: for each stderr chunk
|
|
171
|
+
# <tt>stream: :complete</tt>:: when the command finishes
|
|
172
|
+
# <tt>stream: :error</tt>:: if the command cannot start
|
|
173
|
+
#
|
|
174
|
+
# === Example
|
|
148
175
|
#
|
|
149
|
-
#
|
|
176
|
+
# # Using the factory method (recommended)
|
|
177
|
+
# Command.system("ls -la", :got_files)
|
|
178
|
+
# Command.system("tail -f log.txt", :log, stream: true)
|
|
179
|
+
#
|
|
180
|
+
# # Using the class directly
|
|
181
|
+
# System.new(command: "ls -la", envelope: :got_files, stream: false)
|
|
150
182
|
class System < Data.define(:command, :envelope, :stream)
|
|
151
183
|
include Custom
|
|
152
184
|
|
|
@@ -314,6 +346,16 @@ module Rooibos
|
|
|
314
346
|
# adds its routing prefix. Clean separation. No coupling.
|
|
315
347
|
#
|
|
316
348
|
# Use it to compose child fragments that return their own commands.
|
|
349
|
+
#
|
|
350
|
+
# Prefer the <tt>Command.map</tt> factory method for convenience.
|
|
351
|
+
#
|
|
352
|
+
# === Example
|
|
353
|
+
#
|
|
354
|
+
# # Using the factory method (recommended)
|
|
355
|
+
# Command.map(child_command) { |msg| [:sidebar, msg] }
|
|
356
|
+
#
|
|
357
|
+
# # Using the class directly
|
|
358
|
+
# Mapped.new(inner_command: child_command, mapper: ->(msg) { [:sidebar, msg] })
|
|
317
359
|
class Mapped < Data.define(:inner_command, :mapper)
|
|
318
360
|
include Custom
|
|
319
361
|
|
data/lib/rooibos/version.rb
CHANGED
data/sig/concurrent.rbs
CHANGED
data/sig/rooibos/command.rbs
CHANGED
|
@@ -18,6 +18,10 @@ module Rooibos
|
|
|
18
18
|
# Grace period for cooperative cancellation (seconds).
|
|
19
19
|
# Runtime waits this long before force-killing the thread.
|
|
20
20
|
def rooibos_cancellation_grace_period: () -> Float
|
|
21
|
+
|
|
22
|
+
# Runtime type checking (from Kernel).
|
|
23
|
+
# Required for DWIM nested detection in All.new.
|
|
24
|
+
def is_a?: (Module | Class module_or_class) -> bool
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
# Sentinel value for application termination.
|
|
@@ -114,9 +118,9 @@ module Rooibos
|
|
|
114
118
|
include Custom
|
|
115
119
|
|
|
116
120
|
attr_reader seconds: Float
|
|
117
|
-
attr_reader
|
|
121
|
+
attr_reader envelope: Symbol
|
|
118
122
|
|
|
119
|
-
def self.new: (seconds: Float,
|
|
123
|
+
def self.new: (seconds: Float, envelope: Symbol) -> instance
|
|
120
124
|
|
|
121
125
|
# Execute the timer with cooperative cancellation.
|
|
122
126
|
def call: (Outlet out, Concurrent::Cancellation token) -> void
|
|
@@ -210,7 +214,7 @@ module Rooibos
|
|
|
210
214
|
attr_reader live: Lifecycle
|
|
211
215
|
|
|
212
216
|
# Sends a message to the runtime.
|
|
213
|
-
def put: (*
|
|
217
|
+
def put: (*message args) -> void
|
|
214
218
|
|
|
215
219
|
# Runs a child command synchronously, returning its result.
|
|
216
220
|
def source: (_Callable command, Concurrent::Cancellation token, ?timeout: Float) -> Object?
|
|
@@ -242,17 +246,17 @@ module Rooibos
|
|
|
242
246
|
class All
|
|
243
247
|
include Custom
|
|
244
248
|
|
|
245
|
-
attr_reader
|
|
246
|
-
attr_reader commands: Array[
|
|
249
|
+
attr_reader envelope: Symbol
|
|
250
|
+
attr_reader commands: Array[_Command]
|
|
247
251
|
attr_reader nested: bool
|
|
248
252
|
|
|
249
|
-
def self.new: (Symbol
|
|
253
|
+
def self.new: (Symbol envelope, *_Command | Array[_Command]) -> instance
|
|
250
254
|
|
|
251
255
|
def call: (Outlet outlet, Concurrent::Cancellation token) -> void
|
|
252
256
|
end
|
|
253
257
|
|
|
254
258
|
# Creates an aggregating parallel command.
|
|
255
|
-
def self.all: (Symbol
|
|
259
|
+
def self.all: (Symbol envelope, *_Command | Array[_Command]) -> All
|
|
256
260
|
|
|
257
261
|
# HTTP response shapes for pattern matching
|
|
258
262
|
type http_success_shape = { type: :http, envelope: Symbol, status: Integer, body: String, headers: Hash[String, String] }
|
data/sig/rooibos/message.rbs
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
#++
|
|
5
5
|
|
|
6
6
|
module Rooibos
|
|
7
|
+
# Any value passed through Outlet#put to the runtime.
|
|
8
|
+
# Commands may produce any type; Predicates mixin is optional.
|
|
9
|
+
type message = Object
|
|
10
|
+
|
|
7
11
|
# Messages sent from commands to update functions.
|
|
8
12
|
module Message
|
|
9
13
|
# Fallback predicate mixin.
|
|
@@ -127,12 +131,12 @@ module Rooibos
|
|
|
127
131
|
include Predicates
|
|
128
132
|
|
|
129
133
|
attr_reader envelope: Symbol
|
|
130
|
-
attr_reader results: Array[
|
|
134
|
+
attr_reader results: Array[message]
|
|
131
135
|
attr_reader nested: bool
|
|
132
136
|
|
|
133
137
|
def initialize: (
|
|
134
138
|
envelope: Symbol,
|
|
135
|
-
results: Array[
|
|
139
|
+
results: Array[message],
|
|
136
140
|
nested: bool
|
|
137
141
|
) -> void
|
|
138
142
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rooibos
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kerrick Long
|
|
@@ -306,6 +306,8 @@ files:
|
|
|
306
306
|
- doc/essentials/views.md
|
|
307
307
|
- doc/getting_started/for_go_developers.md
|
|
308
308
|
- doc/getting_started/for_python_developers.md
|
|
309
|
+
- doc/getting_started/for_rails_developers.md
|
|
310
|
+
- doc/getting_started/for_ratatui_ruby_developers.md
|
|
309
311
|
- doc/getting_started/for_react_developers.md
|
|
310
312
|
- doc/getting_started/index.md
|
|
311
313
|
- doc/getting_started/install.md
|