nnq-cli 0.3.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/lib/nnq/cli/base_runner.rb +64 -23
- data/lib/nnq/cli/cli_parser.rb +32 -35
- data/lib/nnq/cli/expression_evaluator.rb +41 -50
- data/lib/nnq/cli/formatter.rb +19 -27
- data/lib/nnq/cli/pipe.rb +2 -4
- data/lib/nnq/cli/pipe_worker.rb +5 -9
- data/lib/nnq/cli/req_rep.rb +20 -12
- data/lib/nnq/cli/socket_setup.rb +17 -10
- data/lib/nnq/cli/surveyor_respondent.rb +6 -6
- data/lib/nnq/cli/term.rb +2 -2
- data/lib/nnq/cli/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cfc34e5b7774c4c3288d440d5172391b098055fd2c376d3827e8a5944dd085d7
|
|
4
|
+
data.tar.gz: '0836095278d158e9b52234060cbfe111abcdc282a31659480ff2eb9db0bad5e8'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d34031ee7de234d6f1f88305b8cbd7699932216bec86bb6f05191e6001608e899ff29160934cc9c9a34b9c8fc71c7bff2d74014904a85753bd63d947bf88f78b
|
|
7
|
+
data.tar.gz: 84a46b47822df0538eb9a875142b01893370c3222b22728ede2dfb1578354cd21775f22bc343daaded4bc60ba56f99af6a44dd0edb40f4181cab44040cc45fc7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 — 2026-04-16
|
|
4
|
+
|
|
5
|
+
- **`-P0` auto-sizes to `nproc`** (capped at 16). `nnq pipe -P 0`
|
|
6
|
+
now spawns one Ractor worker per online processor instead of
|
|
7
|
+
erroring. Explicit values still clamp to `1..16`.
|
|
8
|
+
|
|
9
|
+
## 0.3.2 — 2026-04-16
|
|
10
|
+
|
|
11
|
+
- **REQ generator mode (`-E`/`-e` with no stdin).** `nnq req` now
|
|
12
|
+
produces each request from the send-eval alone when no `-d`/`-F`
|
|
13
|
+
is given and stdin is not piped, matching the existing PUSH/PUB
|
|
14
|
+
generator behaviour. Bounded by `-n` or paced by `-i` like the
|
|
15
|
+
other generator-capable runners.
|
|
16
|
+
- **System tests split into themed files under `test/system/`.**
|
|
17
|
+
Replaces the monolithic `test/system_test.sh` with 10 standalone
|
|
18
|
+
files sharing `test/system/support.sh` helpers.
|
|
19
|
+
`test/system/run_all.sh` chains them; each file also runs
|
|
20
|
+
standalone. New `rake test:system` task invokes `run_all.sh`.
|
|
21
|
+
- **System tests for REQ/PUSH/PUB `-E` generator mode.** REQ fires
|
|
22
|
+
`-E'"foo"' -n 3` against a REP running `-e 'it.upcase' -n 3` and
|
|
23
|
+
verifies three `FOO` replies round-trip; PUSH and PUB get the
|
|
24
|
+
same treatment.
|
|
25
|
+
|
|
26
|
+
## 0.3.1 — 2026-04-15
|
|
27
|
+
|
|
28
|
+
- **Messages are single `String`s, not 1-element arrays.** Every
|
|
29
|
+
runner, the formatter, and the expression evaluator used to wrap
|
|
30
|
+
each message body in a one-element array as a historical
|
|
31
|
+
artifact of multipart thinking. That's gone now: `Formatter#pack`/
|
|
32
|
+
`#unpack` take and return a `String`, `Formatter.preview` takes a
|
|
33
|
+
`String`, and REQ/REP/SURVEYOR/RESPONDENT/pipe runners pass the
|
|
34
|
+
body straight through without `.first` or array literals. Fixes
|
|
35
|
+
an `undefined method 'first' for an instance of String` crash in
|
|
36
|
+
`ReqRunner#request_and_receive` that was masked by the tests.
|
|
37
|
+
- **`-e` / `-E` expressions use Ruby 3.4's `it` default block
|
|
38
|
+
variable** (and accept explicit `|msg|` block-parameter syntax
|
|
39
|
+
via `proc { |msg| ... }`). The old implicit `$_`/`$F` aliasing is
|
|
40
|
+
gone; expressions are compiled as plain `proc { EXPR }` and
|
|
41
|
+
evaluated via `instance_exec(msg, &block)`, so `it.upcase` and
|
|
42
|
+
`|msg| msg.upcase` both work. Cleaner, faster, and no global
|
|
43
|
+
state.
|
|
44
|
+
- **Requires nnq >= 0.6.1** for the `-vvv` trace fixes — cooked
|
|
45
|
+
REQ/REP/RESPONDENT now emit `>>` lines and recv previews strip
|
|
46
|
+
the SP backtrace header so you see the actual payload.
|
|
47
|
+
- **`test/system_test.sh`** — new shell-based system test suite
|
|
48
|
+
modeled on omq-cli's, covering REQ/REP (basic, `--echo`, `-e`),
|
|
49
|
+
the ported `-vvv` REQ/REP verbose-trace assertion from omq-cli
|
|
50
|
+
commit `950890911de078`, PUSH/PULL, PUB/SUB, and the `@name`
|
|
51
|
+
abstract-namespace shortcut for both `-b` and `-c`.
|
|
52
|
+
|
|
3
53
|
## 0.3.0 — 2026-04-15
|
|
4
54
|
|
|
5
55
|
- **Compression switched from LZ4 to Zstd** via the new
|
data/lib/nnq/cli/base_runner.rb
CHANGED
|
@@ -5,14 +5,15 @@ module NNQ
|
|
|
5
5
|
# Template runner base class for all socket-type CLI runners.
|
|
6
6
|
# Subclasses override {#run_loop} to implement socket-specific behaviour.
|
|
7
7
|
#
|
|
8
|
-
# nnq carries one String body per message (no multipart).
|
|
9
|
-
#
|
|
10
|
-
# expressions using `$F`/`$_` work the same way as in omq-cli, and
|
|
11
|
-
# unwraps to a bare String at the send boundary.
|
|
8
|
+
# nnq carries one String body per message (no multipart).
|
|
9
|
+
#
|
|
12
10
|
class BaseRunner
|
|
13
11
|
# @return [Config] frozen CLI configuration
|
|
12
|
+
attr_reader :config
|
|
13
|
+
|
|
14
|
+
|
|
14
15
|
# @return [Object] the NNQ socket instance
|
|
15
|
-
attr_reader :
|
|
16
|
+
attr_reader :sock
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
# @param config [Config] frozen CLI configuration
|
|
@@ -145,6 +146,7 @@ module NNQ
|
|
|
145
146
|
# latecomers finish their handshake before we start sending.
|
|
146
147
|
def apply_grace_period
|
|
147
148
|
return unless config.binds.any? || config.connects.size > 1
|
|
149
|
+
|
|
148
150
|
ri = @sock.options.reconnect_interval
|
|
149
151
|
sleep(ri.is_a?(Range) ? ri.begin : ri)
|
|
150
152
|
end
|
|
@@ -163,7 +165,9 @@ module NNQ
|
|
|
163
165
|
|
|
164
166
|
def run_send_logic
|
|
165
167
|
n = config.count
|
|
166
|
-
|
|
168
|
+
|
|
169
|
+
sleep config.delay if config.delay
|
|
170
|
+
|
|
167
171
|
if config.interval
|
|
168
172
|
run_interval_send(n)
|
|
169
173
|
elsif config.data || config.file
|
|
@@ -189,21 +193,30 @@ module NNQ
|
|
|
189
193
|
|
|
190
194
|
def run_interval_send(n)
|
|
191
195
|
i = send_tick
|
|
192
|
-
|
|
196
|
+
|
|
197
|
+
if @send_tick_eof || (n && n > 0 && i >= n)
|
|
198
|
+
return
|
|
199
|
+
end
|
|
200
|
+
|
|
193
201
|
Async::Loop.quantized(interval: config.interval) do
|
|
194
202
|
i += send_tick
|
|
195
|
-
|
|
203
|
+
|
|
204
|
+
if @send_tick_eof || (n && n > 0 && i >= n)
|
|
205
|
+
break
|
|
206
|
+
end
|
|
196
207
|
end
|
|
197
208
|
end
|
|
198
209
|
|
|
199
210
|
|
|
200
211
|
def run_stdin_send(n)
|
|
201
212
|
i = 0
|
|
213
|
+
|
|
202
214
|
loop do
|
|
203
|
-
msg = read_next
|
|
204
|
-
break unless msg
|
|
215
|
+
msg = read_next or break
|
|
205
216
|
msg = eval_send_expr(msg)
|
|
217
|
+
|
|
206
218
|
send_msg(msg) if msg
|
|
219
|
+
|
|
207
220
|
i += 1
|
|
208
221
|
break if n && n > 0 && i >= n
|
|
209
222
|
end
|
|
@@ -212,6 +225,7 @@ module NNQ
|
|
|
212
225
|
|
|
213
226
|
def send_tick
|
|
214
227
|
raw = read_next_or_nil
|
|
228
|
+
|
|
215
229
|
if raw.nil?
|
|
216
230
|
if @send_eval_proc && !@stdin_ready
|
|
217
231
|
# Pure generator mode: no stdin, eval produces output from nothing.
|
|
@@ -219,9 +233,11 @@ module NNQ
|
|
|
219
233
|
send_msg(msg) if msg
|
|
220
234
|
return 1
|
|
221
235
|
end
|
|
236
|
+
|
|
222
237
|
@send_tick_eof = true
|
|
223
238
|
return 0
|
|
224
239
|
end
|
|
240
|
+
|
|
225
241
|
msg = eval_send_expr(raw)
|
|
226
242
|
send_msg(msg) if msg
|
|
227
243
|
1
|
|
@@ -231,14 +247,16 @@ module NNQ
|
|
|
231
247
|
def run_recv_logic
|
|
232
248
|
n = config.count
|
|
233
249
|
i = 0
|
|
250
|
+
|
|
234
251
|
if config.interval
|
|
235
252
|
run_interval_recv(n)
|
|
236
253
|
else
|
|
237
254
|
loop do
|
|
238
|
-
msg = recv_msg
|
|
239
|
-
break if msg.nil?
|
|
255
|
+
msg = recv_msg or break
|
|
240
256
|
msg = eval_recv_expr(msg)
|
|
257
|
+
|
|
241
258
|
output(msg)
|
|
259
|
+
|
|
242
260
|
i += 1
|
|
243
261
|
break if n && n > 0 && i >= n
|
|
244
262
|
end
|
|
@@ -248,21 +266,28 @@ module NNQ
|
|
|
248
266
|
|
|
249
267
|
def run_interval_recv(n)
|
|
250
268
|
i = recv_tick
|
|
269
|
+
|
|
251
270
|
return if i == 0
|
|
252
271
|
return if n && n > 0 && i >= n
|
|
272
|
+
|
|
253
273
|
Async::Loop.quantized(interval: config.interval) do
|
|
254
274
|
i += recv_tick
|
|
255
|
-
|
|
275
|
+
|
|
276
|
+
if @recv_tick_eof || (n && n > 0 && i >= n)
|
|
277
|
+
break
|
|
278
|
+
end
|
|
256
279
|
end
|
|
257
280
|
end
|
|
258
281
|
|
|
259
282
|
|
|
260
283
|
def recv_tick
|
|
261
284
|
msg = recv_msg
|
|
285
|
+
|
|
262
286
|
if msg.nil?
|
|
263
287
|
@recv_tick_eof = true
|
|
264
288
|
return 0
|
|
265
289
|
end
|
|
290
|
+
|
|
266
291
|
msg = eval_recv_expr(msg)
|
|
267
292
|
output(msg)
|
|
268
293
|
1
|
|
@@ -286,22 +311,32 @@ module NNQ
|
|
|
286
311
|
# -- Message I/O -------------------------------------------------
|
|
287
312
|
|
|
288
313
|
|
|
289
|
-
# msg
|
|
314
|
+
# @param msg [String]
|
|
315
|
+
#
|
|
290
316
|
def send_msg(msg)
|
|
291
|
-
|
|
292
|
-
|
|
317
|
+
body = msg
|
|
318
|
+
|
|
319
|
+
case config.format
|
|
320
|
+
when :marshal
|
|
321
|
+
body = Marshal.dump(msg)
|
|
322
|
+
end
|
|
323
|
+
|
|
293
324
|
@sock.send(body)
|
|
294
325
|
transient_ready!
|
|
295
326
|
end
|
|
296
327
|
|
|
297
328
|
|
|
298
|
-
# @return [
|
|
329
|
+
# @return [String, nil] message body, or nil on close.
|
|
299
330
|
def recv_msg
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
331
|
+
msg = @sock.receive or return
|
|
332
|
+
|
|
333
|
+
case config.format
|
|
334
|
+
when :marshal
|
|
335
|
+
msg = Marshal.load msg
|
|
336
|
+
end
|
|
337
|
+
|
|
303
338
|
transient_ready!
|
|
304
|
-
|
|
339
|
+
msg
|
|
305
340
|
end
|
|
306
341
|
|
|
307
342
|
|
|
@@ -328,7 +363,7 @@ module NNQ
|
|
|
328
363
|
@fmt.decode_marshal($stdin)
|
|
329
364
|
when :raw
|
|
330
365
|
data = $stdin.read
|
|
331
|
-
data.nil? || data.empty? ? nil :
|
|
366
|
+
data.nil? || data.empty? ? nil : data
|
|
332
367
|
else
|
|
333
368
|
line = $stdin.gets
|
|
334
369
|
line.nil? ? nil : @fmt.decode(line)
|
|
@@ -359,6 +394,7 @@ module NNQ
|
|
|
359
394
|
|
|
360
395
|
def output(msg)
|
|
361
396
|
return if config.quiet || msg.nil?
|
|
397
|
+
|
|
362
398
|
$stdout.write(@fmt.encode(msg))
|
|
363
399
|
$stdout.flush
|
|
364
400
|
end
|
|
@@ -416,9 +452,11 @@ module NNQ
|
|
|
416
452
|
title = ["nnq", config.type_name]
|
|
417
453
|
title << (config.compress == :balanced ? "-Z" : "-z") if config.compress
|
|
418
454
|
title << "-P#{config.parallel}" if config.parallel
|
|
455
|
+
|
|
419
456
|
eps.each do |ep|
|
|
420
457
|
title << (ep.respond_to?(:url) ? ep.url : ep.to_s)
|
|
421
458
|
end
|
|
459
|
+
|
|
422
460
|
Process.setproctitle(title.join(" "))
|
|
423
461
|
end
|
|
424
462
|
|
|
@@ -428,6 +466,7 @@ module NNQ
|
|
|
428
466
|
|
|
429
467
|
def log(msg)
|
|
430
468
|
return unless config.verbose >= 1
|
|
469
|
+
|
|
431
470
|
$stderr.write("#{Term.log_prefix(config.verbose)}nnq: #{msg}\n")
|
|
432
471
|
end
|
|
433
472
|
|
|
@@ -437,11 +476,13 @@ module NNQ
|
|
|
437
476
|
# -vvvv: prepend ISO8601 timestamps
|
|
438
477
|
def start_event_monitor
|
|
439
478
|
verbose = config.verbose >= 3
|
|
440
|
-
v
|
|
479
|
+
v = config.verbose
|
|
480
|
+
|
|
441
481
|
@sock.monitor(verbose: verbose) do |event|
|
|
442
482
|
CLI::Term.write_event(event, v)
|
|
443
483
|
end
|
|
444
484
|
end
|
|
485
|
+
|
|
445
486
|
end
|
|
446
487
|
end
|
|
447
488
|
end
|
data/lib/nnq/cli/cli_parser.rb
CHANGED
|
@@ -10,13 +10,13 @@ module NNQ
|
|
|
10
10
|
EXAMPLES = <<~'TEXT'
|
|
11
11
|
-- Request / Reply ------------------------------------------
|
|
12
12
|
|
|
13
|
-
+-----+
|
|
13
|
+
+-----+ "hello" +-----+
|
|
14
14
|
| REQ |------------->| REP |
|
|
15
15
|
| |<-------------| |
|
|
16
|
-
+-----+
|
|
16
|
+
+-----+ "HELLO" +-----+
|
|
17
17
|
|
|
18
18
|
# terminal 1: echo server
|
|
19
|
-
nnq rep --bind tcp://:5555 --recv-eval '
|
|
19
|
+
nnq rep --bind tcp://:5555 --recv-eval 'it.upcase'
|
|
20
20
|
|
|
21
21
|
# terminal 2: send a request
|
|
22
22
|
echo "hello" | nnq req --connect tcp://localhost:5555
|
|
@@ -27,9 +27,9 @@ module NNQ
|
|
|
27
27
|
|
|
28
28
|
-- Publish / Subscribe --------------------------------------
|
|
29
29
|
|
|
30
|
-
+-----+
|
|
30
|
+
+-----+ "weather.nyc 72F" +-----+
|
|
31
31
|
| PUB |--------------------->| SUB | --subscribe "weather."
|
|
32
|
-
+-----+
|
|
32
|
+
+-----+ +-----+
|
|
33
33
|
|
|
34
34
|
# terminal 1: subscriber (all topics by default)
|
|
35
35
|
nnq sub --bind tcp://:5556
|
|
@@ -39,9 +39,9 @@ module NNQ
|
|
|
39
39
|
|
|
40
40
|
-- Periodic Publish -------------------------------------------
|
|
41
41
|
|
|
42
|
-
+-----+
|
|
42
|
+
+-----+ "tick 1" +-----+
|
|
43
43
|
| PUB |--(every 1s)-->| SUB |
|
|
44
|
-
+-----+
|
|
44
|
+
+-----+ +-----+
|
|
45
45
|
|
|
46
46
|
# terminal 1: subscriber
|
|
47
47
|
nnq sub --bind tcp://:5556
|
|
@@ -56,9 +56,9 @@ module NNQ
|
|
|
56
56
|
|
|
57
57
|
-- Pipeline -------------------------------------------------
|
|
58
58
|
|
|
59
|
-
+------+
|
|
59
|
+
+------+ +------+
|
|
60
60
|
| PUSH |----------->| PULL |
|
|
61
|
-
+------+
|
|
61
|
+
+------+ +------+
|
|
62
62
|
|
|
63
63
|
# terminal 1: worker
|
|
64
64
|
nnq pull --bind tcp://:5557
|
|
@@ -72,30 +72,30 @@ module NNQ
|
|
|
72
72
|
|
|
73
73
|
-- Pipe (PULL -> eval -> PUSH) --------------------------------
|
|
74
74
|
|
|
75
|
-
+------+
|
|
75
|
+
+------+ +------+ +------+
|
|
76
76
|
| PUSH |--------->| pipe |--------->| PULL |
|
|
77
|
-
+------+
|
|
77
|
+
+------+ +------+ +------+
|
|
78
78
|
|
|
79
79
|
# terminal 1: producer
|
|
80
80
|
echo -e "hello\nworld" | nnq push --bind ipc://@work
|
|
81
81
|
|
|
82
82
|
# terminal 2: worker -- uppercase each message
|
|
83
|
-
nnq pipe -c ipc://@work -c ipc://@sink -e '
|
|
83
|
+
nnq pipe -c ipc://@work -c ipc://@sink -e 'it.upcase'
|
|
84
84
|
# terminal 3: collector
|
|
85
85
|
nnq pull --bind ipc://@sink
|
|
86
86
|
|
|
87
87
|
# 4 Ractor workers in a single process (-P)
|
|
88
|
-
nnq pipe -c ipc://@work -c ipc://@sink -P4 -r./fib -e 'fib(Integer(
|
|
88
|
+
nnq pipe -c ipc://@work -c ipc://@sink -P4 -r./fib -e 'fib(Integer(it)).to_s'
|
|
89
89
|
|
|
90
90
|
# exit when producer disconnects (--transient)
|
|
91
|
-
nnq pipe -c ipc://@work -c ipc://@sink --transient -e '
|
|
91
|
+
nnq pipe -c ipc://@work -c ipc://@sink --transient -e 'it.upcase'
|
|
92
92
|
|
|
93
93
|
# fan-in: multiple sources -> one sink
|
|
94
94
|
nnq pipe --in -c ipc://@work1 -c ipc://@work2 \
|
|
95
|
-
--out -c ipc://@sink -e '
|
|
95
|
+
--out -c ipc://@sink -e 'it.upcase'
|
|
96
96
|
|
|
97
97
|
# fan-out: one source -> multiple sinks (round-robin)
|
|
98
|
-
nnq pipe --in -b tcp://:5555 --out -c ipc://@sink1 -c ipc://@sink2 -e '
|
|
98
|
+
nnq pipe --in -b tcp://:5555 --out -c ipc://@sink1 -c ipc://@sink2 -e 'it'
|
|
99
99
|
|
|
100
100
|
-- Formats --------------------------------------------------
|
|
101
101
|
|
|
@@ -105,10 +105,6 @@ module NNQ
|
|
|
105
105
|
# quoted -- lossless, round-trippable (uses String#dump escaping)
|
|
106
106
|
nnq pull --bind tcp://:5557 --quoted
|
|
107
107
|
|
|
108
|
-
# JSON Lines -- single-element arrays (nnq is single-body)
|
|
109
|
-
echo '["payload"]' | nnq push --connect tcp://localhost:5557 --jsonl
|
|
110
|
-
nnq pull --bind tcp://:5557 --jsonl
|
|
111
|
-
|
|
112
108
|
# raw -- emit the body verbatim (no framing, no newline)
|
|
113
109
|
nnq pull --bind tcp://:5557 --raw
|
|
114
110
|
|
|
@@ -121,25 +117,25 @@ module NNQ
|
|
|
121
117
|
-- Ruby Eval ------------------------------------------------
|
|
122
118
|
|
|
123
119
|
# filter incoming: only pass messages containing "error"
|
|
124
|
-
nnq pull -b tcp://:5557 --recv-eval '
|
|
120
|
+
nnq pull -b tcp://:5557 --recv-eval 'it.include?("error") ? it : nil'
|
|
125
121
|
|
|
126
122
|
# transform incoming with gems
|
|
127
|
-
nnq sub -c tcp://localhost:5556 -rjson -e 'JSON.parse(
|
|
123
|
+
nnq sub -c tcp://localhost:5556 -rjson -e 'JSON.parse(it)["temperature"]'
|
|
128
124
|
|
|
129
125
|
# require a local file, use its methods
|
|
130
|
-
nnq rep --bind tcp://:5555 --require ./transform.rb -e '
|
|
126
|
+
nnq rep --bind tcp://:5555 --require ./transform.rb -e 'transform(it)'
|
|
131
127
|
|
|
132
|
-
# next skips, break stops
|
|
133
|
-
nnq pull -b tcp://:5557 -e 'next if /^#/; break if /quit/;
|
|
128
|
+
# next skips, break stops
|
|
129
|
+
nnq pull -b tcp://:5557 -e 'next if /^#/; break if it =~ /quit/; it'
|
|
134
130
|
|
|
135
131
|
# BEGIN/END blocks (like awk) -- accumulate and summarize
|
|
136
|
-
nnq pull -b tcp://:5557 -e 'BEGIN{@sum = 0} @sum += Integer(
|
|
132
|
+
nnq pull -b tcp://:5557 -e 'BEGIN{@sum = 0} @sum += Integer(it); nil END{puts @sum}'
|
|
137
133
|
|
|
138
|
-
# transform outgoing messages
|
|
139
|
-
echo hello | nnq push -c tcp://localhost:5557 --send-eval '
|
|
134
|
+
# transform outgoing messages (with explicit block variable msg)
|
|
135
|
+
echo hello | nnq push -c tcp://localhost:5557 --send-eval '|msg| msg.upcase'
|
|
140
136
|
|
|
141
137
|
# REQ: transform request and reply independently
|
|
142
|
-
echo hello | nnq req -c tcp://localhost:5555 -E '
|
|
138
|
+
echo hello | nnq req -c tcp://localhost:5555 -E 'it.upcase' -e 'it'
|
|
143
139
|
|
|
144
140
|
-- Script Handlers (-r) ------------------------------------
|
|
145
141
|
|
|
@@ -150,7 +146,7 @@ module NNQ
|
|
|
150
146
|
nnq pull --bind tcp://:5557 -r./handler.rb
|
|
151
147
|
|
|
152
148
|
# combine script handlers with inline eval
|
|
153
|
-
nnq req -c tcp://localhost:5555 -r./handler.rb -E '
|
|
149
|
+
nnq req -c tcp://localhost:5555 -r./handler.rb -E 'it.upcase'
|
|
154
150
|
|
|
155
151
|
# NNQ.outgoing { |msg| ... } -- registered outgoing transform
|
|
156
152
|
# NNQ.incoming { |msg| ... } -- registered incoming transform
|
|
@@ -268,7 +264,6 @@ module NNQ
|
|
|
268
264
|
o.on("-A", "--ascii", "Safe ASCII, non-printable as dots (default)") { opts[:format] = :ascii }
|
|
269
265
|
o.on("-Q", "--quoted", "C-style quoted with escapes") { opts[:format] = :quoted }
|
|
270
266
|
o.on( "--raw", "Raw binary body, no framing, no newline") { opts[:format] = :raw }
|
|
271
|
-
o.on("-J", "--jsonl", "JSON Lines (single-element array per line)") { opts[:format] = :jsonl }
|
|
272
267
|
o.on( "--msgpack", "MessagePack (binary stream)") { require "msgpack"; opts[:format] = :msgpack }
|
|
273
268
|
o.on("-M", "--marshal", "Ruby Marshal stream (binary)") { opts[:format] = :marshal }
|
|
274
269
|
|
|
@@ -316,14 +311,16 @@ module NNQ
|
|
|
316
311
|
end
|
|
317
312
|
|
|
318
313
|
o.separator "\nProcessing (-e = incoming, -E = outgoing):"
|
|
319
|
-
o.on("-e", "--recv-eval EXPR", "Eval Ruby for each incoming message (
|
|
320
|
-
o.on("-E", "--send-eval EXPR", "Eval Ruby for each outgoing message (
|
|
314
|
+
o.on("-e", "--recv-eval EXPR", "Eval Ruby for each incoming message (it = msg)") { |v| opts[:recv_expr] = v }
|
|
315
|
+
o.on("-E", "--send-eval EXPR", "Eval Ruby for each outgoing message (it = msg)") { |v| opts[:send_expr] = v }
|
|
321
316
|
o.on("-r", "--require LIB", "Require lib/file in Async context; use '-' for stdin. Scripts can register NNQ.outgoing/incoming") { |v|
|
|
322
317
|
require "nnq" unless defined?(NNQ::VERSION)
|
|
323
318
|
opts[:scripts] << (v == "-" ? :stdin : (v.start_with?("./", "../") ? File.expand_path(v) : v))
|
|
324
319
|
}
|
|
325
|
-
o.on("-P", "--parallel N", Integer, "Parallel Ractor workers (
|
|
326
|
-
|
|
320
|
+
o.on("-P", "--parallel N", Integer, "Parallel Ractor workers, 1..16 (0 = nproc, capped at 16)") { |v|
|
|
321
|
+
require "etc"
|
|
322
|
+
resolved = v.zero? ? Etc.nprocessors : v
|
|
323
|
+
opts[:parallel] = [resolved, 16].min
|
|
327
324
|
}
|
|
328
325
|
|
|
329
326
|
o.separator "\nOther:"
|
|
@@ -8,8 +8,10 @@ module NNQ
|
|
|
8
8
|
#
|
|
9
9
|
# One instance per direction (send or recv).
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
11
|
+
# The expression sees the message body via the default block
|
|
12
|
+
# variable `it`, or callers can declare an explicit parameter with
|
|
13
|
+
# block-literal syntax: `-e '|msg| msg.upcase'` compiles to
|
|
14
|
+
# `proc { |msg| msg.upcase }`.
|
|
13
15
|
#
|
|
14
16
|
class ExpressionEvaluator
|
|
15
17
|
attr_reader :begin_proc, :end_proc, :eval_proc
|
|
@@ -28,34 +30,30 @@ module NNQ
|
|
|
28
30
|
|
|
29
31
|
if src
|
|
30
32
|
expr, begin_body, end_body = extract_blocks(src)
|
|
31
|
-
@begin_proc = eval("proc { #{begin_body} }") if begin_body
|
|
32
|
-
@end_proc = eval("proc { #{end_body} }") if end_body
|
|
33
|
+
@begin_proc = eval("proc { #{begin_body} }") if begin_body
|
|
34
|
+
@end_proc = eval("proc { #{end_body} }") if end_body
|
|
35
|
+
|
|
33
36
|
if expr && !expr.strip.empty?
|
|
34
|
-
@eval_proc = eval("proc {
|
|
37
|
+
@eval_proc = eval("proc { #{expr} }")
|
|
35
38
|
end
|
|
36
39
|
elsif fallback_proc
|
|
37
|
-
@eval_proc = proc { |msg|
|
|
38
|
-
body = msg&.first
|
|
39
|
-
$_ = body
|
|
40
|
-
fallback_proc.call(body)
|
|
41
|
-
}
|
|
40
|
+
@eval_proc = proc { |msg| fallback_proc.call(msg) }
|
|
42
41
|
end
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
|
|
46
45
|
# Runs the eval proc against +msg+ using +context+ as self.
|
|
47
|
-
# Returns the normalised result
|
|
46
|
+
# Returns the normalised result (as String), nil (filter/skip), or SENT.
|
|
47
|
+
#
|
|
48
48
|
def call(msg, context)
|
|
49
49
|
return msg unless @eval_proc
|
|
50
50
|
|
|
51
|
-
$F = msg
|
|
52
51
|
result = context.instance_exec(msg, &@eval_proc)
|
|
53
52
|
return nil if result.nil?
|
|
54
53
|
return SENT if result.equal?(context)
|
|
55
|
-
return
|
|
54
|
+
return result if @format == :marshal
|
|
56
55
|
|
|
57
|
-
result
|
|
58
|
-
result.map!(&:to_s)
|
|
56
|
+
result.to_s
|
|
59
57
|
rescue => e
|
|
60
58
|
$stderr.puts "nnq: eval error: #{e.message} (#{e.class})"
|
|
61
59
|
exit 3
|
|
@@ -66,8 +64,7 @@ module NNQ
|
|
|
66
64
|
# Used inside Ractor worker blocks.
|
|
67
65
|
def self.normalize_result(result)
|
|
68
66
|
return nil if result.nil?
|
|
69
|
-
result
|
|
70
|
-
result.map!(&:to_s)
|
|
67
|
+
result.to_s
|
|
71
68
|
end
|
|
72
69
|
|
|
73
70
|
|
|
@@ -77,52 +74,34 @@ module NNQ
|
|
|
77
74
|
def self.compile_inside_ractor(src)
|
|
78
75
|
return [nil, nil, nil] unless src
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return [expr, nil] unless s
|
|
83
|
-
ci = expr.index("{", s)
|
|
84
|
-
depth = 1
|
|
85
|
-
j = ci + 1
|
|
86
|
-
while j < expr.length && depth > 0
|
|
87
|
-
depth += 1 if expr[j] == "{"
|
|
88
|
-
depth -= 1 if expr[j] == "}"
|
|
89
|
-
j += 1
|
|
90
|
-
end
|
|
91
|
-
[expr[0...s] + expr[j..], expr[(ci + 1)..(j - 2)]]
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
expr, begin_body = extract.(src, "BEGIN")
|
|
95
|
-
expr, end_body = extract.(expr, "END")
|
|
77
|
+
expr, begin_body = extract_block(src, "BEGIN")
|
|
78
|
+
expr, end_body = extract_block(expr, "END")
|
|
96
79
|
|
|
97
|
-
begin_proc = eval("proc { #{begin_body} }") if begin_body
|
|
98
|
-
end_proc = eval("proc { #{end_body} }") if end_body
|
|
80
|
+
begin_proc = eval("proc { #{begin_body} }") if begin_body
|
|
81
|
+
end_proc = eval("proc { #{end_body} }") if end_body
|
|
99
82
|
eval_proc = nil
|
|
83
|
+
|
|
100
84
|
if expr && !expr.strip.empty?
|
|
101
|
-
|
|
102
|
-
eval_proc = eval("proc { |__F| $_ = __F&.first; #{ractor_expr} }") # rubocop:disable Security/Eval
|
|
85
|
+
eval_proc = eval("proc { #{expr} }")
|
|
103
86
|
end
|
|
104
87
|
|
|
105
88
|
[begin_proc, end_proc, eval_proc]
|
|
106
89
|
end
|
|
107
90
|
|
|
108
91
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def extract_block(expr, keyword)
|
|
120
|
-
start = expr.index(/#{keyword}\s*\{/)
|
|
121
|
-
return [expr, nil] unless start
|
|
92
|
+
# Strips a +BEGIN {...}+ or +END {...}+ block from +expr+ and
|
|
93
|
+
# returns +[trimmed_expr, block_body_or_nil]+. Brace-matched scan,
|
|
94
|
+
# so nested `{}` inside the block body are handled. Shared by
|
|
95
|
+
# instance and Ractor compile paths, so must be a class method
|
|
96
|
+
# (Ractors cannot call back into instance state).
|
|
97
|
+
#
|
|
98
|
+
def self.extract_block(expr, keyword)
|
|
99
|
+
start = expr.index(/#{keyword}\s*\{/) or return [expr, nil]
|
|
122
100
|
|
|
123
101
|
i = expr.index("{", start)
|
|
124
102
|
depth = 1
|
|
125
103
|
j = i + 1
|
|
104
|
+
|
|
126
105
|
while j < expr.length && depth > 0
|
|
127
106
|
case expr[j]
|
|
128
107
|
when "{"
|
|
@@ -130,6 +109,7 @@ module NNQ
|
|
|
130
109
|
when "}"
|
|
131
110
|
depth -= 1
|
|
132
111
|
end
|
|
112
|
+
|
|
133
113
|
j += 1
|
|
134
114
|
end
|
|
135
115
|
|
|
@@ -137,6 +117,17 @@ module NNQ
|
|
|
137
117
|
trimmed = expr[0...start] + expr[j..]
|
|
138
118
|
[trimmed, body]
|
|
139
119
|
end
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def extract_blocks(expr)
|
|
126
|
+
expr, begin_body = self.class.extract_block(expr, "BEGIN")
|
|
127
|
+
expr, end_body = self.class.extract_block(expr, "END")
|
|
128
|
+
[expr, begin_body, end_body]
|
|
129
|
+
end
|
|
130
|
+
|
|
140
131
|
end
|
|
141
132
|
end
|
|
142
133
|
end
|
data/lib/nnq/cli/formatter.rb
CHANGED
|
@@ -7,11 +7,10 @@ module NNQ
|
|
|
7
7
|
# the socket, not by the formatter.
|
|
8
8
|
#
|
|
9
9
|
# Unlike omq-cli's Formatter, nnq messages are not multipart — one
|
|
10
|
-
# `String` body per message. The API
|
|
11
|
-
#
|
|
12
|
-
# way.
|
|
10
|
+
# `String` body per message. The API takes and returns a plain
|
|
11
|
+
# `String`.
|
|
13
12
|
class Formatter
|
|
14
|
-
# @param format [Symbol] wire format (:ascii, :quoted, :raw, :
|
|
13
|
+
# @param format [Symbol] wire format (:ascii, :quoted, :raw, :msgpack, :marshal)
|
|
15
14
|
def initialize(format)
|
|
16
15
|
@format = format
|
|
17
16
|
end
|
|
@@ -19,45 +18,38 @@ module NNQ
|
|
|
19
18
|
|
|
20
19
|
# Encodes a message body into a printable string for output.
|
|
21
20
|
#
|
|
22
|
-
# @param msg [
|
|
21
|
+
# @param msg [String] message body
|
|
23
22
|
# @return [String] formatted output line
|
|
23
|
+
#
|
|
24
24
|
def encode(msg)
|
|
25
|
-
body = msg.first.to_s
|
|
26
25
|
case @format
|
|
27
26
|
when :ascii
|
|
28
|
-
|
|
27
|
+
msg.b.gsub(/[^[:print:]\t]/, ".") << "\n"
|
|
29
28
|
when :quoted
|
|
30
|
-
|
|
29
|
+
msg.b.dump[1..-2] << "\n"
|
|
31
30
|
when :raw
|
|
32
|
-
|
|
33
|
-
when :jsonl
|
|
34
|
-
JSON.generate([body]) + "\n"
|
|
31
|
+
msg # FIXME: are these really the wire bytes?
|
|
35
32
|
when :msgpack
|
|
36
|
-
MessagePack.pack(
|
|
33
|
+
MessagePack.pack(msg)
|
|
37
34
|
when :marshal
|
|
38
|
-
|
|
35
|
+
msg.inspect << "\n"
|
|
39
36
|
end
|
|
40
37
|
end
|
|
41
38
|
|
|
42
39
|
|
|
43
|
-
# Decodes a formatted input line into a
|
|
40
|
+
# Decodes a formatted input line into a message body.
|
|
44
41
|
#
|
|
45
42
|
# @param line [String] input line (newline-terminated)
|
|
46
|
-
# @return [
|
|
43
|
+
# @return [String] message
|
|
44
|
+
#
|
|
47
45
|
def decode(line)
|
|
48
46
|
case @format
|
|
49
47
|
when :ascii, :marshal
|
|
50
|
-
|
|
48
|
+
line.chomp
|
|
51
49
|
when :quoted
|
|
52
|
-
|
|
50
|
+
"\"#{line.chomp}\"".undump
|
|
53
51
|
when :raw
|
|
54
|
-
|
|
55
|
-
when :jsonl
|
|
56
|
-
arr = JSON.parse(line.chomp)
|
|
57
|
-
unless arr.is_a?(Array) && arr.all? { |e| e.is_a?(String) }
|
|
58
|
-
abort "JSON Lines input must be an array of strings"
|
|
59
|
-
end
|
|
60
|
-
arr.first(1)
|
|
52
|
+
line
|
|
61
53
|
end
|
|
62
54
|
end
|
|
63
55
|
|
|
@@ -87,10 +79,10 @@ module NNQ
|
|
|
87
79
|
|
|
88
80
|
# Formats a message body for human-readable preview (logging).
|
|
89
81
|
#
|
|
90
|
-
# @param
|
|
82
|
+
# @param body [String] message body
|
|
91
83
|
# @return [String] truncated preview
|
|
92
|
-
def self.preview(
|
|
93
|
-
body =
|
|
84
|
+
def self.preview(body)
|
|
85
|
+
body = body.to_s
|
|
94
86
|
"(#{body.bytesize}B) #{preview_body(body)}"
|
|
95
87
|
end
|
|
96
88
|
|
data/lib/nnq/cli/pipe.rb
CHANGED
|
@@ -105,11 +105,9 @@ module NNQ
|
|
|
105
105
|
loop do
|
|
106
106
|
body = @pull.receive
|
|
107
107
|
break if body.nil?
|
|
108
|
-
msg = eval_recv_expr(
|
|
108
|
+
msg = eval_recv_expr(body)
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
@push.send(msg.first)
|
|
112
|
-
end
|
|
110
|
+
@push.send(msg) if msg
|
|
113
111
|
|
|
114
112
|
# Yield after send so send-pump fibers can drain the queue
|
|
115
113
|
# before the next message is enqueued. Without this, one pump
|
data/lib/nnq/cli/pipe_worker.rb
CHANGED
|
@@ -65,9 +65,9 @@ module NNQ
|
|
|
65
65
|
def format_event(event)
|
|
66
66
|
case event.type
|
|
67
67
|
when :message_sent
|
|
68
|
-
"nnq: >> #{NNQ::CLI::Formatter.preview(
|
|
68
|
+
"nnq: >> #{NNQ::CLI::Formatter.preview(event.detail[:body])}"
|
|
69
69
|
when :message_received
|
|
70
|
-
"nnq: << #{NNQ::CLI::Formatter.preview(
|
|
70
|
+
"nnq: << #{NNQ::CLI::Formatter.preview(event.detail[:body])}"
|
|
71
71
|
else
|
|
72
72
|
ep = event.endpoint ? " #{event.endpoint}" : ""
|
|
73
73
|
detail = event.detail ? " #{event.detail}" : ""
|
|
@@ -100,11 +100,9 @@ module NNQ
|
|
|
100
100
|
body = @pull.receive
|
|
101
101
|
break if body.nil?
|
|
102
102
|
msg = NNQ::CLI::ExpressionEvaluator.normalize_result(
|
|
103
|
-
@ctx.instance_exec(
|
|
103
|
+
@ctx.instance_exec(body, &@eval_proc)
|
|
104
104
|
)
|
|
105
|
-
|
|
106
|
-
@push.send(msg.first)
|
|
107
|
-
end
|
|
105
|
+
@push.send(msg) if msg
|
|
108
106
|
n -= 1 if n && n > 0
|
|
109
107
|
break if n == 0
|
|
110
108
|
end
|
|
@@ -127,9 +125,7 @@ module NNQ
|
|
|
127
125
|
out = NNQ::CLI::ExpressionEvaluator.normalize_result(
|
|
128
126
|
@ctx.instance_exec(&@end_proc)
|
|
129
127
|
)
|
|
130
|
-
|
|
131
|
-
@push.send(out.first)
|
|
132
|
-
end
|
|
128
|
+
@push.send(out) if out
|
|
133
129
|
end
|
|
134
130
|
end
|
|
135
131
|
end
|
data/lib/nnq/cli/req_rep.rb
CHANGED
|
@@ -13,31 +13,39 @@ module NNQ
|
|
|
13
13
|
def run_loop(task)
|
|
14
14
|
n = config.count
|
|
15
15
|
i = 0
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
sleep config.delay if config.delay
|
|
18
|
+
generator = @send_eval_proc && !config.data && !config.file && !stdin_ready?
|
|
19
|
+
|
|
17
20
|
loop do
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
if generator
|
|
22
|
+
msg = eval_send_expr(nil)
|
|
23
|
+
else
|
|
24
|
+
msg = read_next
|
|
25
|
+
break unless msg
|
|
26
|
+
msg = eval_send_expr(msg)
|
|
27
|
+
end
|
|
28
|
+
|
|
21
29
|
next unless msg
|
|
30
|
+
|
|
22
31
|
reply = request_and_receive(msg)
|
|
23
32
|
break if reply.nil?
|
|
24
33
|
output(eval_recv_expr(reply))
|
|
25
34
|
i += 1
|
|
26
35
|
break if n && n > 0 && i >= n
|
|
27
|
-
break if !config.interval && (config.data || config.file)
|
|
36
|
+
break if !config.interval && (generator || config.data || config.file) && !(n && n > 0)
|
|
28
37
|
wait_for_interval if config.interval
|
|
29
38
|
end
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
|
|
33
42
|
def request_and_receive(msg)
|
|
34
|
-
return nil if msg.empty?
|
|
35
|
-
body = config.format == :marshal ? Marshal.dump(msg
|
|
43
|
+
return nil if msg.nil? || msg.empty?
|
|
44
|
+
body = config.format == :marshal ? Marshal.dump(msg) : msg
|
|
36
45
|
reply_body = @sock.send_request(body)
|
|
37
46
|
transient_ready!
|
|
38
47
|
return nil if reply_body.nil?
|
|
39
|
-
|
|
40
|
-
[reply]
|
|
48
|
+
config.format == :marshal ? Marshal.load(reply_body) : reply_body
|
|
41
49
|
end
|
|
42
50
|
|
|
43
51
|
|
|
@@ -74,7 +82,7 @@ module NNQ
|
|
|
74
82
|
reply = eval_recv_expr(msg)
|
|
75
83
|
unless reply.equal?(SENT)
|
|
76
84
|
output(reply)
|
|
77
|
-
send_reply(reply ||
|
|
85
|
+
send_reply(reply || "")
|
|
78
86
|
end
|
|
79
87
|
elsif config.echo
|
|
80
88
|
output(msg)
|
|
@@ -92,8 +100,8 @@ module NNQ
|
|
|
92
100
|
|
|
93
101
|
|
|
94
102
|
def send_reply(msg)
|
|
95
|
-
return if msg.
|
|
96
|
-
body = config.format == :marshal ? Marshal.dump(msg
|
|
103
|
+
return if msg.nil?
|
|
104
|
+
body = config.format == :marshal ? Marshal.dump(msg) : msg
|
|
97
105
|
@sock.send_reply(body)
|
|
98
106
|
transient_ready!
|
|
99
107
|
end
|
data/lib/nnq/cli/socket_setup.rb
CHANGED
|
@@ -10,12 +10,14 @@ module NNQ
|
|
|
10
10
|
# (one batch exactly fills a full queue).
|
|
11
11
|
DEFAULT_HWM = 64
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
# Default max inbound message size (1 MiB) so a misconfigured or
|
|
14
15
|
# malicious peer can't force arbitrary memory allocation on a
|
|
15
16
|
# terminal user. Users can raise it with --recv-maxsz N, or
|
|
16
17
|
# disable it entirely with --recv-maxsz 0.
|
|
17
18
|
DEFAULT_RECV_MAXSZ = 1 << 20
|
|
18
19
|
|
|
20
|
+
|
|
19
21
|
# Apply post-construction socket options from +config+ to +sock+.
|
|
20
22
|
# send_hwm and linger are construction-time kwargs (see {.build});
|
|
21
23
|
# the rest of the options are set here and read later by the
|
|
@@ -24,12 +26,15 @@ module NNQ
|
|
|
24
26
|
sock.options.read_timeout = config.timeout if config.timeout
|
|
25
27
|
sock.options.write_timeout = config.timeout if config.timeout
|
|
26
28
|
sock.options.reconnect_interval = config.reconnect_ivl if config.reconnect_ivl
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
|
|
30
|
+
case config.recv_maxsz
|
|
31
|
+
when nil
|
|
32
|
+
sock.options.max_message_size = DEFAULT_RECV_MAXSZ
|
|
33
|
+
when 0
|
|
34
|
+
sock.options.max_message_size = nil
|
|
35
|
+
else
|
|
36
|
+
config.recv_maxsz
|
|
37
|
+
end
|
|
33
38
|
end
|
|
34
39
|
|
|
35
40
|
|
|
@@ -38,10 +43,10 @@ module NNQ
|
|
|
38
43
|
# (send_hwm is captured during routing init and can't be changed
|
|
39
44
|
# after the fact), so we pass them there.
|
|
40
45
|
def self.build(klass, config)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
linger = config.linger
|
|
47
|
+
send_hwm = config.send_hwm || DEFAULT_HWM
|
|
48
|
+
sock = klass.new(linger:, send_hwm:)
|
|
49
|
+
|
|
45
50
|
apply_options(sock, config)
|
|
46
51
|
sock
|
|
47
52
|
end
|
|
@@ -54,6 +59,7 @@ module NNQ
|
|
|
54
59
|
sock.bind(url)
|
|
55
60
|
CLI::Term.write_attach(:bind, sock.last_endpoint, verbose) if verbose >= 1
|
|
56
61
|
end
|
|
62
|
+
|
|
57
63
|
config.connects.each do |url|
|
|
58
64
|
sock.connect(url)
|
|
59
65
|
CLI::Term.write_attach(:connect, url, verbose) if verbose >= 1
|
|
@@ -98,6 +104,7 @@ module NNQ
|
|
|
98
104
|
prefixes = config.subscribes.empty? ? [""] : config.subscribes
|
|
99
105
|
prefixes.each { |p| sock.subscribe(p) }
|
|
100
106
|
end
|
|
107
|
+
|
|
101
108
|
end
|
|
102
109
|
end
|
|
103
110
|
end
|
|
@@ -29,8 +29,8 @@ module NNQ
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def survey_and_collect(msg)
|
|
32
|
-
return if msg.empty?
|
|
33
|
-
body = config.format == :marshal ? Marshal.dump(msg
|
|
32
|
+
return if msg.nil? || msg.empty?
|
|
33
|
+
body = config.format == :marshal ? Marshal.dump(msg) : msg
|
|
34
34
|
@sock.send_survey(body)
|
|
35
35
|
transient_ready!
|
|
36
36
|
collect_replies
|
|
@@ -42,7 +42,7 @@ module NNQ
|
|
|
42
42
|
body = @sock.receive
|
|
43
43
|
break if body.nil?
|
|
44
44
|
reply = config.format == :marshal ? Marshal.load(body) : body
|
|
45
|
-
output(eval_recv_expr(
|
|
45
|
+
output(eval_recv_expr(reply))
|
|
46
46
|
rescue NNQ::TimedOut
|
|
47
47
|
break
|
|
48
48
|
end
|
|
@@ -81,7 +81,7 @@ module NNQ
|
|
|
81
81
|
reply = eval_recv_expr(msg)
|
|
82
82
|
unless reply.equal?(SENT)
|
|
83
83
|
output(reply)
|
|
84
|
-
send_reply(reply ||
|
|
84
|
+
send_reply(reply || "")
|
|
85
85
|
end
|
|
86
86
|
elsif config.echo
|
|
87
87
|
output(msg)
|
|
@@ -99,8 +99,8 @@ module NNQ
|
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def send_reply(msg)
|
|
102
|
-
return if msg.
|
|
103
|
-
body = config.format == :marshal ? Marshal.dump(msg
|
|
102
|
+
return if msg.nil?
|
|
103
|
+
body = config.format == :marshal ? Marshal.dump(msg) : msg
|
|
104
104
|
@sock.send_reply(body)
|
|
105
105
|
transient_ready!
|
|
106
106
|
end
|
data/lib/nnq/cli/term.rb
CHANGED
|
@@ -36,9 +36,9 @@ module NNQ
|
|
|
36
36
|
prefix = log_prefix(verbose)
|
|
37
37
|
case event.type
|
|
38
38
|
when :message_sent
|
|
39
|
-
"#{prefix}nnq: >> #{Formatter.preview(
|
|
39
|
+
"#{prefix}nnq: >> #{Formatter.preview(event.detail[:body])}"
|
|
40
40
|
when :message_received
|
|
41
|
-
"#{prefix}nnq: << #{Formatter.preview(
|
|
41
|
+
"#{prefix}nnq: << #{Formatter.preview(event.detail[:body])}"
|
|
42
42
|
else
|
|
43
43
|
ep = event.endpoint ? " #{event.endpoint}" : ""
|
|
44
44
|
detail = event.detail ? " #{event.detail}" : ""
|
data/lib/nnq/cli/version.rb
CHANGED