omq-cli 0.18.0 → 0.19.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: c13378d48e8072d5537c35b476340431cd3c735f4a9ef6bea93036b776da2fa5
4
- data.tar.gz: 6fffa2d883b8a332b81307c874bc59ac6600997bc2cf954159aa82c14db9ed1a
3
+ metadata.gz: 92ca3f7bf414254b6ab93d4ff6282e1ca714292f6d7119c85cdb8d5f8e2e85c5
4
+ data.tar.gz: '021193bf3cf3785355f9592087ca1b182b50e4fb43fb8cf4a5590b9b75390da5'
5
5
  SHA512:
6
- metadata.gz: e35a88c00fdfbee35163bfc6bf7059f5b89fa97c735523932e76a61b87eafb84bbb9be29ba918cc81f394ff0879cd8978d5075d84ef234d75b30bde09b458c28
7
- data.tar.gz: 728b01ef1873bf8f7651f7cc56f0c88c6d8d147a1ee5a0b42c99b6eb0762b9c8159d921c1db6a0dd352cd2c040529bbd33d0b1be527997fc70156595fe1b2bfc
6
+ metadata.gz: 34500b01e5928851464136a9aeb9a5e2d6c76df95ae14f221068fcc399546b48abe662f0fdd68a9b1a73bde52b2fb20b3085dd9d1a14a08426f7baa40c0f224f
7
+ data.tar.gz: '0319a006c9bfe02acc713676f6db87dfb93314b059af1cc0c25a8a7efb2abfa70ecaf9b5817047332861a8a71f77a5711608fe6fab7d111b7349b02704029e5f'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.19.0 — 2026-08-03
4
+
5
+ - **Rust backend selection via `--backend rust`.** Loads
6
+ `omq-backend-rust` and passes `backend: :rust` to socket
7
+ constructors.
8
+ - **Monorepo import.** `omq-cli` now lives under `gems/omq-cli` in
9
+ `omq.rb` and participates in monorepo test and release tasks.
10
+
3
11
  ## 0.17.1 — 2026-05-04
4
12
 
5
13
  - bump deps
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://img.shields.io/gem/v/omq-cli?color=e9573f)](https://rubygems.org/gems/omq-cli)
4
4
  [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](LICENSE)
5
- [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%203.3-CC342D?logo=ruby&logoColor=white)](https://www.ruby-lang.org)
5
+ [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%204.0-CC342D?logo=ruby&logoColor=white)](https://www.ruby-lang.org)
6
6
 
7
7
  A command-line tool that speaks every ZeroMQ socket pattern, transforms
8
8
  messages with inline Ruby, parallelizes across Ractors, encrypts with CURVE,
@@ -75,6 +75,21 @@ omq push -c@work # IPC abstract namespace (@name → ipc://@
75
75
  Multiple endpoints are allowed — `omq pull -b tcp://:5557 -b tcp://:5558` binds both.
76
76
  Pipe takes two positional endpoints (input, output) or uses `--in`/`--out` for multiple per side.
77
77
 
78
+ ## Backends
79
+
80
+ The default backend is the pure Ruby engine.
81
+
82
+ | Flag | Backend |
83
+ |------|---------|
84
+ | `--backend ruby` | Pure Ruby backend, default |
85
+ | `--backend rust` | Rust backend from `omq-backend-rust` |
86
+ | `--backend libzmq` | libzmq backend from `omq-backend-libzmq` |
87
+ | `--backend ffi` or `--ffi` | Compatibility name for the libzmq backend |
88
+
89
+ Optional backends must be installed next to `omq-cli`.
90
+ Set `OMQ_BACKEND=ruby|rust|libzmq` to choose a default backend.
91
+ Use `-v` to print the effective backend at startup.
92
+
78
93
  ## Socket types
79
94
 
80
95
  ### Unidirectional (send-only / recv-only)
@@ -25,6 +25,7 @@ module OMQ
25
25
  # @return [void]
26
26
  def call(task)
27
27
  set_process_title
28
+ log_backend
28
29
  setup_socket
29
30
  start_event_monitor
30
31
  maybe_start_transient_monitor(task)
@@ -557,6 +558,11 @@ module OMQ
557
558
  end
558
559
 
559
560
 
561
+ def log_backend
562
+ log("backend: #{SocketSetup.backend_name(config)}")
563
+ end
564
+
565
+
560
566
  # Always attached so protocol-level disconnect events can kill
561
567
  # the socket. Verbose gating lives inside the callback:
562
568
  # -vv log connect/disconnect/retry/timeout events
@@ -277,7 +277,8 @@ module OMQ
277
277
  recv_maxsz: nil,
278
278
  curve_server: false,
279
279
  curve_server_key: nil,
280
- crypto: nil,
280
+ crypto: nil,
281
+ backend: :ruby,
281
282
  ffi: false,
282
283
  }.freeze
283
284
 
@@ -331,6 +332,7 @@ module OMQ
331
332
  def parse(argv)
332
333
  opts = DEFAULT_OPTS.transform_values { |v| v.is_a?(Array) ? v.dup : v }
333
334
  pipe_side = nil # nil = legacy positional mode; :in/:out = modal
335
+ explicit_backend = false
334
336
 
335
337
  parser = OptionParser.new do |o|
336
338
  o.banner = "Usage: omq TYPE [options]\n\n" \
@@ -462,8 +464,6 @@ module OMQ
462
464
  o.on("--curve-server-key KEY", "Enable CURVE as client (server's Z85 public key)") { |v| opts[:curve_server_key] = v }
463
465
  o.on("--crypto BACKEND", "Crypto backend: rbnacl (default) or nuckle (pure Ruby, DANGEROUS)") { |v| opts[:crypto] = v }
464
466
  o.separator " Install libsodium: apt install libsodium-dev / brew install libsodium"
465
- o.separator " Env vars: OMQ_SERVER_KEY (client), OMQ_SERVER_PUBLIC + OMQ_SERVER_SECRET (server)"
466
- o.separator " OMQ_CRYPTO (backend: rbnacl or nuckle)"
467
467
 
468
468
  o.separator "\nOther:"
469
469
  o.on("-v", "--verbose", "Verbosity: -v endpoints, -vv events, -vvv messages") { opts[:verbose] += 1 }
@@ -472,20 +472,22 @@ module OMQ
472
472
  }
473
473
  o.on("-q", "--quiet", "Suppress message output") { opts[:quiet] = true }
474
474
  o.on( "--transient", "Exit when all peers disconnect") { opts[:transient] = true }
475
- o.on( "--ffi", "Use libzmq FFI backend (requires omq-ffi gem + system libzmq 4.x)") do
476
- begin
477
- require "omq/ffi"
478
- rescue LoadError => e
479
- abort "omq: --ffi requires the omq-ffi gem and system libzmq 4.x (#{e.message})"
480
- end
475
+ o.on( "--backend BACKEND", %w[ruby rust libzmq ffi],
476
+ "Socket backend: ruby, rust, or libzmq") do |v|
477
+ backend = normalize_backend(v.to_sym)
478
+ load_socket_backend!(backend)
479
+ opts[:backend] = backend
480
+ opts[:ffi] = false
481
+ explicit_backend = true
482
+ end
483
+ o.on( "--ffi", "Alias for --backend libzmq") do
484
+ load_socket_backend!(:libzmq)
485
+ opts[:backend] = :libzmq
481
486
  opts[:ffi] = true
487
+ explicit_backend = true
482
488
  end
483
489
  o.on("-V", "--version") {
484
- if ENV["OMQ_DEV"]
485
- require_relative "../../../../omq/lib/omq/version"
486
- else
487
- require "omq/version"
488
- end
490
+ require "omq/version"
489
491
  puts "omq-cli #{OMQ::CLI::VERSION} (omq #{OMQ::VERSION})"
490
492
  exit
491
493
  }
@@ -496,6 +498,12 @@ module OMQ
496
498
  o.on("--examples") { CLI.page EXAMPLES
497
499
  exit }
498
500
 
501
+ o.separator "\nEnv vars:"
502
+ o.separator " OMQ_BACKEND (socket backend: ruby, rust, or libzmq)"
503
+ o.separator " OMQ_SERVER_KEY (client CURVE server key)"
504
+ o.separator " OMQ_SERVER_PUBLIC + OMQ_SERVER_SECRET (server CURVE keypair)"
505
+ o.separator " OMQ_CRYPTO (CURVE crypto backend: rbnacl or nuckle)"
506
+
499
507
  o.separator "\nExit codes: 0 = success, 1 = error, 2 = timeout"
500
508
  end
501
509
 
@@ -517,6 +525,8 @@ module OMQ
517
525
  opts[:type_name] = type_name.downcase
518
526
  end
519
527
 
528
+ apply_env_backend!(opts) if opts[:type_name] && !explicit_backend
529
+
520
530
  # Host shorthand (tcp://*:PORT, tcp://:PORT, tcp://localhost:PORT)
521
531
  # is normalized inside OMQ::Transport::TCP — see its
522
532
  # #normalize_bind_host / #normalize_connect_host / #loopback_host.
@@ -567,6 +577,53 @@ module OMQ
567
577
  end
568
578
 
569
579
 
580
+ # Loads an optional OMQ socket backend while parsing backend flags.
581
+ #
582
+ def load_socket_backend!(backend)
583
+ case backend
584
+ when :ruby
585
+ nil
586
+ when :rust
587
+ require "omq/backend/rust"
588
+ when :libzmq
589
+ require "omq/backend/libzmq"
590
+ end
591
+ rescue LoadError => e
592
+ case backend
593
+ when :rust
594
+ abort "omq: --backend rust requires the omq-backend-rust gem (#{e.message})"
595
+ when :libzmq
596
+ abort "omq: --backend #{backend} requires the omq-backend-libzmq gem and system libzmq 4.x (#{e.message})"
597
+ else
598
+ abort "omq: could not load backend #{backend} (#{e.message})"
599
+ end
600
+ end
601
+
602
+
603
+ # Applies OMQ_BACKEND when no explicit backend flag was given.
604
+ #
605
+ def apply_env_backend!(opts)
606
+ env_backend = ENV["OMQ_BACKEND"]
607
+ return if env_backend.nil? || env_backend.empty?
608
+
609
+ unless %w[ruby rust libzmq].include?(env_backend)
610
+ abort "omq: invalid OMQ_BACKEND=#{env_backend.inspect} (use ruby, rust, or libzmq)"
611
+ end
612
+
613
+ backend = env_backend.to_sym
614
+ load_socket_backend!(backend)
615
+ opts[:backend] = backend
616
+ opts[:ffi] = false
617
+ end
618
+
619
+
620
+ # Maps legacy CLI spelling to the backend name supported by omq.
621
+ #
622
+ def normalize_backend(backend)
623
+ backend == :ffi ? :libzmq : backend
624
+ end
625
+
626
+
570
627
  # Validates option combinations, aborting on invalid combos.
571
628
  #
572
629
  # @param opts [Hash] parsed options from {#parse}
@@ -625,7 +682,7 @@ module OMQ
625
682
  end
626
683
 
627
684
  (opts[:connects] + opts[:binds]).each do |url|
628
- abort "inproc not supported, use tcp:// or ipc://" if url.include?("inproc://")
685
+ abort "ruby:// not supported in CLI, use tcp:// or ipc://" if url.include?("ruby://")
629
686
  end
630
687
 
631
688
  all_urls = if type_name == "pipe"
@@ -62,6 +62,7 @@ module OMQ
62
62
  :curve_server,
63
63
  :curve_server_key,
64
64
  :crypto,
65
+ :backend,
65
66
  :ffi,
66
67
  :stdin_is_tty,
67
68
  ) do
@@ -48,7 +48,7 @@ module OMQ
48
48
 
49
49
 
50
50
  def setup_socket
51
- @sock = @config.ffi ? OMQ.const_get(@socket_sym).new(backend: :ffi) : OMQ.const_get(@socket_sym).new
51
+ @sock = OMQ.const_get(@socket_sym).new(**OMQ::CLI::SocketSetup.backend_kwargs(@config))
52
52
  OMQ::CLI::SocketSetup.apply_options(@sock, @config)
53
53
  OMQ::CLI::SocketSetup.apply_recv_maxsz(@sock, @config)
54
54
  @sock.identity = @config.identity if @config.identity
data/lib/omq/cli/pipe.rb CHANGED
@@ -46,6 +46,7 @@ module OMQ
46
46
 
47
47
  def run_sequential(task)
48
48
  set_pipe_process_title
49
+ log_backend
49
50
  in_eps, out_eps = resolve_endpoints
50
51
  @pull, @push = build_pull_push(in_eps, out_eps)
51
52
  compile_expr
@@ -83,7 +84,7 @@ module OMQ
83
84
 
84
85
 
85
86
  def build_pull_push(in_eps, out_eps)
86
- kwargs = config.ffi ? { backend: :ffi } : {}
87
+ kwargs = SocketSetup.backend_kwargs(config)
87
88
 
88
89
  pull = OMQ::PULL.new(**kwargs).tap do |sock|
89
90
  SocketSetup.apply_options(sock, config)
@@ -152,6 +153,7 @@ module OMQ
152
153
 
153
154
  def run_parallel(task)
154
155
  set_pipe_process_title
156
+ log_backend
155
157
  OMQ.freeze_for_ractors!
156
158
 
157
159
  in_eps, out_eps = resolve_endpoints
@@ -240,6 +242,12 @@ module OMQ
240
242
  end
241
243
  end
242
244
 
245
+
246
+ def log_backend
247
+ return unless config.verbose >= 1
248
+ $stderr.write("#{Term.log_prefix(config.timestamps)}omq: backend: #{SocketSetup.backend_name(config)}\n")
249
+ end
250
+
243
251
  end
244
252
  end
245
253
  end
@@ -38,7 +38,7 @@ module OMQ
38
38
 
39
39
 
40
40
  def setup_sockets
41
- kwargs = @config.ffi ? { backend: :ffi } : {}
41
+ kwargs = OMQ::CLI::SocketSetup.backend_kwargs(@config)
42
42
  @pull = OMQ::PULL.new(**kwargs)
43
43
  @push = OMQ::PUSH.new(**kwargs)
44
44
  OMQ::CLI::SocketSetup.apply_options(@pull, @config)
@@ -86,6 +86,31 @@ module OMQ
86
86
  end
87
87
 
88
88
 
89
+ # Returns socket constructor kwargs for the selected backend.
90
+ #
91
+ def self.backend_kwargs(config)
92
+ backend = backend_name(config)
93
+ return {} if backend.nil? || backend == :ruby
94
+
95
+ { backend: backend }
96
+ end
97
+
98
+
99
+ # Returns the effective backend name after legacy --ffi handling.
100
+ #
101
+ def self.backend_name(config)
102
+ backend = if config.respond_to?(:ffi) && config.ffi
103
+ :libzmq
104
+ elsif config.respond_to?(:backend)
105
+ config.backend
106
+ else
107
+ :ruby
108
+ end
109
+ backend = :libzmq if backend == :ffi
110
+ backend || :ruby
111
+ end
112
+
113
+
89
114
  # Apply common socket options from +config+ to +sock+.
90
115
  #
91
116
  def self.apply_options(sock, config)
@@ -105,7 +130,7 @@ module OMQ
105
130
  # Create and fully configure a socket from +klass+ and +config+.
106
131
  #
107
132
  def self.build(klass, config)
108
- sock = config.ffi ? klass.new(backend: :ffi) : klass.new
133
+ sock = klass.new(**backend_kwargs(config))
109
134
  sock.conflate = true if config.conflate && %w[pub radio].include?(config.type_name)
110
135
  apply_options(sock, config)
111
136
  apply_recv_maxsz(sock, config)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OMQ
4
4
  module CLI
5
- VERSION = "0.18.0"
5
+ VERSION = "0.19.0"
6
6
  end
7
7
  end
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.18.0
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.27'
18
+ version: '0.28'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0.27'
25
+ version: '0.28'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: ffi
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -132,7 +132,7 @@ files:
132
132
  - lib/omq/cli/term.rb
133
133
  - lib/omq/cli/transient_monitor.rb
134
134
  - lib/omq/cli/version.rb
135
- homepage: https://github.com/paddor/omq-cli
135
+ homepage: https://github.com/zeromq/omq.rb/tree/main/gems/omq-cli
136
136
  licenses:
137
137
  - ISC
138
138
  metadata: {}
@@ -150,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
150
  - !ruby/object:Gem::Version
151
151
  version: '0'
152
152
  requirements: []
153
- rubygems_version: 4.0.6
153
+ rubygems_version: 4.0.16
154
154
  specification_version: 4
155
155
  summary: ZeroMQ CLI — pipe, filter, and transform messages from the terminal
156
156
  test_files: []