omq-cli 0.7.1 → 0.8.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/CHANGELOG.md +41 -0
- data/lib/omq/cli/base_runner.rb +61 -41
- data/lib/omq/cli/cli_parser.rb +57 -52
- data/lib/omq/cli/formatter.rb +16 -0
- data/lib/omq/cli/parallel_worker.rb +191 -0
- data/lib/omq/cli/pipe.rb +52 -172
- data/lib/omq/cli/pipe_worker.rb +149 -0
- data/lib/omq/cli/push_pull.rb +8 -0
- data/lib/omq/cli/ractor_helpers.rb +81 -0
- data/lib/omq/cli/req_rep.rb +5 -0
- data/lib/omq/cli/scatter_gather.rb +8 -0
- data/lib/omq/cli/socket_setup.rb +21 -14
- data/lib/omq/cli/transient_monitor.rb +1 -1
- data/lib/omq/cli/version.rb +1 -1
- data/lib/omq/cli.rb +5 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e9d13a0ffe0dbde54709fe0489385447cf61913d8a0b7d9abc2caa67e64fd7a5
|
|
4
|
+
data.tar.gz: 6987e6aa0569b9e2bc41d6265adf7ea6bc05458086764df0e3c6ca501f08849d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71c929c48a622fe318a531b03a6a7ab3f15c418dcf5b7662f000be0865423c660beb5967536d9ff30d26cd601648638cfdaa63f535a2da0e3dcc076c5bb36ed3
|
|
7
|
+
data.tar.gz: a3e937b036c55ae4072ddccb38b9acf278e22f8e60ee957b9441611d2a256f34b02cde3c3c847b53811e7365e498fcdeeab5aa512bf2f22c05fa309ce6c997e0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0 — 2026-04-08
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`-P` for pull, gather, and rep** — parallel Ractor workers for recv-only
|
|
8
|
+
and request-reply socket types. Output serialized through `Ractor::Port`
|
|
9
|
+
to avoid scrambled stdout.
|
|
10
|
+
- **`RactorHelpers` module** — shared Ractor infrastructure: `preresolve_tcp`,
|
|
11
|
+
`start_log_consumer`, `start_output_consumer` with `SHUTDOWN` sentinel for
|
|
12
|
+
clean consumer shutdown.
|
|
13
|
+
- **`ParallelWorker` class** — general Ractor worker for parallel socket modes.
|
|
14
|
+
- **Process titles** — all runners set descriptive `proctitle`
|
|
15
|
+
(`omq TYPE [-z] [-PN] ENDPOINTS`). Pipe shows `omq pipe [-z] [-PN] IN -> OUT`.
|
|
16
|
+
Bare script mode shows `omq script`.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **ASCII-only source** — replaced all Unicode special characters (em-dashes,
|
|
21
|
+
box-drawing, arrows) with ASCII equivalents in lib/ and test/.
|
|
22
|
+
- **Pipe default HWM** — pipe sockets now default to HWM of 64 (instead of
|
|
23
|
+
the socket default 1000) to bound memory with large messages in pipeline
|
|
24
|
+
stages. Override with `--send-hwm` / `--recv-hwm`.
|
|
25
|
+
- **Message preview** — total byte count first, 12 chars per part, max 3 parts
|
|
26
|
+
shown (`(1234B) frame1|frame2|frame3|...(5 parts)`).
|
|
27
|
+
- **`SocketSetup.apply_options`** — extracted shared socket option setup,
|
|
28
|
+
used by `BaseRunner`, `PipeRunner`, `PipeWorker`, and `ParallelWorker`.
|
|
29
|
+
- **`Formatter.preview`** — extracted from duplicated `msg_preview` methods.
|
|
30
|
+
- **Pipe/PipeWorker** — use bare `.new` for sockets, `SocketSetup.apply_options`
|
|
31
|
+
for configuration, `RactorHelpers` for Ractor infrastructure.
|
|
32
|
+
|
|
33
|
+
## 0.7.2 — 2026-04-07
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **Cleaned up pipe.rb** — removed unused `@fmt` formatters, dead `log` method,
|
|
38
|
+
and `with_timeout` wrapper. Moved `preresolve_tcp` and `start_log_consumer` to
|
|
39
|
+
`PipeWorker` class methods.
|
|
40
|
+
- **Guard `with_timeout(nil)`** — `Fiber.scheduler.with_timeout(nil)` fires
|
|
41
|
+
immediately in Async; peer-wait and pipe sequential mode now skip the timeout
|
|
42
|
+
wrapper when `config.timeout` is nil.
|
|
43
|
+
|
|
3
44
|
## 0.7.1 — 2026-04-07
|
|
4
45
|
|
|
5
46
|
### Fixed
|
data/lib/omq/cli/base_runner.rb
CHANGED
|
@@ -24,6 +24,7 @@ module OMQ
|
|
|
24
24
|
# @param task [Async::Task] the parent async task
|
|
25
25
|
# @return [void]
|
|
26
26
|
def call(task)
|
|
27
|
+
set_process_title
|
|
27
28
|
setup_socket
|
|
28
29
|
start_event_monitor if config.verbose >= 2
|
|
29
30
|
maybe_start_transient_monitor(task)
|
|
@@ -46,7 +47,33 @@ module OMQ
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
|
|
49
|
-
#
|
|
50
|
+
# -- Parallel Ractor workers -----------------------------------------
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def run_parallel_workers(socket_sym)
|
|
54
|
+
OMQ.freeze_for_ractors!
|
|
55
|
+
eps = RactorHelpers.preresolve_tcp(config.endpoints)
|
|
56
|
+
output_port, output_thread = RactorHelpers.start_output_consumer
|
|
57
|
+
log_port, log_thread = RactorHelpers.start_log_consumer
|
|
58
|
+
|
|
59
|
+
workers = config.parallel.times.map do
|
|
60
|
+
::Ractor.new(config, socket_sym, eps, output_port, log_port) do |cfg, sym, e, oport, lport|
|
|
61
|
+
ParallelWorker.new(cfg, sym, e, oport, lport).call
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
workers.each do |w|
|
|
66
|
+
w.join
|
|
67
|
+
rescue ::Ractor::RemoteError => e
|
|
68
|
+
$stderr.write("omq: Ractor error: #{e.cause&.message || e.message}\n")
|
|
69
|
+
end
|
|
70
|
+
ensure
|
|
71
|
+
RactorHelpers.stop_consumer(output_port, output_thread) if output_port
|
|
72
|
+
RactorHelpers.stop_consumer(log_port, log_thread) if log_port
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# -- Socket creation ---------------------------------------------
|
|
50
77
|
|
|
51
78
|
|
|
52
79
|
def setup_socket
|
|
@@ -68,7 +95,7 @@ module OMQ
|
|
|
68
95
|
end
|
|
69
96
|
|
|
70
97
|
|
|
71
|
-
#
|
|
98
|
+
# -- Transient disconnect monitor --------------------------------
|
|
72
99
|
|
|
73
100
|
|
|
74
101
|
def maybe_start_transient_monitor(task)
|
|
@@ -83,7 +110,7 @@ module OMQ
|
|
|
83
110
|
end
|
|
84
111
|
|
|
85
112
|
|
|
86
|
-
#
|
|
113
|
+
# -- BEGIN / END blocks ------------------------------------------
|
|
87
114
|
|
|
88
115
|
|
|
89
116
|
def run_begin_blocks
|
|
@@ -98,7 +125,7 @@ module OMQ
|
|
|
98
125
|
end
|
|
99
126
|
|
|
100
127
|
|
|
101
|
-
#
|
|
128
|
+
# -- Peer wait with grace period ---------------------------------
|
|
102
129
|
|
|
103
130
|
|
|
104
131
|
def needs_peer_wait?
|
|
@@ -107,12 +134,18 @@ module OMQ
|
|
|
107
134
|
|
|
108
135
|
|
|
109
136
|
def wait_for_peer
|
|
110
|
-
|
|
137
|
+
wait_body = proc do
|
|
111
138
|
@sock.peer_connected.wait
|
|
112
139
|
log "Peer connected"
|
|
113
140
|
wait_for_subscriber
|
|
114
141
|
apply_grace_period
|
|
115
142
|
end
|
|
143
|
+
|
|
144
|
+
if config.timeout
|
|
145
|
+
Fiber.scheduler.with_timeout(config.timeout, &wait_body)
|
|
146
|
+
else
|
|
147
|
+
wait_body.call
|
|
148
|
+
end
|
|
116
149
|
end
|
|
117
150
|
|
|
118
151
|
|
|
@@ -133,19 +166,7 @@ module OMQ
|
|
|
133
166
|
end
|
|
134
167
|
|
|
135
168
|
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def with_timeout(seconds)
|
|
140
|
-
if seconds
|
|
141
|
-
Async::Task.current.with_timeout(seconds) { yield }
|
|
142
|
-
else
|
|
143
|
-
yield
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
# ── Socket setup ────────────────────────────────────────────────
|
|
169
|
+
# -- Socket setup ------------------------------------------------
|
|
149
170
|
|
|
150
171
|
|
|
151
172
|
def setup_subscriptions
|
|
@@ -163,7 +184,7 @@ module OMQ
|
|
|
163
184
|
end
|
|
164
185
|
|
|
165
186
|
|
|
166
|
-
#
|
|
187
|
+
# -- Shared loop bodies ------------------------------------------
|
|
167
188
|
|
|
168
189
|
|
|
169
190
|
def run_send_logic
|
|
@@ -279,7 +300,7 @@ module OMQ
|
|
|
279
300
|
end
|
|
280
301
|
|
|
281
302
|
|
|
282
|
-
#
|
|
303
|
+
# -- Message I/O -------------------------------------------------
|
|
283
304
|
|
|
284
305
|
|
|
285
306
|
def send_msg(parts)
|
|
@@ -366,7 +387,7 @@ module OMQ
|
|
|
366
387
|
end
|
|
367
388
|
|
|
368
389
|
|
|
369
|
-
#
|
|
390
|
+
# -- Eval --------------------------------------------------------
|
|
370
391
|
|
|
371
392
|
|
|
372
393
|
def compile_expr
|
|
@@ -383,7 +404,7 @@ module OMQ
|
|
|
383
404
|
|
|
384
405
|
|
|
385
406
|
def assign_send_aliases
|
|
386
|
-
# Keep ivar aliases
|
|
407
|
+
# Keep ivar aliases -- subclasses check these directly
|
|
387
408
|
@send_begin_proc = @send_evaluator.begin_proc
|
|
388
409
|
@send_eval_proc = @send_evaluator.eval_proc
|
|
389
410
|
@send_end_proc = @send_evaluator.end_proc
|
|
@@ -410,7 +431,22 @@ module OMQ
|
|
|
410
431
|
SENT = ExpressionEvaluator::SENT
|
|
411
432
|
|
|
412
433
|
|
|
413
|
-
#
|
|
434
|
+
# -- Process title -------------------------------------------------
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def set_process_title(endpoints: nil)
|
|
438
|
+
eps = endpoints || config.endpoints
|
|
439
|
+
title = ["omq", config.type_name]
|
|
440
|
+
title << "-z" if config.compress
|
|
441
|
+
title << "-P#{config.parallel}" if config.parallel
|
|
442
|
+
eps.each do |ep|
|
|
443
|
+
title << (ep.respond_to?(:url) ? ep.url : ep.to_s)
|
|
444
|
+
end
|
|
445
|
+
Process.setproctitle(title.join(" "))
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# -- Logging -----------------------------------------------------
|
|
414
450
|
|
|
415
451
|
|
|
416
452
|
def log(msg)
|
|
@@ -425,9 +461,9 @@ module OMQ
|
|
|
425
461
|
@sock.monitor(verbose: verbose) do |event|
|
|
426
462
|
case event.type
|
|
427
463
|
when :message_sent
|
|
428
|
-
$stderr.write("omq: >> #{
|
|
464
|
+
$stderr.write("omq: >> #{Formatter.preview(event.detail[:parts])}\n")
|
|
429
465
|
when :message_received
|
|
430
|
-
$stderr.write("omq: << #{
|
|
466
|
+
$stderr.write("omq: << #{Formatter.preview(event.detail[:parts])}\n")
|
|
431
467
|
else
|
|
432
468
|
ep = event.endpoint ? " #{event.endpoint}" : ""
|
|
433
469
|
detail = event.detail ? " #{event.detail}" : ""
|
|
@@ -435,22 +471,6 @@ module OMQ
|
|
|
435
471
|
end
|
|
436
472
|
end
|
|
437
473
|
end
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
def msg_preview(parts)
|
|
441
|
-
parts.map { |p| preview_bytes(p) }.join(" | ")
|
|
442
|
-
end
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
def preview_bytes(str)
|
|
446
|
-
bytes = str.b
|
|
447
|
-
preview = bytes[0, 10].gsub(/[^[:print:]]/, ".")
|
|
448
|
-
if bytes.bytesize > 10
|
|
449
|
-
"#{preview}... (#{bytes.bytesize}B)"
|
|
450
|
-
else
|
|
451
|
-
preview
|
|
452
|
-
end
|
|
453
|
-
end
|
|
454
474
|
end
|
|
455
475
|
end
|
|
456
476
|
end
|
data/lib/omq/cli/cli_parser.rb
CHANGED
|
@@ -6,12 +6,12 @@ module OMQ
|
|
|
6
6
|
#
|
|
7
7
|
class CliParser
|
|
8
8
|
EXAMPLES = <<~'TEXT'
|
|
9
|
-
|
|
9
|
+
-- Request / Reply ------------------------------------------
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
+-----+ "hello" +-----+
|
|
12
|
+
| REQ |------------->| REP |
|
|
13
|
+
| |<-------------| |
|
|
14
|
+
+-----+ "HELLO" +-----+
|
|
15
15
|
|
|
16
16
|
# terminal 1: echo server
|
|
17
17
|
omq rep --bind tcp://:5555 --recv-eval '$F.map(&:upcase)'
|
|
@@ -23,11 +23,11 @@ module OMQ
|
|
|
23
23
|
omq rep --bind ipc:///tmp/echo.sock --echo &
|
|
24
24
|
echo "hello" | omq req --connect ipc:///tmp/echo.sock
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
-- Publish / Subscribe --------------------------------------
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
+-----+ "weather.nyc 72F" +-----+
|
|
29
|
+
| PUB |--------------------->| SUB | --subscribe "weather."
|
|
30
|
+
+-----+ +-----+
|
|
31
31
|
|
|
32
32
|
# terminal 1: subscriber (all topics by default)
|
|
33
33
|
omq sub --bind tcp://:5556
|
|
@@ -35,11 +35,11 @@ module OMQ
|
|
|
35
35
|
# terminal 2: publisher (needs --delay for subscription to propagate)
|
|
36
36
|
echo "weather.nyc 72F" | omq pub --connect tcp://localhost:5556 --delay 1
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
-- Periodic Publish -------------------------------------------
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
+-----+ "tick 1" +-----+
|
|
41
|
+
| PUB |--(every 1s)-->| SUB |
|
|
42
|
+
+-----+ +-----+
|
|
43
43
|
|
|
44
44
|
# terminal 1: subscriber
|
|
45
45
|
omq sub --bind tcp://:5556
|
|
@@ -52,11 +52,11 @@ module OMQ
|
|
|
52
52
|
omq pub --connect tcp://localhost:5556 --delay 1 \
|
|
53
53
|
--data "tick" --interval 1 --count 5
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
-- Pipeline -------------------------------------------------
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
+------+ +------+
|
|
58
|
+
| PUSH |----------->| PULL |
|
|
59
|
+
+------+ +------+
|
|
60
60
|
|
|
61
61
|
# terminal 1: worker
|
|
62
62
|
omq pull --bind tcp://:5557
|
|
@@ -68,16 +68,16 @@ module OMQ
|
|
|
68
68
|
omq pull --bind ipc:///tmp/pipeline.sock &
|
|
69
69
|
echo "task 1" | omq push --connect ipc:///tmp/pipeline.sock
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
-- Pipe (PULL -> eval -> PUSH) --------------------------------
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
+------+ +------+ +------+
|
|
74
|
+
| PUSH |--------->| pipe |--------->| PULL |
|
|
75
|
+
+------+ +------+ +------+
|
|
76
76
|
|
|
77
77
|
# terminal 1: producer
|
|
78
78
|
echo -e "hello\nworld" | omq push --bind ipc://@work
|
|
79
79
|
|
|
80
|
-
# terminal 2: worker
|
|
80
|
+
# terminal 2: worker -- uppercase each message
|
|
81
81
|
omq pipe -c ipc://@work -c ipc://@sink -e '$F.map(&:upcase)'
|
|
82
82
|
# terminal 3: collector
|
|
83
83
|
omq pull --bind ipc://@sink
|
|
@@ -88,19 +88,19 @@ module OMQ
|
|
|
88
88
|
# exit when producer disconnects (--transient)
|
|
89
89
|
omq pipe -c ipc://@work -c ipc://@sink --transient -e '$F.map(&:upcase)'
|
|
90
90
|
|
|
91
|
-
# fan-in: multiple sources
|
|
91
|
+
# fan-in: multiple sources -> one sink
|
|
92
92
|
omq pipe --in -c ipc://@work1 -c ipc://@work2 \
|
|
93
93
|
--out -c ipc://@sink -e '$F.map(&:upcase)'
|
|
94
94
|
|
|
95
|
-
# fan-out: one source
|
|
95
|
+
# fan-out: one source -> multiple sinks (round-robin)
|
|
96
96
|
omq pipe --in -b tcp://:5555 --out -c ipc://@sink1 -c ipc://@sink2 -e '$F'
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
-- CLIENT / SERVER (draft) ----------------------------------
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
+--------+ "hello" +--------+
|
|
101
|
+
| CLIENT |------------>| SERVER | --recv-eval '$F.map(&:upcase)'
|
|
102
|
+
| |<------------| |
|
|
103
|
+
+--------+ "HELLO" +--------+
|
|
104
104
|
|
|
105
105
|
# terminal 1: upcasing server
|
|
106
106
|
omq server --bind tcp://:5555 --recv-eval '$F.map(&:upcase)'
|
|
@@ -108,28 +108,28 @@ module OMQ
|
|
|
108
108
|
# terminal 2: client
|
|
109
109
|
echo "hello" | omq client --connect tcp://localhost:5555
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
-- Formats --------------------------------------------------
|
|
112
112
|
|
|
113
|
-
# ascii (default)
|
|
113
|
+
# ascii (default) -- non-printable replaced with dots
|
|
114
114
|
omq pull --bind tcp://:5557 --ascii
|
|
115
115
|
|
|
116
|
-
# quoted
|
|
116
|
+
# quoted -- lossless, round-trippable (uses String#dump escaping)
|
|
117
117
|
omq pull --bind tcp://:5557 --quoted
|
|
118
118
|
|
|
119
|
-
# JSON Lines
|
|
119
|
+
# JSON Lines -- structured, multipart as arrays
|
|
120
120
|
echo '["key","value"]' | omq push --connect tcp://localhost:5557 --jsonl
|
|
121
121
|
omq pull --bind tcp://:5557 --jsonl
|
|
122
122
|
|
|
123
123
|
# multipart via tabs
|
|
124
124
|
printf "routing-key\tpayload" | omq push --connect tcp://localhost:5557
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
-- Compression ----------------------------------------------
|
|
127
127
|
|
|
128
128
|
# both sides must use --compress
|
|
129
129
|
omq pull --bind tcp://:5557 --compress &
|
|
130
130
|
echo "compressible data" | omq push --connect tcp://localhost:5557 --compress
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
-- CURVE Encryption -----------------------------------------
|
|
133
133
|
|
|
134
134
|
# server (prints OMQ_SERVER_KEY=...)
|
|
135
135
|
omq rep --bind tcp://:5555 --echo --curve-server
|
|
@@ -138,12 +138,12 @@ module OMQ
|
|
|
138
138
|
echo "secret" | omq req --connect tcp://localhost:5555 \
|
|
139
139
|
--curve-server-key '<key from server>'
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
-- ROUTER / DEALER ------------------------------------------
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
+--------+ +--------+
|
|
144
|
+
| DEALER |---------->| ROUTER |
|
|
145
|
+
| id=w1 | | |
|
|
146
|
+
+--------+ +--------+
|
|
147
147
|
|
|
148
148
|
# terminal 1: router shows identity + message
|
|
149
149
|
omq router --bind tcp://:5555
|
|
@@ -151,7 +151,7 @@ module OMQ
|
|
|
151
151
|
# terminal 2: dealer with identity
|
|
152
152
|
echo "hello" | omq dealer --connect tcp://localhost:5555 --identity worker-1
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
-- Ruby Eval ------------------------------------------------
|
|
155
155
|
|
|
156
156
|
# filter incoming: only pass messages containing "error"
|
|
157
157
|
omq pull -b tcp://:5557 --recv-eval '$F.first.include?("error") ? $F : nil'
|
|
@@ -162,10 +162,10 @@ module OMQ
|
|
|
162
162
|
# require a local file, use its methods
|
|
163
163
|
omq rep --bind tcp://:5555 --require ./transform.rb -e 'upcase_all($F)'
|
|
164
164
|
|
|
165
|
-
# next skips, break stops
|
|
165
|
+
# next skips, break stops -- regexps match against $_
|
|
166
166
|
omq pull -b tcp://:5557 -e 'next if /^#/; break if /quit/; $F'
|
|
167
167
|
|
|
168
|
-
# BEGIN/END blocks (like awk)
|
|
168
|
+
# BEGIN/END blocks (like awk) -- accumulate and summarize
|
|
169
169
|
omq pull -b tcp://:5557 -e 'BEGIN{@sum = 0} @sum += Integer($_); nil END{puts @sum}'
|
|
170
170
|
|
|
171
171
|
# transform outgoing messages
|
|
@@ -174,9 +174,9 @@ module OMQ
|
|
|
174
174
|
# REQ: transform request and reply independently
|
|
175
175
|
echo hello | omq req -c tcp://localhost:5555 -E '$F.map(&:upcase)' -e '$_'
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
-- Script Handlers (-r) ------------------------------------
|
|
178
178
|
|
|
179
|
-
# handler.rb
|
|
179
|
+
# handler.rb -- register transforms from a file
|
|
180
180
|
# db = PG.connect("dbname=app")
|
|
181
181
|
# OMQ.incoming { |first_part, _| db.exec(first_part).values.flatten }
|
|
182
182
|
# at_exit { db.close }
|
|
@@ -185,8 +185,8 @@ module OMQ
|
|
|
185
185
|
# combine script handlers with inline eval
|
|
186
186
|
omq req -c tcp://localhost:5555 -r./handler.rb -E '$F.map(&:upcase)'
|
|
187
187
|
|
|
188
|
-
# OMQ.outgoing { |msg| ... }
|
|
189
|
-
# OMQ.incoming { |msg| ... }
|
|
188
|
+
# OMQ.outgoing { |msg| ... } -- registered outgoing transform
|
|
189
|
+
# OMQ.incoming { |msg| ... } -- registered incoming transform
|
|
190
190
|
# CLI flags (-e/-E) override registered handlers
|
|
191
191
|
TEXT
|
|
192
192
|
|
|
@@ -271,7 +271,7 @@ module OMQ
|
|
|
271
271
|
o.banner = "Usage: omq TYPE [options]\n\n" \
|
|
272
272
|
"Types: req, rep, pub, sub, push, pull, pair, dealer, router\n" \
|
|
273
273
|
"Draft: client, server, radio, dish, scatter, gather, channel, peer\n" \
|
|
274
|
-
"Virtual: pipe (PULL
|
|
274
|
+
"Virtual: pipe (PULL -> eval -> PUSH)\n\n"
|
|
275
275
|
|
|
276
276
|
o.separator "Connection:"
|
|
277
277
|
o.on("-c", "--connect URL", "Connect to endpoint (repeatable)") { |v|
|
|
@@ -412,7 +412,7 @@ module OMQ
|
|
|
412
412
|
type_name = argv.shift
|
|
413
413
|
if type_name.nil?
|
|
414
414
|
abort parser.to_s if opts[:scripts].empty?
|
|
415
|
-
# bare script mode
|
|
415
|
+
# bare script mode -- type_name stays nil
|
|
416
416
|
elsif !SOCKET_TYPE_NAMES.include?(type_name.downcase)
|
|
417
417
|
abort "Unknown socket type: #{type_name}. Known: #{SOCKET_TYPE_NAMES.join(', ')}"
|
|
418
418
|
else
|
|
@@ -486,10 +486,15 @@ module OMQ
|
|
|
486
486
|
abort "--send-eval and --target are mutually exclusive" if opts[:send_expr] && opts[:target]
|
|
487
487
|
|
|
488
488
|
if opts[:parallel]
|
|
489
|
-
|
|
489
|
+
parallel_types = %w[pipe pull gather rep]
|
|
490
|
+
abort "-P/--parallel is only valid for #{parallel_types.join(", ")}" unless parallel_types.include?(type_name)
|
|
490
491
|
abort "-P/--parallel must be 1..16" unless (1..16).include?(opts[:parallel])
|
|
491
|
-
|
|
492
|
-
|
|
492
|
+
if type_name == "pipe"
|
|
493
|
+
all_eps = opts[:in_endpoints] + opts[:out_endpoints] + opts[:endpoints]
|
|
494
|
+
else
|
|
495
|
+
all_eps = opts[:endpoints]
|
|
496
|
+
end
|
|
497
|
+
abort "-P/--parallel requires all endpoints to use --connect (not --bind)" if all_eps.any?(&:bind?)
|
|
493
498
|
end
|
|
494
499
|
|
|
495
500
|
(opts[:connects] + opts[:binds]).each do |url|
|
data/lib/omq/cli/formatter.rb
CHANGED
|
@@ -98,6 +98,22 @@ module OMQ
|
|
|
98
98
|
rescue
|
|
99
99
|
abort "omq: decompression failed (did the sender use --compress?)"
|
|
100
100
|
end
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Formats message parts for human-readable preview (logging).
|
|
104
|
+
#
|
|
105
|
+
# @param parts [Array<String>] message frames
|
|
106
|
+
# @return [String] truncated preview of each frame joined by |
|
|
107
|
+
def self.preview(parts)
|
|
108
|
+
total = parts.sum(&:bytesize)
|
|
109
|
+
shown = parts.first(3).map do |p|
|
|
110
|
+
bytes = p.b
|
|
111
|
+
preview = bytes[0, 12].gsub(/[^[:print:]]/, ".")
|
|
112
|
+
bytes.bytesize > 12 ? "#{preview}..." : preview
|
|
113
|
+
end
|
|
114
|
+
tail = parts.size > 3 ? "|...(#{parts.size} parts)" : ""
|
|
115
|
+
"(#{total}B) #{shown.join("|")}#{tail}"
|
|
116
|
+
end
|
|
101
117
|
end
|
|
102
118
|
end
|
|
103
119
|
end
|