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 +4 -4
- data/CHANGELOG.md +158 -0
- data/README.md +14 -9
- data/lib/omq/cli/base_runner.rb +152 -210
- data/lib/omq/cli/cli_parser.rb +470 -0
- data/lib/omq/cli/client_server.rb +32 -59
- data/lib/omq/cli/config.rb +10 -0
- data/lib/omq/cli/expression_evaluator.rb +156 -0
- data/lib/omq/cli/formatter.rb +27 -1
- data/lib/omq/cli/pair.rb +9 -7
- data/lib/omq/cli/parallel_recv_runner.rb +150 -0
- data/lib/omq/cli/pipe.rb +175 -156
- data/lib/omq/cli/pub_sub.rb +5 -1
- data/lib/omq/cli/push_pull.rb +5 -1
- data/lib/omq/cli/radio_dish.rb +5 -1
- data/lib/omq/cli/req_rep.rb +44 -49
- data/lib/omq/cli/router_dealer.rb +13 -46
- data/lib/omq/cli/routing_helper.rb +95 -0
- data/lib/omq/cli/scatter_gather.rb +5 -1
- data/lib/omq/cli/socket_setup.rb +100 -0
- data/lib/omq/cli/transient_monitor.rb +41 -0
- data/lib/omq/cli/version.rb +1 -1
- data/lib/omq/cli.rb +72 -426
- metadata +94 -6
- data/lib/omq/cli/channel.rb +0 -8
- data/lib/omq/cli/peer.rb +0 -8
data/lib/omq/cli/base_runner.rb
CHANGED
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
module CLI
|
|
5
|
+
# Template runner base class for all socket-type CLI runners.
|
|
6
|
+
# Subclasses override {#run_loop} to implement socket-specific behaviour.
|
|
5
7
|
class BaseRunner
|
|
8
|
+
# @return [Config] frozen CLI configuration
|
|
9
|
+
# @return [Object] the OMQ socket instance
|
|
6
10
|
attr_reader :config, :sock
|
|
7
11
|
|
|
8
12
|
|
|
13
|
+
# @param config [Config] frozen CLI configuration
|
|
14
|
+
# @param socket_class [Class] OMQ socket class to instantiate (e.g. OMQ::PUSH)
|
|
9
15
|
def initialize(config, socket_class)
|
|
10
16
|
@config = config
|
|
11
17
|
@klass = socket_class
|
|
@@ -13,26 +19,18 @@ module OMQ
|
|
|
13
19
|
end
|
|
14
20
|
|
|
15
21
|
|
|
22
|
+
# Runs the full lifecycle: socket setup, peer wait, BEGIN/END blocks, and the main loop.
|
|
23
|
+
#
|
|
24
|
+
# @param task [Async::Task] the parent async task
|
|
25
|
+
# @return [void]
|
|
16
26
|
def call(task)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
setup_curve
|
|
20
|
-
setup_subscriptions
|
|
21
|
-
compile_expr
|
|
22
|
-
|
|
23
|
-
if config.transient
|
|
24
|
-
start_disconnect_monitor(task)
|
|
25
|
-
Async::Task.current.yield # let monitor start waiting
|
|
26
|
-
end
|
|
27
|
-
|
|
27
|
+
setup_socket
|
|
28
|
+
maybe_start_transient_monitor(task)
|
|
28
29
|
sleep(config.delay) if config.delay && config.recv_only?
|
|
29
30
|
wait_for_peer if needs_peer_wait?
|
|
30
|
-
|
|
31
|
-
@sock.instance_exec(&@send_begin_proc) if @send_begin_proc
|
|
32
|
-
@sock.instance_exec(&@recv_begin_proc) if @recv_begin_proc
|
|
31
|
+
run_begin_blocks
|
|
33
32
|
run_loop(task)
|
|
34
|
-
|
|
35
|
-
@sock.instance_exec(&@recv_end_proc) if @recv_end_proc
|
|
33
|
+
run_end_blocks
|
|
36
34
|
ensure
|
|
37
35
|
@sock&.close
|
|
38
36
|
end
|
|
@@ -46,34 +44,59 @@ module OMQ
|
|
|
46
44
|
raise NotImplementedError
|
|
47
45
|
end
|
|
48
46
|
|
|
47
|
+
|
|
49
48
|
# ── Socket creation ─────────────────────────────────────────────
|
|
50
49
|
|
|
51
50
|
|
|
51
|
+
def setup_socket
|
|
52
|
+
@sock = create_socket
|
|
53
|
+
attach_endpoints unless config.parallel
|
|
54
|
+
setup_curve
|
|
55
|
+
setup_subscriptions
|
|
56
|
+
compile_expr
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
52
60
|
def create_socket
|
|
53
|
-
|
|
54
|
-
sock_opts[:conflate] = true if config.conflate && %w[pub radio].include?(config.type_name)
|
|
55
|
-
sock = @klass.new(**sock_opts)
|
|
56
|
-
sock.recv_timeout = config.timeout if config.timeout
|
|
57
|
-
sock.send_timeout = config.timeout if config.timeout
|
|
58
|
-
sock.reconnect_interval = config.reconnect_ivl if config.reconnect_ivl
|
|
59
|
-
sock.heartbeat_interval = config.heartbeat_ivl if config.heartbeat_ivl
|
|
60
|
-
sock.identity = config.identity if config.identity
|
|
61
|
-
sock.router_mandatory = true if config.type_name == "router"
|
|
62
|
-
sock
|
|
61
|
+
SocketSetup.build(@klass, config)
|
|
63
62
|
end
|
|
64
63
|
|
|
65
64
|
|
|
66
65
|
def attach_endpoints
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
SocketSetup.attach(@sock, config, verbose: config.verbose)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ── Transient disconnect monitor ────────────────────────────────
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def maybe_start_transient_monitor(task)
|
|
74
|
+
return unless config.transient
|
|
75
|
+
@transient_monitor = TransientMonitor.new(@sock, config, task, method(:log))
|
|
76
|
+
Async::Task.current.yield # let monitor start waiting
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def transient_ready!
|
|
81
|
+
@transient_monitor&.ready!
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ── BEGIN / END blocks ──────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def run_begin_blocks
|
|
89
|
+
@sock.instance_exec(&@send_begin_proc) if @send_begin_proc
|
|
90
|
+
@sock.instance_exec(&@recv_begin_proc) if @recv_begin_proc
|
|
75
91
|
end
|
|
76
92
|
|
|
93
|
+
|
|
94
|
+
def run_end_blocks
|
|
95
|
+
@sock.instance_exec(&@send_end_proc) if @send_end_proc
|
|
96
|
+
@sock.instance_exec(&@recv_end_proc) if @recv_end_proc
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
|
|
77
100
|
# ── Peer wait with grace period ─────────────────────────────────
|
|
78
101
|
|
|
79
102
|
|
|
@@ -86,46 +109,29 @@ module OMQ
|
|
|
86
109
|
with_timeout(config.timeout) do
|
|
87
110
|
@sock.peer_connected.wait
|
|
88
111
|
log "Peer connected"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
log "Subscriber joined"
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Grace period: when multiple peers may be connecting (bind or
|
|
95
|
-
# multiple connect URLs), wait one reconnect interval so
|
|
96
|
-
# latecomers finish their handshake before we start sending.
|
|
97
|
-
if config.binds.any? || config.connects.size > 1
|
|
98
|
-
ri = @sock.options.reconnect_interval
|
|
99
|
-
sleep(ri.is_a?(Range) ? ri.begin : ri)
|
|
100
|
-
end
|
|
112
|
+
wait_for_subscriber
|
|
113
|
+
apply_grace_period
|
|
101
114
|
end
|
|
102
115
|
end
|
|
103
116
|
|
|
104
|
-
# ── Transient disconnect monitor ────────────────────────────────
|
|
105
|
-
|
|
106
117
|
|
|
107
|
-
def
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
@sock.all_peers_gone.wait unless @sock.connection_count == 0
|
|
112
|
-
log "All peers disconnected, exiting"
|
|
113
|
-
@sock.reconnect_enabled = false
|
|
114
|
-
if config.send_only?
|
|
115
|
-
task.stop
|
|
116
|
-
else
|
|
117
|
-
@sock.close_read
|
|
118
|
-
end
|
|
119
|
-
end
|
|
118
|
+
def wait_for_subscriber
|
|
119
|
+
return unless %w[pub xpub].include?(config.type_name)
|
|
120
|
+
@sock.subscriber_joined.wait
|
|
121
|
+
log "Subscriber joined"
|
|
120
122
|
end
|
|
121
123
|
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
# Grace period: when multiple peers may be connecting (bind or
|
|
126
|
+
# multiple connect URLs), wait one reconnect interval so
|
|
127
|
+
# latecomers finish their handshake before we start sending.
|
|
128
|
+
def apply_grace_period
|
|
129
|
+
return unless config.binds.any? || config.connects.size > 1
|
|
130
|
+
ri = @sock.options.reconnect_interval
|
|
131
|
+
sleep(ri.is_a?(Range) ? ri.begin : ri)
|
|
127
132
|
end
|
|
128
133
|
|
|
134
|
+
|
|
129
135
|
# ── Timeout helper ──────────────────────────────────────────────
|
|
130
136
|
|
|
131
137
|
|
|
@@ -137,50 +143,22 @@ module OMQ
|
|
|
137
143
|
end
|
|
138
144
|
end
|
|
139
145
|
|
|
146
|
+
|
|
140
147
|
# ── Socket setup ────────────────────────────────────────────────
|
|
141
148
|
|
|
142
149
|
|
|
143
150
|
def setup_subscriptions
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
end
|
|
151
|
+
SocketSetup.setup_subscriptions(@sock, config)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def setup_subscriptions_on(sock)
|
|
156
|
+
SocketSetup.setup_subscriptions(sock, config)
|
|
151
157
|
end
|
|
152
158
|
|
|
153
159
|
|
|
154
160
|
def setup_curve
|
|
155
|
-
|
|
156
|
-
server_mode = config.curve_server || (ENV["OMQ_SERVER_PUBLIC"] && ENV["OMQ_SERVER_SECRET"])
|
|
157
|
-
|
|
158
|
-
return unless server_key_z85 || server_mode
|
|
159
|
-
|
|
160
|
-
crypto = CLI.load_curve_crypto(config.curve_crypto || ENV["OMQ_CURVE_CRYPTO"], verbose: config.verbose)
|
|
161
|
-
require "protocol/zmtp/mechanism/curve"
|
|
162
|
-
|
|
163
|
-
if server_key_z85
|
|
164
|
-
server_key = Protocol::ZMTP::Z85.decode(server_key_z85)
|
|
165
|
-
client_key = crypto::PrivateKey.generate
|
|
166
|
-
@sock.mechanism = Protocol::ZMTP::Mechanism::Curve.client(
|
|
167
|
-
client_key.public_key.to_s, client_key.to_s,
|
|
168
|
-
server_key: server_key, crypto: crypto
|
|
169
|
-
)
|
|
170
|
-
elsif server_mode
|
|
171
|
-
if ENV["OMQ_SERVER_PUBLIC"] && ENV["OMQ_SERVER_SECRET"]
|
|
172
|
-
server_pub = Protocol::ZMTP::Z85.decode(ENV["OMQ_SERVER_PUBLIC"])
|
|
173
|
-
server_sec = Protocol::ZMTP::Z85.decode(ENV["OMQ_SERVER_SECRET"])
|
|
174
|
-
else
|
|
175
|
-
key = crypto::PrivateKey.generate
|
|
176
|
-
server_pub = key.public_key.to_s
|
|
177
|
-
server_sec = key.to_s
|
|
178
|
-
end
|
|
179
|
-
@sock.mechanism = Protocol::ZMTP::Mechanism::Curve.server(
|
|
180
|
-
server_pub, server_sec, crypto: crypto
|
|
181
|
-
)
|
|
182
|
-
$stderr.puts "OMQ_SERVER_KEY='#{Protocol::ZMTP::Z85.encode(server_pub)}'"
|
|
183
|
-
end
|
|
161
|
+
SocketSetup.setup_curve(@sock, config)
|
|
184
162
|
end
|
|
185
163
|
|
|
186
164
|
|
|
@@ -189,28 +167,14 @@ module OMQ
|
|
|
189
167
|
|
|
190
168
|
def run_send_logic
|
|
191
169
|
n = config.count
|
|
192
|
-
i = 0
|
|
193
170
|
sleep(config.delay) if config.delay
|
|
194
171
|
if config.interval
|
|
195
|
-
|
|
196
|
-
unless @send_tick_eof || (n && n > 0 && i >= n)
|
|
197
|
-
Async::Loop.quantized(interval: config.interval) do
|
|
198
|
-
i += send_tick
|
|
199
|
-
break if @send_tick_eof || (n && n > 0 && i >= n)
|
|
200
|
-
end
|
|
201
|
-
end
|
|
172
|
+
run_interval_send(n)
|
|
202
173
|
elsif config.data || config.file
|
|
203
174
|
parts = eval_send_expr(read_next)
|
|
204
175
|
send_msg(parts) if parts
|
|
205
176
|
elsif stdin_ready?
|
|
206
|
-
|
|
207
|
-
parts = read_next
|
|
208
|
-
break unless parts
|
|
209
|
-
parts = eval_send_expr(parts)
|
|
210
|
-
send_msg(parts) if parts
|
|
211
|
-
i += 1
|
|
212
|
-
break if n && n > 0 && i >= n
|
|
213
|
-
end
|
|
177
|
+
run_stdin_send(n)
|
|
214
178
|
elsif @send_eval_proc
|
|
215
179
|
parts = eval_send_expr(nil)
|
|
216
180
|
send_msg(parts) if parts
|
|
@@ -218,6 +182,29 @@ module OMQ
|
|
|
218
182
|
end
|
|
219
183
|
|
|
220
184
|
|
|
185
|
+
def run_interval_send(n)
|
|
186
|
+
i = send_tick
|
|
187
|
+
return if @send_tick_eof || (n && n > 0 && i >= n)
|
|
188
|
+
Async::Loop.quantized(interval: config.interval) do
|
|
189
|
+
i += send_tick
|
|
190
|
+
break if @send_tick_eof || (n && n > 0 && i >= n)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def run_stdin_send(n)
|
|
196
|
+
i = 0
|
|
197
|
+
loop do
|
|
198
|
+
parts = read_next
|
|
199
|
+
break unless parts
|
|
200
|
+
parts = eval_send_expr(parts)
|
|
201
|
+
send_msg(parts) if parts
|
|
202
|
+
i += 1
|
|
203
|
+
break if n && n > 0 && i >= n
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
|
|
221
208
|
def send_tick
|
|
222
209
|
raw = read_next_or_nil
|
|
223
210
|
if raw.nil? && !@send_eval_proc
|
|
@@ -244,6 +231,17 @@ module OMQ
|
|
|
244
231
|
end
|
|
245
232
|
|
|
246
233
|
|
|
234
|
+
# Parallel recv-eval: delegates to ParallelRecvRunner.
|
|
235
|
+
#
|
|
236
|
+
def run_parallel_recv(task)
|
|
237
|
+
# @sock was created by call() before run_loop; close it now so it doesn't
|
|
238
|
+
# steal messages from the N worker sockets ParallelRecvRunner creates.
|
|
239
|
+
@sock&.close
|
|
240
|
+
@sock = nil
|
|
241
|
+
ParallelRecvRunner.new(@klass, config, @fmt, method(:output)).run(task)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
|
|
247
245
|
def wait_for_loops(receiver, sender)
|
|
248
246
|
if config.data || config.file || config.send_expr || config.recv_expr || config.target
|
|
249
247
|
sender.wait
|
|
@@ -257,6 +255,7 @@ module OMQ
|
|
|
257
255
|
end
|
|
258
256
|
end
|
|
259
257
|
|
|
258
|
+
|
|
260
259
|
# ── Message I/O ─────────────────────────────────────────────────
|
|
261
260
|
|
|
262
261
|
|
|
@@ -286,23 +285,32 @@ module OMQ
|
|
|
286
285
|
|
|
287
286
|
|
|
288
287
|
def read_next
|
|
288
|
+
config.data || config.file ? read_inline_data : read_stdin_input
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def read_inline_data
|
|
289
293
|
if config.data
|
|
290
294
|
@fmt.decode(config.data + "\n")
|
|
291
|
-
|
|
295
|
+
else
|
|
292
296
|
@file_data ||= (config.file == "-" ? $stdin.read : File.read(config.file)).chomp
|
|
293
297
|
@fmt.decode(@file_data + "\n")
|
|
294
|
-
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def read_stdin_input
|
|
303
|
+
case config.format
|
|
304
|
+
when :msgpack
|
|
295
305
|
@fmt.decode_msgpack($stdin)
|
|
296
|
-
|
|
306
|
+
when :marshal
|
|
297
307
|
@fmt.decode_marshal($stdin)
|
|
298
|
-
|
|
308
|
+
when :raw
|
|
299
309
|
data = $stdin.read
|
|
300
|
-
|
|
301
|
-
[data]
|
|
310
|
+
data.nil? || data.empty? ? nil : [data]
|
|
302
311
|
else
|
|
303
312
|
line = $stdin.gets
|
|
304
|
-
|
|
305
|
-
@fmt.decode(line)
|
|
313
|
+
line.nil? ? nil : @fmt.decode(line)
|
|
306
314
|
end
|
|
307
315
|
end
|
|
308
316
|
|
|
@@ -334,116 +342,50 @@ module OMQ
|
|
|
334
342
|
$stdout.flush
|
|
335
343
|
end
|
|
336
344
|
|
|
337
|
-
# ── Routing helpers ─────────────────────────────────────────────
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def display_routing_id(id)
|
|
341
|
-
if id.bytes.all? { |b| b >= 0x20 && b <= 0x7E }
|
|
342
|
-
id
|
|
343
|
-
else
|
|
344
|
-
"0x#{id.unpack1("H*")}"
|
|
345
|
-
end
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
def resolve_target(target)
|
|
350
|
-
if target.start_with?("0x")
|
|
351
|
-
[target[2..].delete(" ")].pack("H*")
|
|
352
|
-
else
|
|
353
|
-
target
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
345
|
|
|
357
346
|
# ── Eval ────────────────────────────────────────────────────────
|
|
358
347
|
|
|
359
348
|
|
|
360
349
|
def compile_expr
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
350
|
+
@send_evaluator = compile_evaluator(config.send_expr, fallback: OMQ.outgoing_proc)
|
|
351
|
+
@recv_evaluator = compile_evaluator(config.recv_expr, fallback: OMQ.incoming_proc)
|
|
352
|
+
assign_send_aliases
|
|
353
|
+
assign_recv_aliases
|
|
365
354
|
end
|
|
366
355
|
|
|
367
356
|
|
|
368
|
-
def
|
|
369
|
-
|
|
370
|
-
proc do |msg|
|
|
371
|
-
$_ = msg&.first
|
|
372
|
-
block.call(msg)
|
|
373
|
-
end
|
|
357
|
+
def compile_evaluator(src, fallback:)
|
|
358
|
+
ExpressionEvaluator.new(src, format: config.format, fallback_proc: fallback)
|
|
374
359
|
end
|
|
375
360
|
|
|
376
361
|
|
|
377
|
-
def
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if expr && !expr.strip.empty?
|
|
383
|
-
instance_variable_set(:"@#{direction}_eval_proc", eval("proc { $_ = $F&.first; #{expr} }"))
|
|
384
|
-
end
|
|
362
|
+
def assign_send_aliases
|
|
363
|
+
# Keep ivar aliases — subclasses check these directly
|
|
364
|
+
@send_begin_proc = @send_evaluator.begin_proc
|
|
365
|
+
@send_eval_proc = @send_evaluator.eval_proc
|
|
366
|
+
@send_end_proc = @send_evaluator.end_proc
|
|
385
367
|
end
|
|
386
368
|
|
|
387
369
|
|
|
388
|
-
def
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
[expr, begin_body, end_body]
|
|
370
|
+
def assign_recv_aliases
|
|
371
|
+
@recv_begin_proc = @recv_evaluator.begin_proc
|
|
372
|
+
@recv_eval_proc = @recv_evaluator.eval_proc
|
|
373
|
+
@recv_end_proc = @recv_evaluator.end_proc
|
|
393
374
|
end
|
|
394
375
|
|
|
395
376
|
|
|
396
|
-
def extract_block(expr, keyword)
|
|
397
|
-
start = expr.index(/#{keyword}\s*\{/)
|
|
398
|
-
return [expr, nil] unless start
|
|
399
|
-
|
|
400
|
-
# Find the opening brace
|
|
401
|
-
i = expr.index("{", start)
|
|
402
|
-
depth = 1
|
|
403
|
-
j = i + 1
|
|
404
|
-
while j < expr.length && depth > 0
|
|
405
|
-
case expr[j]
|
|
406
|
-
when "{" then depth += 1
|
|
407
|
-
when "}" then depth -= 1
|
|
408
|
-
end
|
|
409
|
-
j += 1
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
body = expr[(i + 1)..(j - 2)]
|
|
413
|
-
trimmed = expr[0...start] + expr[j..]
|
|
414
|
-
[trimmed, body]
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
SENT = Object.new.freeze # sentinel: eval already sent the reply
|
|
419
|
-
|
|
420
377
|
def eval_send_expr(parts)
|
|
421
|
-
|
|
422
|
-
run_eval(@send_eval_proc, parts)
|
|
378
|
+
@send_evaluator.call(parts, @sock)
|
|
423
379
|
end
|
|
424
380
|
|
|
425
381
|
|
|
426
382
|
def eval_recv_expr(parts)
|
|
427
|
-
|
|
428
|
-
run_eval(@recv_eval_proc, parts)
|
|
383
|
+
@recv_evaluator.call(parts, @sock)
|
|
429
384
|
end
|
|
430
385
|
|
|
431
386
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
result = @sock.instance_exec(parts, &eval_proc)
|
|
435
|
-
return nil if result.nil?
|
|
436
|
-
return SENT if result.equal?(@sock)
|
|
437
|
-
return [result] if config.format == :marshal
|
|
438
|
-
case result
|
|
439
|
-
when Array then result
|
|
440
|
-
when String then [result]
|
|
441
|
-
else [result.to_str]
|
|
442
|
-
end
|
|
443
|
-
rescue => e
|
|
444
|
-
$stderr.puts "omq: eval error: #{e.message} (#{e.class})"
|
|
445
|
-
exit 3
|
|
446
|
-
end
|
|
387
|
+
SENT = ExpressionEvaluator::SENT
|
|
388
|
+
|
|
447
389
|
|
|
448
390
|
# ── Logging ─────────────────────────────────────────────────────
|
|
449
391
|
|