cztop 1.1.0 → 1.1.1

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: a73261e3bc3a15f8302b4e7eca58eb25393cfe301d9b755a7291bf35ef126674
4
- data.tar.gz: 5d1f75990a28eba2460cfd61e5825492bcd66604bd7990dbcdf4a74db00c0e7e
3
+ metadata.gz: 066d9600730db24e49b9b83fee30840882432df13dde7372f9e7f969b3774b33
4
+ data.tar.gz: 67ee0941443c6a9eadf7d27b4bc084e00d20c2dc7ca4448fc6459edc93a88efb
5
5
  SHA512:
6
- metadata.gz: 9ebe5b00e21a32e0b645f50a789a8818dddf31d4ed69a74c247b30b443e02cd4cdd05121f3cd2bc286c41e21e09bc6feb0b304fc245d9be6dbaf648e287c0bb8
7
- data.tar.gz: cd3c05936f8ce4c0c8c6b8f3774d70bfaf1a7b85d9770e159fb0aed4ae0628f97e74bbaa76742b7518a06d257dd6c22504dc2285a827fcc36b5bb5f59bf51ac8
6
+ metadata.gz: c8852f4b214935ef5b0302a7a6481a2f51148dfc6fac939ca9d9b5f665ab06d8242418bdbe996edb9ad6fe010a84a540fd9b24e379cdd5b3197fd3c7a80f0478
7
+ data.tar.gz: 506f6e6b9a927e8c68d60c410d3ed84abfae1edef435c43baae8855eb2f02409815692eb4cf18501298ab5c8f9c988cdd48d308a93eb20d1ae136bf51c8283fe
@@ -4,13 +4,13 @@ on: [push,pull_request]
4
4
 
5
5
  jobs:
6
6
  build:
7
- runs-on: ubuntu-20.04
7
+ runs-on: ubuntu-22.04
8
8
  steps:
9
- - uses: actions/checkout@v2
9
+ - uses: actions/checkout@v4
10
10
  - name: Set up Ruby
11
11
  uses: ruby/setup-ruby@v1
12
12
  with:
13
- ruby-version: 3.1
13
+ ruby-version: 3.3
14
14
  - name: Install CZMQ
15
15
  run: sudo apt-get install libczmq-dev
16
16
  - name: Run the default task
@@ -7,11 +7,11 @@ jobs:
7
7
  runs-on: ubuntu-22.04
8
8
  timeout-minutes: 15
9
9
  steps:
10
- - uses: actions/checkout@v2
10
+ - uses: actions/checkout@v4
11
11
  - name: Set up Ruby
12
12
  uses: ruby/setup-ruby@v1
13
13
  with:
14
- ruby-version: 3.3
14
+ ruby-version: '3.3'
15
15
  - name: Install ZMQ and CZMQ
16
16
  run: |
17
17
  export PKG_CONFIG_PATH=$HOME/lib/pkgconfig # custom libs (for linking)
@@ -8,12 +8,11 @@ jobs:
8
8
  strategy:
9
9
  matrix:
10
10
  ruby:
11
- - 3.0
12
- - 3.1
13
- - 3.2
14
- - 3.3
11
+ - '3.1'
12
+ - '3.2'
13
+ - '3.3'
15
14
  steps:
16
- - uses: actions/checkout@v2
15
+ - uses: actions/checkout@v4
17
16
  - name: Set up Ruby ${{ matrix.ruby }}
18
17
  uses: ruby/setup-ruby@v1
19
18
  with:
data/.projections.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "lib/*.rb": { "alternate": "spec/{}_spec.rb" },
3
+ "spec/*_spec.rb": { "alternate": "lib/{}.rb", "dispatch": "bundle exec rspec -ff {file}" }
4
+ }
data/CHANGES.md CHANGED
@@ -1,3 +1,12 @@
1
+ 1.1.1 (1/4/2024)
2
+ -----
3
+ * speed up Async::IO#wait_readable and #wait_writable
4
+ * update CI and specs
5
+
6
+ 1.1.0 (1/4/2024)
7
+ -----
8
+ * support Async
9
+
1
10
  1.1.0.pre1 (10/17/2022)
2
11
  -----
3
12
  * modernize syntax using Rubocop
data/README.md CHANGED
@@ -10,6 +10,76 @@ FFI binding of [CZMQ](https://github.com/zeromq/czmq) and has a focus on being
10
10
  easy to use for Rubyists (POLS) and providing first class support for security
11
11
  mechanisms (like CURVE).
12
12
 
13
+
14
+ ## Example with Async
15
+
16
+ ```ruby
17
+ #! /usr/bin/env ruby
18
+
19
+ require 'cztop/async'
20
+
21
+ Async do |task|
22
+ task.async do |t|
23
+ socket = CZTop::Socket::REP.new("inproc://req_rep_example")
24
+ io = Async::IO.try_convert socket
25
+
26
+ socket.options.rcvtimeo = 50 # ms
27
+
28
+ loop do
29
+ msg = io.receive
30
+ puts "<<< #{msg.to_a.inspect}"
31
+ io << msg.to_a.map(&:upcase)
32
+ rescue IO::TimeoutError
33
+ break
34
+ end
35
+
36
+ puts "REP done."
37
+ end
38
+
39
+ task.async do
40
+ socket = CZTop::Socket::REQ.new("inproc://req_rep_example")
41
+ io = Async::IO.try_convert socket
42
+
43
+ 10.times do |i|
44
+ io << "foobar ##{i}"
45
+ msg = io.receive
46
+ puts ">>> #{msg.to_a.inspect}"
47
+ end
48
+
49
+ puts "REQ done."
50
+ end
51
+ end
52
+ ```
53
+
54
+
55
+ Output:
56
+ ```
57
+ <<< ["foobar #0"]
58
+ >>> ["FOOBAR #0"]
59
+ <<< ["foobar #1"]
60
+ >>> ["FOOBAR #1"]
61
+ <<< ["foobar #2"]
62
+ >>> ["FOOBAR #2"]
63
+ <<< ["foobar #3"]
64
+ >>> ["FOOBAR #3"]
65
+ <<< ["foobar #4"]
66
+ >>> ["FOOBAR #4"]
67
+ <<< ["foobar #5"]
68
+ >>> ["FOOBAR #5"]
69
+ <<< ["foobar #6"]
70
+ >>> ["FOOBAR #6"]
71
+ <<< ["foobar #7"]
72
+ >>> ["FOOBAR #7"]
73
+ <<< ["foobar #8"]
74
+ >>> ["FOOBAR #8"]
75
+ <<< ["foobar #9"]
76
+ >>> ["FOOBAR #9"]
77
+ REQ done.
78
+ REP done.
79
+ 0.46user 0.09system 0:00.60elapsed 90%CPU (0avgtext+0avgdata 47296maxresident)k
80
+ 0inputs+0outputs (0major+13669minor)pagefaults 0swaps
81
+ ```
82
+
13
83
  ## Overview
14
84
 
15
85
  ### Class Hierarchy
@@ -59,24 +129,12 @@ More information in the [API documentation](http://www.rubydoc.info/github/paddo
59
129
 
60
130
  ### Features
61
131
 
132
+ * Ruby idiomatic API
62
133
  * compatible with [Async](https://github.com/socketry/async) / [Async::IO](https://github.com/socketry/async-io)
63
- * Ruby-like API
64
- * method names
65
- * sending a message via a socket is done with `Socket#<<`
66
- * `socket << "simple message"`
67
- * `socket << ["multi", "frame", "message"]`
68
- * `#x=` methods instead of `#set_x` (e.g. socket options)
69
- * `#[]` where it makes sense (e.g. on a Message, Config, or Certificate)
70
- * no manual error checking needed
71
- * if there's an error, an appropriate exception is raised
72
- * of course, no manual dealing with the ZMQ context
73
- * easy security
74
- * use `Socket#CURVE_server!(cert)` on the server
75
- * and `Socket#CURVE_client!(client_cert, server_cert)` on the client
76
- * socket types as Ruby classes
77
- * no need to manually pass type constants
78
- * but you can: `CZTop::Socket.new_by_type(:REP)`
79
- * e.g. `#subscribe` only exists on `CZTop::Socket::SUB`
134
+ * errors as exceptions
135
+ * CURVE security
136
+ * supports CZMQ DRAFT API
137
+ * extensive spec coverage
80
138
 
81
139
  ## Requirements
82
140
 
@@ -95,9 +153,11 @@ On macOS using Homebrew, run:
95
153
 
96
154
  ### Supported Rubies
97
155
 
98
- * MRI (3.0, 3.2, 3.3)
99
- * Rubinius (HEAD)
100
- * JRuby 9000 (HEAD)
156
+ At least:
157
+
158
+ * Ruby 3.0, 3.1, 3.2, 3.3
159
+
160
+ Using CZTop with Async will require Ruby 3.2 because of the use of `IO::TimeoutError`, which was introduced in Ruby 3.2.
101
161
 
102
162
  ## Installation
103
163
 
data/cztop.gemspec CHANGED
@@ -8,7 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Patrik Wenger"]
9
9
  spec.email = ["paddor@gmail.com"]
10
10
 
11
- spec.summary = %q{CZMQ Ruby binding based on the generated low-level FFI bindings of CZMQ}
11
+ spec.summary = 'CZMQ FFI binding to bring ZMQ sockets to Ruby'
12
+ spec.description = 'CZMQ binding based on the generated low-level FFI bindings of CZMQ'
12
13
  spec.homepage = "https://rubygems.org/gems/cztop"
13
14
  spec.license = "ISC"
14
15
  spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
@@ -22,7 +23,7 @@ Gem::Specification.new do |spec|
22
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
24
  spec.require_paths = ["lib"]
24
25
 
25
- spec.add_runtime_dependency "czmq-ffi-gen", "~> 1.1.0.pre1"
26
+ spec.add_runtime_dependency "czmq-ffi-gen", "~> 1.1.0"
26
27
 
27
28
  spec.add_development_dependency "bundler"
28
29
  spec.add_development_dependency "rake"
@@ -32,6 +33,9 @@ Gem::Specification.new do |spec|
32
33
  spec.add_development_dependency "pry"
33
34
  spec.add_development_dependency "yard"
34
35
  spec.add_development_dependency "rubocop", "~> 1.36.0"
35
- spec.add_development_dependency "async", ">= 2.0.1"
36
- spec.add_development_dependency "async-io"
36
+
37
+ if RUBY_VERSION >= '3.1'
38
+ spec.add_development_dependency "async", ">= 2.0.1"
39
+ spec.add_development_dependency "async-io"
40
+ end
37
41
  end
@@ -4,37 +4,32 @@ require 'cztop/async'
4
4
 
5
5
  Async do |task|
6
6
  task.async do |t|
7
- socket = CZTop::Socket::REP.new("ipc:///tmp/req_rep_example")
7
+ socket = CZTop::Socket::REP.new("inproc://req_rep_example")
8
+ io = Async::IO.try_convert socket
8
9
 
9
- # Simply echo every message, with every frame String#upcase'd.
10
- socket.options.rcvtimeo = 3
11
- io = Async::IO.try_convert socket
10
+ socket.options.rcvtimeo = 50 # ms
12
11
 
13
- msg = io.receive
14
- puts "<<< #{msg.to_a.inspect}"
15
- io << msg.to_a.map(&:upcase)
12
+ loop do
13
+ msg = io.receive
14
+ puts "<<< #{msg.to_a.inspect}"
15
+ io << msg.to_a.map(&:upcase)
16
+ rescue IO::TimeoutError
17
+ break
18
+ end
16
19
 
17
20
  puts "REP done."
18
21
  end
19
22
 
20
23
  task.async do
21
- socket = CZTop::Socket::REQ.new("ipc:///tmp/req_rep_example")
22
- puts ">>> Socket connected."
24
+ socket = CZTop::Socket::REQ.new("inproc://req_rep_example")
25
+ io = Async::IO.try_convert socket
23
26
 
24
- io = Async::IO.try_convert socket
25
- # sleep 5
26
- io << "foobar"
27
+ 10.times do |i|
28
+ io << "foobar ##{i}"
29
+ msg = io.receive
30
+ puts ">>> #{msg.to_a.inspect}"
31
+ end
27
32
 
28
- socket.options.rcvtimeo = 3
29
- msg = io.receive
30
- puts ">>> #{msg.to_a.inspect}"
31
33
  puts "REQ done."
32
34
  end
33
-
34
- task.async do
35
- 6.times do
36
- sleep 0.5
37
- puts "tick"
38
- end
39
- end
40
35
  end
data/lib/cztop/async.rb CHANGED
@@ -49,6 +49,8 @@ module Async
49
49
  def wait_readable(timeout = read_timeout)
50
50
  @io_fd ||= ::IO.for_fd @io.fd, autoclose: false
51
51
 
52
+ return true if @io.readable?
53
+
52
54
  if timeout
53
55
  timeout_at = now + timeout
54
56
 
@@ -67,6 +69,8 @@ module Async
67
69
  def wait_writable(timeout = write_timeout)
68
70
  @io_fd ||= ::IO.for_fd @io.fd, autoclose: false
69
71
 
72
+ return true if @io.writable?
73
+
70
74
  if timeout
71
75
  timeout_at = now + timeout
72
76
 
@@ -13,6 +13,12 @@ module CZTop
13
13
  include HasFFIDelegate
14
14
  extend CZTop::HasFFIDelegate::ClassMethods
15
15
 
16
+ unless ::CZMQ::FFI::Zsys.has_curve
17
+ def self.new(...)
18
+ fail NotImplementedError
19
+ end
20
+ end
21
+
16
22
  # Initializes a new certificate store.
17
23
  #
18
24
  # @param location [String, #to_s, nil] location the path to the
@@ -8,6 +8,13 @@ module CZTop
8
8
  extend CZTop::HasFFIDelegate::ClassMethods
9
9
  include ::CZMQ::FFI
10
10
 
11
+ unless ::CZMQ::FFI::Zsys.has_curve
12
+ def self.new(...)
13
+ fail NotImplementedError
14
+ end
15
+ end
16
+
17
+
11
18
  # Warns if CURVE security isn't available.
12
19
  # @return [void]
13
20
  def self.check_curve_availability
@@ -57,6 +64,16 @@ module CZTop
57
64
  end
58
65
 
59
66
 
67
+ KEY_ALL_ZERO = '0000000000000000000000000000000000000000'
68
+
69
+ # @return [Boolean] whether one of the keys is all zeros (happens when CURVE is not available, i.e. libzmq was
70
+ # compiled without libsodium)
71
+ # @see .check_curve_availability
72
+ def zero?
73
+ public_key(format: :z85) == KEY_ALL_ZERO || secret_key(format: :z85) == KEY_ALL_ZERO
74
+ end
75
+
76
+
60
77
  # Returns the public key either as Z85-encoded ASCII string (default) or
61
78
  # binary string.
62
79
  # @param format [Symbol] +:z85+ for Z85, +:binary+ for binary
data/lib/cztop/version.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CZTop
4
4
 
5
- VERSION = '1.1.0'
5
+ VERSION = '1.1.1'
6
6
 
7
7
  end
@@ -111,58 +111,8 @@ module CZTop
111
111
 
112
112
  # @!endgroup
113
113
 
114
- # @!group Security Mechanisms
115
-
116
- # @return [Boolean] whether this zocket is a CURVE server
117
- def CURVE_server?
118
- Zsock.curve_server(@zocket).positive?
119
- end
120
-
121
-
122
- # Make this zocket a CURVE server.
123
- # @param bool [Boolean]
124
- # @note You'll have to use a {CZTop::Authenticator}.
125
- def CURVE_server=(bool)
126
- Zsock.set_curve_server(@zocket, bool ? 1 : 0)
127
- end
128
-
129
-
130
- # @return [String] Z85 encoded server key set
131
- # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
132
- # supported
133
- def CURVE_serverkey
134
- CURVE_key(:curve_serverkey)
135
- end
136
114
 
137
-
138
- # Get one of the CURVE keys.
139
- # @param key_name [Symbol] something like +:curve_serverkey+
140
- # @return [String, nil] key, if CURVE is supported and active, or nil
141
- def CURVE_key(key_name)
142
- return nil if mechanism != :CURVE
143
-
144
- ptr = Zsock.__send__(key_name, @zocket)
145
- return nil if ptr.null?
146
-
147
- ptr.read_string
148
- end
149
- private :CURVE_key
150
-
151
- # Sets the server's public key, so the zocket can authenticate the
152
- # remote server.
153
- # @param key [String] Z85 (40 bytes) or binary (32 bytes) server key
154
- # @raise [ArgumentError] if key has wrong size
155
- def CURVE_serverkey=(key)
156
- case key.bytesize
157
- when 40
158
- Zsock.set_curve_serverkey(@zocket, key)
159
- when 32
160
- ptr = ::FFI::MemoryPointer.from_string(key)
161
- Zsock.set_curve_serverkey_bin(@zocket, ptr)
162
- else
163
- raise ArgumentError, format('invalid server key: %p', key)
164
- end
165
- end
115
+ # @!group Security Mechanisms
166
116
 
167
117
  # supported security mechanisms and their macro value equivalent
168
118
  MECHANISMS = {
@@ -182,20 +132,92 @@ module CZTop
182
132
  raise format('unknown ZMQ security mechanism code: %i', code)
183
133
  end
184
134
 
135
+ if ::CZMQ::FFI::Zsys.has_curve
136
+ # @return [Boolean] whether this zocket is a CURVE server
137
+ def CURVE_server?
138
+ Zsock.curve_server(@zocket).positive?
139
+ end
185
140
 
186
- # @return [String] Z85 encoded secret key set
187
- # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
188
- # supported
189
- def CURVE_secretkey
190
- CURVE_key(:curve_secretkey)
191
- end
141
+
142
+ # Make this zocket a CURVE server.
143
+ # @param bool [Boolean]
144
+ # @note You'll have to use a {CZTop::Authenticator}.
145
+ def CURVE_server=(bool)
146
+ Zsock.set_curve_server(@zocket, bool ? 1 : 0)
147
+ end
148
+
149
+
150
+ # @return [String] Z85 encoded server key set
151
+ # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
152
+ # supported
153
+ def CURVE_serverkey
154
+ CURVE_key(:curve_serverkey)
155
+ end
156
+
157
+
158
+ # Get one of the CURVE keys.
159
+ # @param key_name [Symbol] something like +:curve_serverkey+
160
+ # @return [String, nil] key, if CURVE is supported and active, or nil
161
+ def CURVE_key(key_name)
162
+ return nil if mechanism != :CURVE
163
+
164
+ ptr = Zsock.__send__(key_name, @zocket)
165
+ return nil if ptr.null?
166
+
167
+ ptr.read_string
168
+ end
169
+ private :CURVE_key
170
+
171
+ # Sets the server's public key, so the zocket can authenticate the
172
+ # remote server.
173
+ # @param key [String] Z85 (40 bytes) or binary (32 bytes) server key
174
+ # @raise [ArgumentError] if key has wrong size
175
+ def CURVE_serverkey=(key)
176
+ case key.bytesize
177
+ when 40
178
+ Zsock.set_curve_serverkey(@zocket, key)
179
+ when 32
180
+ ptr = ::FFI::MemoryPointer.from_string(key)
181
+ Zsock.set_curve_serverkey_bin(@zocket, ptr)
182
+ else
183
+ raise ArgumentError, format('invalid server key: %p', key)
184
+ end
185
+ end
192
186
 
193
187
 
194
- # @return [String] Z85 encoded public key set
195
- # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
196
- # supported
197
- def CURVE_publickey
198
- CURVE_key(:curve_publickey)
188
+ # @return [String] Z85 encoded secret key set
189
+ # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
190
+ # supported
191
+ def CURVE_secretkey
192
+ CURVE_key(:curve_secretkey)
193
+ end
194
+
195
+
196
+ # @return [String] Z85 encoded public key set
197
+ # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
198
+ # supported
199
+ def CURVE_publickey
200
+ CURVE_key(:curve_publickey)
201
+ end
202
+ else
203
+ def CURVE_server?
204
+ fail NotImplementedError
205
+ end
206
+ def CURVE_server=(...)
207
+ fail NotImplementedError
208
+ end
209
+ def CURVE_serverkey(...)
210
+ fail NotImplementedError
211
+ end
212
+ def CURVE_serverkey=(...)
213
+ fail NotImplementedError
214
+ end
215
+ def CURVE_secretkey(...)
216
+ fail NotImplementedError
217
+ end
218
+ def CURVE_publickey(...)
219
+ fail NotImplementedError
220
+ end
199
221
  end
200
222
 
201
223
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cztop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.1.0.pre1
19
+ version: 1.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.1.0.pre1
26
+ version: 1.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -164,7 +164,7 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
- description:
167
+ description: CZMQ binding based on the generated low-level FFI bindings of CZMQ
168
168
  email:
169
169
  - paddor@gmail.com
170
170
  executables:
@@ -177,6 +177,7 @@ files:
177
177
  - ".github/workflows/draft_api.yml"
178
178
  - ".github/workflows/stable_api.yml"
179
179
  - ".gitignore"
180
+ - ".projections.json"
180
181
  - ".rspec"
181
182
  - ".rubocop.yml"
182
183
  - ".yardopts"
@@ -273,5 +274,5 @@ requirements: []
273
274
  rubygems_version: 3.5.3
274
275
  signing_key:
275
276
  specification_version: 4
276
- summary: CZMQ Ruby binding based on the generated low-level FFI bindings of CZMQ
277
+ summary: CZMQ FFI binding to bring ZMQ sockets to Ruby
277
278
  test_files: []