omq-cli 0.15.1 → 0.15.2
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 +21 -0
- data/README.md +65 -30
- data/lib/omq/cli/base_runner.rb +1 -2
- data/lib/omq/cli/version.rb +1 -1
- metadata +15 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6f38a5924268358a3b48021d597248a341e3590ea36a13fc9ee8cc078d2a826
|
|
4
|
+
data.tar.gz: fc3e4b1b13cb85b9e492f6f75e4a6409e5fe2357ee1806bd68abe27954117e41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c8b10c0dc4aa93ab2676c8df93cc62026fd08b17cc76d2cb3da73059e0fba443263e9200a9eb65835bd7985bf48e5409fd04dabd0574cbe1b5c1735e14ce0c88
|
|
7
|
+
data.tar.gz: 7899ea0cbfac91674f8bc750cd31faf957bcb9f6b9996261d606d6859322b7c74fdbc2311f3d7dc9ec72355add2f9734f1493d36a4eb86af3bffb6eed110a874
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.2 — 2026-04-15
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **`-vvv` trace output for REQ/REP/PAIR.** `recv_msg` now calls
|
|
8
|
+
`trace_recv` itself, so every runner logs received messages under
|
|
9
|
+
`-vvv` (previously only `run_recv_logic` / `recv_tick` runners did,
|
|
10
|
+
leaving REQ/REP/PAIR receive-side traces silent).
|
|
11
|
+
|
|
12
|
+
### Tests
|
|
13
|
+
|
|
14
|
+
- **System test for REQ/REP verbose trace order.** Asserts REQ logs
|
|
15
|
+
`>> hi` then `<< HI`, and REP (with `-e'it.first.upcase'`) logs
|
|
16
|
+
`<< hi` then `>> HI`.
|
|
17
|
+
- **Test suite aborts on background-thread exceptions.**
|
|
18
|
+
`Thread.abort_on_exception = true` in `test_helper.rb` — a raising
|
|
19
|
+
spawned thread now re-raises in the main thread immediately instead
|
|
20
|
+
of leaving the test hanging on a receive from a dead peer.
|
|
21
|
+
- **`connect_before_bind_test`**: drop `linger: 1` from
|
|
22
|
+
`OMQ::PULL.new` — PULL is read-only and doesn't accept `linger`.
|
|
23
|
+
|
|
3
24
|
## 0.15.0 — 2026-04-14
|
|
4
25
|
|
|
5
26
|
### Changed
|
data/README.md
CHANGED
|
@@ -1,33 +1,58 @@
|
|
|
1
|
-
# omq —
|
|
1
|
+
# omq — Swiss army knife for ØMQ
|
|
2
2
|
|
|
3
3
|
[](https://rubygems.org/gems/omq-cli)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://www.ruby-lang.org)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Built on [
|
|
11
|
-
|
|
12
|
-
## Install
|
|
7
|
+
A command-line tool that speaks every ZeroMQ socket pattern, transforms
|
|
8
|
+
messages with inline Ruby, parallelizes across Ractors, encrypts with CURVE,
|
|
9
|
+
and reshapes stdin/stdout through three I/O formats on top of three
|
|
10
|
+
on-the-wire formats. Built on [ØMQ](https://github.com/zeromq/omq) — pure
|
|
11
|
+
Ruby, no C dependencies.
|
|
13
12
|
|
|
14
13
|
```sh
|
|
15
14
|
gem install omq-cli
|
|
16
15
|
```
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
Think of it as `nngcat` with a scripting engine bolted on. A few things it can do out of the box:
|
|
19
18
|
|
|
20
19
|
```sh
|
|
21
|
-
#
|
|
20
|
+
# an echo server in one line
|
|
22
21
|
omq rep -b tcp://:5555 --echo
|
|
23
22
|
|
|
24
|
-
#
|
|
25
|
-
echo "hello" | omq req -c tcp://localhost:5555
|
|
26
|
-
|
|
27
|
-
# Upcase server — -e evals Ruby on each incoming message
|
|
23
|
+
# upcase every incoming message
|
|
28
24
|
omq rep -b tcp://:5555 -e 'it.map(&:upcase)'
|
|
25
|
+
|
|
26
|
+
# publish a live timestamp every second
|
|
27
|
+
omq pub -c tcp://localhost:5556 -E 'Time.now.to_s' -i 1
|
|
28
|
+
|
|
29
|
+
# CPU-parallel PULL → transform → PUSH pipeline across all cores
|
|
30
|
+
omq pipe -c@work -c@sink -P0 -r./fib.rb -e 'fib(Integer(it.first)).to_s'
|
|
31
|
+
|
|
32
|
+
# JSON over the wire, filter by field
|
|
33
|
+
omq sub -c tcp://localhost:5556 -rjson -e 'JSON.parse(it.first)["temperature"]'
|
|
29
34
|
```
|
|
30
35
|
|
|
36
|
+
## Highlights
|
|
37
|
+
|
|
38
|
+
- **Every socket pattern** — req/rep, pub/sub, push/pull, dealer/router,
|
|
39
|
+
xpub/xsub, pair, and draft types (client/server, radio/dish, scatter/gather,
|
|
40
|
+
peer, channel)
|
|
41
|
+
- **Inline Ruby transforms** — `-e` rewrites incoming messages, `-E` rewrites
|
|
42
|
+
outgoing. `next` / `break` / `nil` for flow control, `BEGIN{}` / `END{}` for
|
|
43
|
+
awk-style aggregation, `-r` to load helper scripts
|
|
44
|
+
- **Ractor-parallel `pipe`** — `-P0` spawns one worker Ractor per core, each
|
|
45
|
+
with its own PULL/PUSH pair. CPU-bound transforms actually scale
|
|
46
|
+
- **I/O formats** — ASCII, quoted, or JSONL for stdin/stdout (display only;
|
|
47
|
+
wire stays plain)
|
|
48
|
+
- **Wire formats** — raw ZMTP, MessagePack, or Ruby Marshal shape the frame
|
|
49
|
+
payload itself, so arbitrary Ruby objects round-trip end-to-end. Optional
|
|
50
|
+
Zstandard compression (`-z`) on top
|
|
51
|
+
- **CURVE encryption** — end-to-end encrypted sockets via libsodium (or nuckle,
|
|
52
|
+
pure Ruby). `omq keygen` generates a persistent keypair
|
|
53
|
+
- **Transient mode** — `--transient` exits cleanly when peers disconnect,
|
|
54
|
+
perfect for pipeline workers and one-shot sinks
|
|
55
|
+
|
|
31
56
|
```
|
|
32
57
|
Usage: omq TYPE [options]
|
|
33
58
|
|
|
@@ -61,7 +86,8 @@ Pipe takes two positional endpoints (input, output) or uses `--in`/`--out` for m
|
|
|
61
86
|
| `scatter` | `gather` | Pipeline (draft, single-frame only) |
|
|
62
87
|
| `radio` | `dish` | Group messaging (draft, single-frame only) |
|
|
63
88
|
|
|
64
|
-
Send-only sockets read from stdin (or `--data`/`--file`) and send. Recv-only
|
|
89
|
+
Send-only sockets read from stdin (or `--data`/`--file`) and send. Recv-only
|
|
90
|
+
sockets receive and write to stdout.
|
|
65
91
|
|
|
66
92
|
```sh
|
|
67
93
|
echo "task" | omq push -c tcp://worker:5557
|
|
@@ -96,8 +122,8 @@ echo "hello" | omq req -c tcp://localhost:5555
|
|
|
96
122
|
| `dealer` | Like `pair` but round-robin send to multiple peers |
|
|
97
123
|
| `channel` | Like `pair` (draft, single-frame) |
|
|
98
124
|
|
|
99
|
-
These spawn two concurrent tasks: a receiver (prints incoming) and a sender
|
|
100
|
-
`-e` transforms incoming, `-E` transforms outgoing.
|
|
125
|
+
These spawn two concurrent tasks: a receiver (prints incoming) and a sender
|
|
126
|
+
(reads stdin). `-e` transforms incoming, `-E` transforms outgoing.
|
|
101
127
|
|
|
102
128
|
### Routing sockets
|
|
103
129
|
|
|
@@ -212,8 +238,7 @@ echo hello | omq push -c tcp://localhost:5557 -E 'it.map(&:upcase)'
|
|
|
212
238
|
omq pull -b tcp://:5557 -e 'it.first.include?("error") ? it : nil'
|
|
213
239
|
|
|
214
240
|
# REQ: different transforms per direction
|
|
215
|
-
echo hello | omq req -c tcp://localhost:5555
|
|
216
|
-
-E 'it.map(&:upcase)' -e 'it.map(&:reverse)'
|
|
241
|
+
echo hello | omq req -c tcp://localhost:5555 -E 'it.map(&:upcase)' -e 'it.map(&:reverse)'
|
|
217
242
|
|
|
218
243
|
# generate messages without stdin
|
|
219
244
|
omq pub -c tcp://localhost:5556 -E 'Time.now.to_s' -i 1
|
|
@@ -305,17 +330,32 @@ OMQ.outgoing { |msg| [*msg, Time.now.iso8601] }
|
|
|
305
330
|
|
|
306
331
|
## Formats
|
|
307
332
|
|
|
333
|
+
Two distinct axes: **I/O formats** reshape how messages are read from stdin
|
|
334
|
+
and printed to stdout, while **wire formats** shape the frame payload that
|
|
335
|
+
actually goes over ZMTP. Pick one of each — they compose freely.
|
|
336
|
+
|
|
337
|
+
### I/O formats (stdin/stdout only)
|
|
338
|
+
|
|
308
339
|
| Flag | Format |
|
|
309
340
|
|------|--------|
|
|
310
341
|
| `-A` / `--ascii` | Tab-separated frames, non-printable → dots (default) |
|
|
311
342
|
| `-Q` / `--quoted` | C-style escapes, lossless round-trip |
|
|
312
|
-
| `--raw` | Raw ZMTP binary (pipe to `hexdump -C` for debugging) |
|
|
313
343
|
| `-J` / `--jsonl` | JSON Lines — `["frame1","frame2"]` per line |
|
|
314
|
-
| `--msgpack` | MessagePack arrays (binary stream) |
|
|
315
|
-
| `-M` / `--marshal` | Ruby Marshal — one arbitrary Ruby object per message |
|
|
316
344
|
|
|
317
|
-
|
|
318
|
-
|
|
345
|
+
Display-only: the wire carries plain ZMTP frames. Multipart messages are
|
|
346
|
+
tab-separated in ASCII/quoted mode and encoded as JSON arrays in JSONL.
|
|
347
|
+
|
|
348
|
+
### Wire formats (on the ZMTP frame payload)
|
|
349
|
+
|
|
350
|
+
| Flag | Format |
|
|
351
|
+
|------|--------|
|
|
352
|
+
| `--raw` | Raw ZMTP binary (pipe to `hexdump -C` for debugging) |
|
|
353
|
+
| `--msgpack` | MessagePack — each frame is one packed object |
|
|
354
|
+
| `-M` / `--marshal` | Ruby Marshal — each frame is one arbitrary Ruby object |
|
|
355
|
+
|
|
356
|
+
Wire formats reshape the payload end-to-end: inside `-e`/`-E`, `it` is the
|
|
357
|
+
decoded object (not an Array of frames), so scalars, hashes, and custom
|
|
358
|
+
classes flow through transparently between peers speaking the same format.
|
|
319
359
|
|
|
320
360
|
```sh
|
|
321
361
|
# send multipart via tabs
|
|
@@ -326,12 +366,8 @@ echo '["key","value"]' | omq push -c tcp://localhost:5557 -J
|
|
|
326
366
|
omq pull -b tcp://:5557 -J
|
|
327
367
|
```
|
|
328
368
|
|
|
329
|
-
Under `-M`, each wire frame is one Marshal-dumped Ruby object. Inside `-e` / `-E`,
|
|
330
|
-
`it` is that raw object — not an Array of frames — so scalars, hashes, custom
|
|
331
|
-
classes, or any Marshal-safe value flow through transparently:
|
|
332
|
-
|
|
333
369
|
```sh
|
|
334
|
-
# send a bare String, receive a { string => encoding } Hash
|
|
370
|
+
# send a bare String with Marshal, receive a { string => encoding } Hash
|
|
335
371
|
omq push -b tcp://:5557 -ME '"foo"'
|
|
336
372
|
omq pull -c tcp://:5557 -M -e '{it => it.encoding}'
|
|
337
373
|
# => {"foo" => #<Encoding:UTF-8>}
|
|
@@ -413,8 +449,7 @@ omq rep -b tcp://:5555 --echo --curve-server --crypto nuckle
|
|
|
413
449
|
omq rep -b tcp://:5555 --echo --curve-server
|
|
414
450
|
|
|
415
451
|
# client (paste the key)
|
|
416
|
-
echo "secret" | omq req -c tcp://localhost:5555
|
|
417
|
-
--curve-server-key '<key from server>'
|
|
452
|
+
echo "secret" | omq req -c tcp://localhost:5555 --curve-server-key '<key from server>'
|
|
418
453
|
```
|
|
419
454
|
|
|
420
455
|
Persistent keys via env vars: `OMQ_SERVER_PUBLIC` + `OMQ_SERVER_SECRET` (server), `OMQ_SERVER_KEY` (client).
|
data/lib/omq/cli/base_runner.rb
CHANGED
|
@@ -289,7 +289,6 @@ module OMQ
|
|
|
289
289
|
loop do
|
|
290
290
|
parts = recv_msg
|
|
291
291
|
break if parts.nil?
|
|
292
|
-
trace_recv(parts)
|
|
293
292
|
parts = eval_recv_expr(parts)
|
|
294
293
|
output(parts)
|
|
295
294
|
i += 1
|
|
@@ -316,7 +315,6 @@ module OMQ
|
|
|
316
315
|
@recv_tick_eof = true
|
|
317
316
|
return 0
|
|
318
317
|
end
|
|
319
|
-
trace_recv(parts)
|
|
320
318
|
parts = eval_recv_expr(parts)
|
|
321
319
|
output(parts)
|
|
322
320
|
1
|
|
@@ -405,6 +403,7 @@ module OMQ
|
|
|
405
403
|
parts = Marshal.load(parts.first)
|
|
406
404
|
end
|
|
407
405
|
|
|
406
|
+
trace_recv(parts)
|
|
408
407
|
transient_ready!
|
|
409
408
|
parts
|
|
410
409
|
end
|
data/lib/omq/cli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omq-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.15.
|
|
4
|
+
version: 0.15.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrik Wenger
|
|
@@ -15,20 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0.
|
|
19
|
-
- - ">="
|
|
20
|
-
- !ruby/object:Gem::Version
|
|
21
|
-
version: 0.19.3
|
|
18
|
+
version: '0.22'
|
|
22
19
|
type: :runtime
|
|
23
20
|
prerelease: false
|
|
24
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
22
|
requirements:
|
|
26
23
|
- - "~>"
|
|
27
24
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: '0.
|
|
29
|
-
- - ">="
|
|
30
|
-
- !ruby/object:Gem::Version
|
|
31
|
-
version: 0.19.3
|
|
25
|
+
version: '0.22'
|
|
32
26
|
- !ruby/object:Gem::Dependency
|
|
33
27
|
name: omq-ffi
|
|
34
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -49,84 +43,84 @@ dependencies:
|
|
|
49
43
|
requirements:
|
|
50
44
|
- - "~>"
|
|
51
45
|
- !ruby/object:Gem::Version
|
|
52
|
-
version: '0.
|
|
46
|
+
version: '0.2'
|
|
53
47
|
type: :runtime
|
|
54
48
|
prerelease: false
|
|
55
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
56
50
|
requirements:
|
|
57
51
|
- - "~>"
|
|
58
52
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: '0.
|
|
53
|
+
version: '0.2'
|
|
60
54
|
- !ruby/object:Gem::Dependency
|
|
61
55
|
name: omq-rfc-radiodish
|
|
62
56
|
requirement: !ruby/object:Gem::Requirement
|
|
63
57
|
requirements:
|
|
64
58
|
- - "~>"
|
|
65
59
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '0.
|
|
60
|
+
version: '0.2'
|
|
67
61
|
type: :runtime
|
|
68
62
|
prerelease: false
|
|
69
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
70
64
|
requirements:
|
|
71
65
|
- - "~>"
|
|
72
66
|
- !ruby/object:Gem::Version
|
|
73
|
-
version: '0.
|
|
67
|
+
version: '0.2'
|
|
74
68
|
- !ruby/object:Gem::Dependency
|
|
75
69
|
name: omq-rfc-scattergather
|
|
76
70
|
requirement: !ruby/object:Gem::Requirement
|
|
77
71
|
requirements:
|
|
78
72
|
- - "~>"
|
|
79
73
|
- !ruby/object:Gem::Version
|
|
80
|
-
version: '0.
|
|
74
|
+
version: '0.2'
|
|
81
75
|
type: :runtime
|
|
82
76
|
prerelease: false
|
|
83
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
84
78
|
requirements:
|
|
85
79
|
- - "~>"
|
|
86
80
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: '0.
|
|
81
|
+
version: '0.2'
|
|
88
82
|
- !ruby/object:Gem::Dependency
|
|
89
83
|
name: omq-rfc-channel
|
|
90
84
|
requirement: !ruby/object:Gem::Requirement
|
|
91
85
|
requirements:
|
|
92
86
|
- - "~>"
|
|
93
87
|
- !ruby/object:Gem::Version
|
|
94
|
-
version: '0.
|
|
88
|
+
version: '0.2'
|
|
95
89
|
type: :runtime
|
|
96
90
|
prerelease: false
|
|
97
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
98
92
|
requirements:
|
|
99
93
|
- - "~>"
|
|
100
94
|
- !ruby/object:Gem::Version
|
|
101
|
-
version: '0.
|
|
95
|
+
version: '0.2'
|
|
102
96
|
- !ruby/object:Gem::Dependency
|
|
103
97
|
name: omq-rfc-p2p
|
|
104
98
|
requirement: !ruby/object:Gem::Requirement
|
|
105
99
|
requirements:
|
|
106
100
|
- - "~>"
|
|
107
101
|
- !ruby/object:Gem::Version
|
|
108
|
-
version: '0.
|
|
102
|
+
version: '0.2'
|
|
109
103
|
type: :runtime
|
|
110
104
|
prerelease: false
|
|
111
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
112
106
|
requirements:
|
|
113
107
|
- - "~>"
|
|
114
108
|
- !ruby/object:Gem::Version
|
|
115
|
-
version: '0.
|
|
109
|
+
version: '0.2'
|
|
116
110
|
- !ruby/object:Gem::Dependency
|
|
117
111
|
name: omq-rfc-zstd
|
|
118
112
|
requirement: !ruby/object:Gem::Requirement
|
|
119
113
|
requirements:
|
|
120
114
|
- - "~>"
|
|
121
115
|
- !ruby/object:Gem::Version
|
|
122
|
-
version: '0.
|
|
116
|
+
version: '0.3'
|
|
123
117
|
type: :runtime
|
|
124
118
|
prerelease: false
|
|
125
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
126
120
|
requirements:
|
|
127
121
|
- - "~>"
|
|
128
122
|
- !ruby/object:Gem::Version
|
|
129
|
-
version: '0.
|
|
123
|
+
version: '0.3'
|
|
130
124
|
- !ruby/object:Gem::Dependency
|
|
131
125
|
name: msgpack
|
|
132
126
|
requirement: !ruby/object:Gem::Requirement
|