omq-cli 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b1d3b2b127f70f0c59d6e008de4b4f94a0886012819953a3028ca346ecc1feb
4
- data.tar.gz: a1e912d9aec54b22e4467cb7b2e24c3765333feb5b5fd83cbfc2fa27e0b6a3b9
3
+ metadata.gz: 7962982bfc0731a3f52ef7de818d9b479f60ec3046002592f92d9aed601f3047
4
+ data.tar.gz: 961aea9c3ecb30189672dff9bb3c29539f001a615813c9b132529034201b9681
5
5
  SHA512:
6
- metadata.gz: e773ac7df2e7aab7212491ded8c85fe7b6591ec5c4f3553efcac5f23786f54db9fc20b25af5df835e4b0ae17e1117ad63178e3429e616fc18bf00a43f2ca8eee
7
- data.tar.gz: 6ef6c64204c54e18f8f3fced595ac4b0b3f36299f528cca981dc34876fa654a73c744be1324318a5fd0f2308f002d594779654facc143f5c19096d18534a39cf
6
+ metadata.gz: 3e0fe43bba8360dce4daf5c8c8d46ba05010afc8a9c6958c013f3769508ea7123936b4092ff81f42d0371cf385bffc0329995ffd3f55839047135021a750cdca
7
+ data.tar.gz: 3fe10d13fa4c7f079d99a89423e6d7fa6ac5fab7c121ddd3518ba021ff8668bea1a65473363cf5708b8921a7712741d923713d6c982e149efe24e564315b87c6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,163 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 — 2026-04-07
4
+
5
+ ### Fixed
6
+
7
+ - **Fix broken Gemfile** — remove deleted `omq-draft`, add all RFC gems
8
+ with `OMQ_DEV` path resolution.
9
+ - **Fix CURVE API calls** — `Curve.server` and `Curve.client` now use
10
+ keyword arguments to match the updated protocol-zmtp API.
11
+ - **Replace `require "omq/draft/all"`** with explicit RFC requires
12
+ (`omq/rfc/clientserver`, `radiodish`, `scattergather`, `channel`, `p2p`).
13
+
14
+ ### Changed
15
+
16
+ - YARD documentation on all public methods and classes.
17
+ - Code style: two blank lines between methods and constants.
18
+
19
+ ### Refactored
20
+
21
+ - **`req_rep.rb` + `pair.rb` method extraction** — `ReqRunner#run_loop` (23 lines)
22
+ gains `wait_for_interval`; `RepRunner#run_loop` (27 lines) gains
23
+ `handle_rep_request`; `PairRunner#run_loop` (20 lines) gains `recv_async`.
24
+ Each `run_loop` shrinks to ~10 lines.
25
+
26
+ - **`BaseRunner` method extraction** — `call` (23 lines), `wait_for_peer` (18),
27
+ `read_next` (20), `run_send_logic` (29), and `compile_expr` (13) each
28
+ decomposed into focused helpers: `setup_socket`, `maybe_start_transient_monitor`,
29
+ `run_begin_blocks`, `run_end_blocks`, `wait_for_subscriber`, `apply_grace_period`,
30
+ `read_inline_data`, `read_stdin_input`, `run_interval_send`, `run_stdin_send`,
31
+ `compile_evaluator`, `assign_send_aliases`, `assign_recv_aliases`.
32
+ Every public-facing method is now ≤10 lines.
33
+
34
+ - **`RoutingHelper`: extract `async_send_loop`, `interval_send_loop`, `stdin_send_loop`** —
35
+ the sender `task.async` block was identical in `RouterRunner#run_loop` and
36
+ `ServerRunner#monitor_loop`. Moved to `RoutingHelper` and split into three
37
+ focused helpers. Both callers reduced to a 3-line orchestration.
38
+ `ServerRunner#reply_loop` gained `handle_server_request` to hold the 3-branch
39
+ dispatch, leaving the loop itself at ~8 lines.
40
+
41
+ - **`pipe.rb` method extraction** — `run_sequential` (50 lines) and `run_parallel`
42
+ (105 lines) decomposed into focused helpers: `build_pull_push`,
43
+ `apply_socket_intervals`, `setup_sequential_transient`, `sequential_message_loop`,
44
+ `build_socket_pairs`, `wait_for_pairs`, `setup_parallel_transient`,
45
+ `build_worker_data`, `spawn_workers`, `join_workers`. Each caller shrinks to
46
+ ~8 lines.
47
+
48
+ - **Hoist `eval_proc` + `Integer#times` in Ractor worker loops** — in both
49
+ `ParallelRecvRunner` and `pipe.rb#spawn_workers`, `if eval_proc` was checked
50
+ on every message despite being invariant. Now a pre-loop branch splits into
51
+ two (or four, combined with `n_count`) separate loops. Count limits use
52
+ `n.times` instead of a manual `i` counter, eliminating the `n && n > 0`
53
+ check from every iteration.
54
+
55
+ - **`ExpressionEvaluator.normalize_result`** — the `case result when nil/Array/String/else`
56
+ block appeared in both Ractor worker bodies (`ParallelRecvRunner` and `pipe.rb`).
57
+ Extracted to a class method and both callers updated to use it.
58
+
59
+ - **Extract `ParallelRecvRunner`** — `BaseRunner#run_parallel_recv` (107 lines
60
+ of Ractor worker management) moved into `OMQ::CLI::ParallelRecvRunner` in
61
+ `lib/omq/cli/parallel_recv_runner.rb`. Constructor takes `klass, config, fmt,
62
+ output_fn`; `BaseRunner#run_parallel_recv` becomes a 4-line delegator.
63
+ `BaseRunner` shrinks from 426 to ~325 lines.
64
+
65
+ - **Extract `CliParser`** — `parse_options` (178 lines), `validate!` (48 lines),
66
+ `validate_gems!`, `DEFAULT_OPTS`, and the `EXAMPLES` heredoc (184 lines) moved
67
+ from the `OMQ::CLI` module into a dedicated `OMQ::CLI::CliParser` class in
68
+ `lib/omq/cli/cli_parser.rb`. `CLI.build_config` now calls `CliParser.parse`
69
+ and `CliParser.validate!`; `CLI.run_socket` calls `CliParser.validate_gems!`.
70
+ `cli.rb` shrinks from 674 to ~200 lines.
71
+
72
+ - **`ReqRunner#run_loop` deduplication** — the interval and non-interval
73
+ branches were identical loops; merged into one with a trailing
74
+ `sleep(wait)` gated on `config.interval`.
75
+ - **Remove empty runner subclasses** — `DealerRunner`, `ChannelRunner`,
76
+ `ClientRunner`, and `PeerRunner` were empty class bodies that added no
77
+ behaviour. `RUNNER_MAP` now points directly at `PairRunner`, `ReqRunner`,
78
+ and `ServerRunner` for those socket types. `channel.rb` and `peer.rb`
79
+ deleted; empty bodies removed from `router_dealer.rb` and
80
+ `client_server.rb`.
81
+ - **Extract `TransientMonitor`** — `start_disconnect_monitor`,
82
+ `transient_ready!`, and `@transient_barrier` removed from `BaseRunner`
83
+ into `OMQ::CLI::TransientMonitor`. `BaseRunner` holds a single
84
+ `@transient_monitor` collaborator; `transient_ready!` delegates to
85
+ `monitor.ready!`.
86
+ - **Extract `RoutingHelper` module** — `display_routing_id`, `resolve_target`,
87
+ and a `send_targeted_or_eval` template method (calling a `send_to_peer` hook)
88
+ extracted from `RouterRunner` and `ServerRunner` into a shared module.
89
+ `display_routing_id` and `resolve_target` removed from `BaseRunner`, which
90
+ never used them directly.
91
+ - **Extract `SocketSetup`** — socket construction (`SocketSetup.build`),
92
+ endpoint attachment (`SocketSetup.attach` for URL lists,
93
+ `SocketSetup.attach_endpoints` for `Endpoint` objects), subscription/group
94
+ setup (`SocketSetup.setup_subscriptions`), and CURVE configuration
95
+ (`SocketSetup.setup_curve`) extracted from `BaseRunner` into a stateless
96
+ module. `PipeRunner#attach_endpoints` now delegates to the shared module.
97
+ - **Extract `ExpressionEvaluator`** — expression compilation (`extract_block`,
98
+ `extract_blocks`, BEGIN/END parsing, proc wrapping) and result normalisation
99
+ live in `OMQ::CLI::ExpressionEvaluator`. Removes duplicate code from
100
+ `BaseRunner`, `PipeRunner`, and both Ractor worker blocks. A shared
101
+ `ExpressionEvaluator.compile_inside_ractor` class method replaces the
102
+ identical inline parse lambdas that previously appeared in each Ractor block.
103
+
104
+ ### Added (OMQ::Ractor integration)
105
+
106
+ - **`-P` extended to recv-only socket types** (`pull`, `sub`, `gather`,
107
+ `dish`) when combined with `--recv-eval`. Each worker gets its own
108
+ socket connecting to the external endpoint; ZMQ distributes work
109
+ naturally. Results are collected via an inproc PULL back to main for
110
+ output. Requires all endpoints to use `--connect`.
111
+ - **`omq-ractor` dependency** — `OMQ::Ractor` is now used for all
112
+ parallel worker management.
113
+
114
+ ### Changed
115
+
116
+ - **`pipe -P` rewritten using `OMQ::Ractor`** — workers no longer
117
+ create their own Async reactor internally. Sockets are created and
118
+ peer-waited in the main Async context, then passed to
119
+ `OMQ::Ractor.new`; worker blocks contain only pure computation.
120
+ Semantics are unchanged.
121
+
122
+ ### Fixed
123
+
124
+ - **`-P` recv-only: stale round-robin entry** — the initial socket
125
+ created by `call()` was connecting to the upstream endpoint before
126
+ `run_parallel_recv` closed it, leaving a dead entry in the PUSH
127
+ peer's round-robin cycle and silently dropping 1 in every N+1
128
+ messages. Fixed by skipping `attach_endpoints` when `config.parallel`
129
+ is set.
130
+ - **`-P` BEGIN/END blocks in Ractor workers** — `@ivar` expressions in
131
+ BEGIN/END/eval blocks raised `Ractor::IsolationError` because `self`
132
+ inside a Ractor is a shareable object. All three procs now execute via
133
+ `instance_exec` on a per-worker `Object.new` context.
134
+ - **`-P` END block result forwarded** — the return value of an END block
135
+ was discarded rather than forwarded to the output socket. Now captured
136
+ and pushed to `push_p`, enabling patterns like
137
+ `BEGIN{@s=0} @s+=Integer($F.first);nil END{[@s.to_s]}` to emit once
138
+ per worker on exit.
139
+
140
+ ---
141
+
142
+ ### Added
143
+
144
+ - **`-r` defers loading to inside the Async reactor** — scripts now run
145
+ within the event loop and may use async APIs, OMQ sockets, etc.
146
+ - **Bare script mode (`omq -r FILE`)** — omitting the socket type runs the
147
+ script directly inside `Async{}` with free reign. The `OMQ` module is
148
+ included into the top-level namespace so scripts can write `PUSH.new`
149
+ instead of `OMQ::PUSH.new`.
150
+ - **`-r -` / `-r-`** — reads and evals the script from stdin, useful for
151
+ quick copy-paste invocations. Cannot be combined with `-F -`.
152
+ - **`--recv-maxsz COUNT`** — sets the ZMQ `max_message_size` socket option;
153
+ the socket discards incoming messages exceeding `COUNT` bytes and drops
154
+ the peer connection before allocation.
155
+
156
+ ### Fixed
157
+
158
+ - `Protocol::ZMTP::Codec::Frame` was referenced as bare `ZMTP::Codec::Frame`
159
+ in the formatter (`--raw` format). Fixed the constant path.
160
+
3
161
  ## 0.2.0 — 2026-03-31
4
162
 
5
163
  ### Added
data/README.md CHANGED
@@ -4,7 +4,8 @@
4
4
  [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](LICENSE)
5
5
  [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%203.3-CC342D?logo=ruby&logoColor=white)](https://www.ruby-lang.org)
6
6
 
7
- Command-line tool for sending and receiving ZeroMQ messages on any socket type. Like `nngcat` from libnng, but with Ruby eval, Ractor parallelism, and message handlers.
7
+ Command-line tool for sending and receiving ZeroMQ messages on any socket type.
8
+ Like `nngcat` from libnng, but with Ruby eval, Ractor parallelism, and message handlers.
8
9
 
9
10
  Built on [omq](https://github.com/zeromq/omq) — pure Ruby ZeroMQ, no C dependencies.
10
11
 
@@ -46,7 +47,8 @@ omq pull -b ipc:///tmp/feed.sock # IPC (unix socket)
46
47
  omq push -c ipc://@abstract # IPC (abstract namespace, Linux)
47
48
  ```
48
49
 
49
- Multiple endpoints are allowed — `omq pull -b tcp://:5557 -b tcp://:5558` binds both. Pipe requires exactly two (`-c` for pull-side, `-c` for push-side).
50
+ Multiple endpoints are allowed — `omq pull -b tcp://:5557 -b tcp://:5558` binds both.
51
+ Pipe takes two positional endpoints (input, output) or uses `--in`/`--out` for multiple per side.
50
52
 
51
53
  ## Socket types
52
54
 
@@ -94,7 +96,8 @@ echo "hello" | omq req -c tcp://localhost:5555
94
96
  | `dealer` | Like `pair` but round-robin send to multiple peers |
95
97
  | `channel` | Like `pair` (draft, single-frame) |
96
98
 
97
- These spawn two concurrent tasks: a receiver (prints incoming) and a sender (reads stdin). `-e` transforms incoming, `-E` transforms outgoing.
99
+ These spawn two concurrent tasks: a receiver (prints incoming) and a sender (reads stdin).
100
+ `-e` transforms incoming, `-E` transforms outgoing.
98
101
 
99
102
  ### Routing sockets
100
103
 
@@ -128,7 +131,8 @@ omq pipe -c ipc://@work -c ipc://@sink -e '$F.map(&:upcase)'
128
131
  omq pipe -c ipc://@work -c ipc://@sink -P 4 -r./fib.rb -e 'fib(Integer($_)).to_s'
129
132
  ```
130
133
 
131
- The first endpoint is the pull-side (input), the second is the push-side (output). Both must use `-c`.
134
+ The first endpoint is the pull-side (input), the second is the push-side (output).
135
+ Both must use `-c`.
132
136
 
133
137
  ## Eval: -e and -E
134
138
 
@@ -298,7 +302,8 @@ OMQ.outgoing { |msg| [*msg, Time.now.iso8601] }
298
302
  | `--msgpack` | MessagePack arrays (binary stream) |
299
303
  | `-M` / `--marshal` | Ruby Marshal (binary stream of `Array<String>` objects) |
300
304
 
301
- Multipart messages: in ASCII/quoted mode, frames are tab-separated. In JSONL mode, each message is a JSON array.
305
+ Multipart messages: in ASCII/quoted mode, frames are tab-separated. In JSONL mode,
306
+ each message is a JSON array.
302
307
 
303
308
  ```sh
304
309
  # send multipart via tabs
@@ -432,7 +437,8 @@ omq pipe -c ipc://@work -c ipc://@sink --transient -e '$F.map(&:upcase)'
432
437
 
433
438
  ### Multi-peer pipe with `--in`/`--out`
434
439
 
435
- Use `--in` and `--out` to attach multiple endpoints per side. These are modal switches — subsequent `-b`/`-c` flags attach to the current side:
440
+ Use `--in` and `--out` to attach multiple endpoints per side. These are modal switches — subsequent
441
+ `-b`/`-c` flags attach to the current side:
436
442
 
437
443
  ```sh
438
444
  # fan-in: 2 producers → 1 consumer
@@ -448,9 +454,8 @@ omq pipe --in -b tcp://:5555 -b tcp://:5556 --out -c tcp://sink:5557 -e '$F'
448
454
  omq pipe --in -c ipc://@a -c ipc://@b --out -c ipc://@sink -P 4 -e '$F'
449
455
  ```
450
456
 
451
- `-P`/`--parallel` requires all endpoints to be `--connect`. In parallel mode, each Ractor worker gets its own PULL/PUSH pair connecting to all endpoints.
452
-
453
- Note: in Ractor workers, use `__F` instead of `$F` (global variables aren't shared across Ractors).
457
+ `-P`/`--parallel` requires all endpoints to be `--connect`. In parallel mode, each Ractor worker
458
+ gets its own PULL/PUSH pair connecting to all endpoints.
454
459
 
455
460
  ## Transient mode
456
461