cztop 1.0.0 → 1.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/coverage.yml +20 -0
- data/.github/workflows/draft_api.yml +27 -0
- data/.github/workflows/{main.yml → stable_api.yml} +6 -6
- data/.rubocop.yml +175 -0
- data/CHANGES.md +8 -1
- data/Gemfile +5 -0
- data/README.md +3 -1
- data/ci/install-libczmq +22 -0
- data/ci/install-libzmq +22 -0
- data/cztop.gemspec +3 -2
- data/lib/cztop/actor.rb +55 -26
- data/lib/cztop/authenticator.rb +18 -9
- data/lib/cztop/beacon.rb +22 -10
- data/lib/cztop/cert_store.rb +8 -2
- data/lib/cztop/certificate.rb +47 -18
- data/lib/cztop/config/comments.rb +14 -3
- data/lib/cztop/config/serialization.rb +25 -5
- data/lib/cztop/config/traversing.rb +44 -13
- data/lib/cztop/config.rb +23 -9
- data/lib/cztop/frame.rb +23 -10
- data/lib/cztop/has_ffi_delegate.rb +11 -1
- data/lib/cztop/message/frames.rb +16 -2
- data/lib/cztop/message.rb +36 -22
- data/lib/cztop/metadata.rb +35 -24
- data/lib/cztop/monitor.rb +14 -5
- data/lib/cztop/poller/aggregated.rb +31 -15
- data/lib/cztop/poller/zmq.rb +25 -22
- data/lib/cztop/poller/zpoller.rb +18 -6
- data/lib/cztop/poller.rb +43 -18
- data/lib/cztop/polymorphic_zsock_methods.rb +6 -1
- data/lib/cztop/proxy.rb +34 -19
- data/lib/cztop/send_receive_methods.rb +5 -1
- data/lib/cztop/socket/types.rb +128 -22
- data/lib/cztop/socket.rb +23 -18
- data/lib/cztop/version.rb +5 -1
- data/lib/cztop/z85/padded.rb +12 -3
- data/lib/cztop/z85/pipe.rb +40 -17
- data/lib/cztop/z85.rb +17 -6
- data/lib/cztop/zap.rb +57 -32
- data/lib/cztop/zsock_options.rb +155 -122
- data/lib/cztop.rb +2 -1
- metadata +28 -10
- data/.ruby-version +0 -1
data/lib/cztop/authenticator.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CZTop
|
3
4
|
# Authentication for ZeroMQ security mechanisms.
|
4
5
|
#
|
5
6
|
# This is implemented using an {Actor}.
|
6
7
|
#
|
7
8
|
# @see http://api.zeromq.org/czmq3-0:zauth
|
8
9
|
class Authenticator
|
10
|
+
|
9
11
|
include ::CZMQ::FFI
|
10
12
|
|
11
13
|
# function pointer to the +zauth()+ function
|
12
14
|
ZAUTH_FPTR = ::CZMQ::FFI.ffi_libraries.each do |dl|
|
13
|
-
fptr = dl.find_function(
|
15
|
+
fptr = dl.find_function('zauth')
|
14
16
|
break fptr if fptr
|
15
17
|
end
|
16
18
|
raise LoadError, "couldn't find zauth()" if ZAUTH_FPTR.nil?
|
@@ -23,6 +25,7 @@ module CZTop
|
|
23
25
|
def initialize(cert_store = nil)
|
24
26
|
if cert_store
|
25
27
|
raise ArgumentError unless cert_store.is_a?(CertStore)
|
28
|
+
|
26
29
|
cert_store = cert_store.ffi_delegate
|
27
30
|
cert_store.__undef_finalizer # native object is now owned by zauth() actor
|
28
31
|
end
|
@@ -38,13 +41,15 @@ module CZTop
|
|
38
41
|
@actor.terminate
|
39
42
|
end
|
40
43
|
|
44
|
+
|
41
45
|
# Enable verbose logging of commands and activity.
|
42
46
|
# @return [void]
|
43
47
|
def verbose!
|
44
|
-
@actor <<
|
48
|
+
@actor << 'VERBOSE'
|
45
49
|
@actor.wait
|
46
50
|
end
|
47
51
|
|
52
|
+
|
48
53
|
# Add a list of IP addresses to the whitelist. For _NULL_, all clients
|
49
54
|
# from these addresses will be accepted. For _PLAIN_ and _CURVE_, they
|
50
55
|
# will be allowed to continue with authentication.
|
@@ -52,10 +57,11 @@ module CZTop
|
|
52
57
|
# @param addrs [String] IP address(es) to allow
|
53
58
|
# @return [void]
|
54
59
|
def allow(*addrs)
|
55
|
-
@actor << [
|
60
|
+
@actor << ['ALLOW', *addrs]
|
56
61
|
@actor.wait
|
57
62
|
end
|
58
63
|
|
64
|
+
|
59
65
|
# Add a list of IP addresses to the blacklist. For all security
|
60
66
|
# mechanisms, this rejects the connection without any further
|
61
67
|
# authentication. Use either a whitelist, or a blacklist, not not both. If
|
@@ -65,22 +71,23 @@ module CZTop
|
|
65
71
|
# @param addrs [String] IP address(es) to deny
|
66
72
|
# @return [void]
|
67
73
|
def deny(*addrs)
|
68
|
-
@actor << [
|
74
|
+
@actor << ['DENY', *addrs]
|
69
75
|
@actor.wait
|
70
76
|
end
|
71
77
|
|
78
|
+
|
72
79
|
# Configure PLAIN security mechanism using a plain-text password file. The
|
73
80
|
# password file will be reloaded automatically if modified externally.
|
74
81
|
#
|
75
82
|
# @param filename [String] path to the password file
|
76
83
|
# @return [void]
|
77
84
|
def plain(filename)
|
78
|
-
@actor << [
|
85
|
+
@actor << ['PLAIN', *filename]
|
79
86
|
@actor.wait
|
80
87
|
end
|
81
88
|
|
82
89
|
# used to allow any CURVE client
|
83
|
-
ALLOW_ANY =
|
90
|
+
ALLOW_ANY = '*'
|
84
91
|
|
85
92
|
# Configure CURVE authentication, using a directory that holds all public
|
86
93
|
# client certificates, i.e. their public keys. The certificates must have been
|
@@ -90,15 +97,17 @@ module CZTop
|
|
90
97
|
# @param directory [String] the directory to take the keys from
|
91
98
|
# @return [void]
|
92
99
|
def curve(directory = ALLOW_ANY)
|
93
|
-
@actor << [
|
100
|
+
@actor << ['CURVE', directory]
|
94
101
|
@actor.wait
|
95
102
|
end
|
96
103
|
|
104
|
+
|
97
105
|
# Configure GSSAPI authentication.
|
98
106
|
# @return [void]
|
99
107
|
def gssapi
|
100
|
-
@actor <<
|
108
|
+
@actor << 'GSSAPI'
|
101
109
|
@actor.wait
|
102
110
|
end
|
111
|
+
|
103
112
|
end
|
104
113
|
end
|
data/lib/cztop/beacon.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CZTop
|
2
4
|
# Used for LAN discovery and presence.
|
3
5
|
#
|
@@ -5,11 +7,12 @@ module CZTop
|
|
5
7
|
#
|
6
8
|
# @see http://api.zeromq.org/czmq3-0:zbeacon
|
7
9
|
class Beacon
|
10
|
+
|
8
11
|
include ::CZMQ::FFI
|
9
12
|
|
10
13
|
# function pointer to the +zbeacon()+ function
|
11
14
|
ZBEACON_FPTR = ::CZMQ::FFI.ffi_libraries.each do |dl|
|
12
|
-
fptr = dl.find_function(
|
15
|
+
fptr = dl.find_function('zbeacon')
|
13
16
|
break fptr if fptr
|
14
17
|
end
|
15
18
|
raise LoadError, "couldn't find zbeacon()" if ZBEACON_FPTR.nil?
|
@@ -28,12 +31,14 @@ module CZTop
|
|
28
31
|
@actor.terminate
|
29
32
|
end
|
30
33
|
|
34
|
+
|
31
35
|
# Enable verbose logging of commands and activity.
|
32
36
|
# @return [void]
|
33
37
|
def verbose!
|
34
|
-
@actor <<
|
38
|
+
@actor << 'VERBOSE'
|
35
39
|
end
|
36
40
|
|
41
|
+
|
37
42
|
# Run the beacon on the specified UDP port.
|
38
43
|
#
|
39
44
|
# @param port [Integer] port number to
|
@@ -43,7 +48,7 @@ module CZTop
|
|
43
48
|
# interrupted
|
44
49
|
# @raise [NotImplementedError] if the system doesn't support UDP broadcasts
|
45
50
|
def configure(port)
|
46
|
-
@actor.send_picture(
|
51
|
+
@actor.send_picture('si', :string, 'CONFIGURE', :int, port)
|
47
52
|
ptr = Zstr.recv(@actor)
|
48
53
|
|
49
54
|
# NULL if context terminated or interrupted
|
@@ -64,42 +69,49 @@ module CZTop
|
|
64
69
|
# @raise [ArgumentError] if data is longer than {MAX_BEACON_DATA} bytes
|
65
70
|
# @return [void]
|
66
71
|
def publish(data, interval)
|
67
|
-
raise ArgumentError,
|
68
|
-
|
69
|
-
|
72
|
+
raise ArgumentError, 'data too long' if data.bytesize > MAX_BEACON_DATA
|
73
|
+
|
74
|
+
@actor.send_picture('sbi', :string, 'PUBLISH', :string, data,
|
75
|
+
:int, data.bytesize, :int, interval)
|
70
76
|
end
|
71
77
|
|
78
|
+
|
72
79
|
# Stop broadcasting the beacon.
|
73
80
|
# @return [void]
|
74
81
|
def silence
|
75
|
-
@actor <<
|
82
|
+
@actor << 'SILENCE'
|
76
83
|
end
|
77
84
|
|
85
|
+
|
78
86
|
# Start listening to beacons from peers.
|
79
87
|
# @param filter [String] do a prefix match on received beacons
|
80
88
|
# @return [void]
|
81
89
|
def subscribe(filter)
|
82
|
-
@actor.send_picture(
|
90
|
+
@actor.send_picture('sb', :string, 'SUBSCRIBE',
|
83
91
|
:string, filter, :int, filter.bytesize)
|
84
92
|
end
|
85
93
|
|
94
|
+
|
86
95
|
# Just like {#subscribe}, but subscribe to all peer beacons.
|
87
96
|
# @return [void]
|
88
97
|
def listen
|
89
|
-
@actor.send_picture(
|
98
|
+
@actor.send_picture('sb', :string, 'SUBSCRIBE',
|
90
99
|
:string, nil, :int, 0)
|
91
100
|
end
|
92
101
|
|
102
|
+
|
93
103
|
# Stop listening to other peers.
|
94
104
|
# @return [void]
|
95
105
|
def unsubscribe
|
96
|
-
@actor <<
|
106
|
+
@actor << 'UNSUBSCRIBE'
|
97
107
|
end
|
98
108
|
|
109
|
+
|
99
110
|
# Receive next beacon from a peer.
|
100
111
|
# @return [Message] 2-frame message with ([ipaddr, data])
|
101
112
|
def receive
|
102
113
|
@actor.receive
|
103
114
|
end
|
115
|
+
|
104
116
|
end
|
105
117
|
end
|
data/lib/cztop/cert_store.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
module CZTop
|
4
|
-
|
5
6
|
# A store for CURVE security certificates, either backed by files on disk or
|
6
7
|
# in-memory.
|
7
8
|
#
|
8
9
|
# @see http://api.zeromq.org/czmq3-0:zcertstore
|
9
10
|
class CertStore
|
11
|
+
|
10
12
|
include ::CZMQ::FFI
|
11
13
|
include HasFFIDelegate
|
12
14
|
extend CZTop::HasFFIDelegate::ClassMethods
|
@@ -21,6 +23,7 @@ module CZTop
|
|
21
23
|
attach_ffi_delegate(Zcertstore.new(location))
|
22
24
|
end
|
23
25
|
|
26
|
+
|
24
27
|
# Looks up a certificate in the store by its public key.
|
25
28
|
#
|
26
29
|
# @param pubkey [String] the public key in question, in Z85 format
|
@@ -29,9 +32,11 @@ module CZTop
|
|
29
32
|
def lookup(pubkey)
|
30
33
|
ptr = ffi_delegate.lookup(pubkey)
|
31
34
|
return nil if ptr.null?
|
35
|
+
|
32
36
|
Certificate.from_ffi_delegate(ptr)
|
33
37
|
end
|
34
38
|
|
39
|
+
|
35
40
|
# Inserts a new certificate into the store.
|
36
41
|
#
|
37
42
|
# @note The same public key must not be inserted more than once.
|
@@ -43,11 +48,12 @@ module CZTop
|
|
43
48
|
raise ArgumentError unless cert.is_a?(Certificate)
|
44
49
|
|
45
50
|
@_inserted_pubkeys ||= Set.new
|
46
|
-
pubkey
|
51
|
+
pubkey = cert.public_key
|
47
52
|
raise ArgumentError if @_inserted_pubkeys.include? pubkey
|
48
53
|
|
49
54
|
ffi_delegate.insert(cert.ffi_delegate)
|
50
55
|
@_inserted_pubkeys << pubkey
|
51
56
|
end
|
57
|
+
|
52
58
|
end
|
53
59
|
end
|
data/lib/cztop/certificate.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CZTop
|
2
4
|
# Represents a CZMQ::FFI::Zcert.
|
3
5
|
class Certificate
|
6
|
+
|
4
7
|
include HasFFIDelegate
|
5
8
|
extend CZTop::HasFFIDelegate::ClassMethods
|
6
9
|
include ::CZMQ::FFI
|
@@ -9,9 +12,11 @@ module CZTop
|
|
9
12
|
# @return [void]
|
10
13
|
def self.check_curve_availability
|
11
14
|
return if Zsys.has_curve
|
15
|
+
|
12
16
|
warn "CZTop: CURVE isn't available. Consider installing libsodium."
|
13
17
|
end
|
14
18
|
|
19
|
+
|
15
20
|
# Loads a certificate from a file.
|
16
21
|
# @param filename [String, Pathname, #to_s] path to certificate file
|
17
22
|
# @return [Certificate] the loaded certificate
|
@@ -20,6 +25,7 @@ module CZTop
|
|
20
25
|
from_ffi_delegate(ptr)
|
21
26
|
end
|
22
27
|
|
28
|
+
|
23
29
|
# Creates a new certificate from the given keys (either binary or in Z85
|
24
30
|
# format).
|
25
31
|
# @param public_key [String] binary public key (32 or 40 bytes)
|
@@ -29,25 +35,28 @@ module CZTop
|
|
29
35
|
# @raise [ArgumentError] if keys passed are invalid
|
30
36
|
# @raise [SystemCallError] if this fails
|
31
37
|
def self.new_from(public_key, secret_key = nil)
|
32
|
-
raise ArgumentError,
|
38
|
+
raise ArgumentError, 'no public key given' unless public_key
|
39
|
+
|
33
40
|
secret_key ||= "\x00" * 32 # no secret key given, provide 32 null bytes
|
34
41
|
|
35
42
|
# convert Z85 => binary
|
36
43
|
public_key = Z85.decode(public_key) if public_key.bytesize == 40
|
37
44
|
secret_key = Z85.decode(secret_key) if secret_key.bytesize == 40
|
38
45
|
|
39
|
-
raise ArgumentError,
|
40
|
-
raise ArgumentError,
|
46
|
+
raise ArgumentError, 'invalid public key size' if public_key.bytesize != 32
|
47
|
+
raise ArgumentError, 'invalid secret key size' if secret_key.bytesize != 32
|
41
48
|
|
42
49
|
ptr = Zcert.new_from(public_key, secret_key)
|
43
50
|
from_ffi_delegate(ptr)
|
44
51
|
end
|
45
52
|
|
53
|
+
|
46
54
|
# Initialize a new in-memory certificate with random keys.
|
47
55
|
def initialize
|
48
56
|
attach_ffi_delegate(Zcert.new)
|
49
57
|
end
|
50
58
|
|
59
|
+
|
51
60
|
# Returns the public key either as Z85-encoded ASCII string (default) or
|
52
61
|
# binary string.
|
53
62
|
# @param format [Symbol] +:z85+ for Z85, +:binary+ for binary
|
@@ -59,10 +68,11 @@ module CZTop
|
|
59
68
|
when :binary
|
60
69
|
ffi_delegate.public_key.read_string(32)
|
61
70
|
else
|
62
|
-
raise ArgumentError,
|
71
|
+
raise ArgumentError, format('invalid format: %p', format)
|
63
72
|
end
|
64
73
|
end
|
65
74
|
|
75
|
+
|
66
76
|
# Returns the secret key either as Z85-encoded ASCII string (default) or
|
67
77
|
# binary string.
|
68
78
|
# @param format [Symbol] +:z85+ for Z85, +:binary+ for binary
|
@@ -73,16 +83,17 @@ module CZTop
|
|
73
83
|
case format
|
74
84
|
when :z85
|
75
85
|
key = ffi_delegate.secret_txt.force_encoding(Encoding::ASCII)
|
76
|
-
return nil if key.count(
|
86
|
+
return nil if key.count('0') == 40
|
77
87
|
when :binary
|
78
88
|
key = ffi_delegate.secret_key.read_string(32)
|
79
89
|
return nil if key.count("\0") == 32
|
80
90
|
else
|
81
|
-
raise ArgumentError,
|
91
|
+
raise ArgumentError, format('invalid format: %p', format)
|
82
92
|
end
|
83
93
|
key
|
84
94
|
end
|
85
95
|
|
96
|
+
|
86
97
|
# Get metadata.
|
87
98
|
# @param key [String] metadata key
|
88
99
|
# @return [String] value for meta key
|
@@ -90,32 +101,38 @@ module CZTop
|
|
90
101
|
def [](key)
|
91
102
|
ffi_delegate.meta(key)
|
92
103
|
end
|
104
|
+
|
105
|
+
|
93
106
|
# Set metadata.
|
94
107
|
# @param key [String] metadata key
|
95
108
|
# @param value [String] metadata value
|
96
109
|
# @return [value]
|
97
110
|
def []=(key, value)
|
98
111
|
if value
|
99
|
-
ffi_delegate.set_meta(key,
|
112
|
+
ffi_delegate.set_meta(key, '%s', :string, value)
|
100
113
|
else
|
101
114
|
ffi_delegate.unset_meta(key)
|
102
115
|
end
|
103
116
|
end
|
104
117
|
|
118
|
+
|
105
119
|
# Returns meta keys set.
|
106
120
|
# @return [Array<String>]
|
107
121
|
def meta_keys
|
108
|
-
zlist
|
122
|
+
zlist = ffi_delegate.meta_keys
|
109
123
|
first_key = zlist.first
|
110
124
|
return [] if first_key.null?
|
125
|
+
|
111
126
|
keys = [first_key.read_string]
|
112
|
-
while key = zlist.next
|
127
|
+
while (key = zlist.next)
|
113
128
|
break if key.null?
|
129
|
+
|
114
130
|
keys << key.read_string
|
115
131
|
end
|
116
132
|
keys
|
117
133
|
end
|
118
134
|
|
135
|
+
|
119
136
|
# Save full certificate (public + secret) to files.
|
120
137
|
# @param filename [String, #to_s] path/filename to public file
|
121
138
|
# @return [void]
|
@@ -126,55 +143,67 @@ module CZTop
|
|
126
143
|
def save(filename)
|
127
144
|
# see https://github.com/zeromq/czmq/issues/1244
|
128
145
|
raise ArgumentError, "filename can't be empty" if filename.to_s.empty?
|
146
|
+
|
129
147
|
rc = ffi_delegate.save(filename.to_s)
|
130
|
-
return if rc
|
131
|
-
|
148
|
+
return if rc.zero?
|
149
|
+
|
150
|
+
raise_zmq_err(format('error while saving to file %p', filename))
|
132
151
|
end
|
133
152
|
|
153
|
+
|
134
154
|
# Saves the public key to file in ZPL ({Config}) format.
|
135
155
|
# @param filename [String, #to_s] path/filename to public file
|
136
156
|
# @return [void]
|
137
157
|
# @raise [SystemCallError] if this fails
|
138
158
|
def save_public(filename)
|
139
159
|
rc = ffi_delegate.save_public(filename.to_s)
|
140
|
-
return if rc
|
141
|
-
|
160
|
+
return if rc.zero?
|
161
|
+
|
162
|
+
raise_zmq_err(format('error while saving to the file %p', filename))
|
142
163
|
end
|
143
164
|
|
165
|
+
|
144
166
|
# Saves the secret key to file in ZPL ({Config}) format.
|
145
167
|
# @param filename [String, #to_s] path/filename to secret file
|
146
168
|
# @return [void]
|
147
169
|
# @raise [SystemCallError] if this fails
|
148
170
|
def save_secret(filename)
|
149
171
|
rc = ffi_delegate.save_secret(filename.to_s)
|
150
|
-
return if rc
|
151
|
-
|
172
|
+
return if rc.zero?
|
173
|
+
|
174
|
+
raise_zmq_err(format('error while saving to the file %p', filename))
|
152
175
|
end
|
153
176
|
|
177
|
+
|
154
178
|
# Applies this certificate on a {Socket} or {Actor}.
|
155
179
|
# @param zocket [Socket, Actor] path/filename to secret file
|
156
180
|
# @return [void]
|
157
181
|
# @raise [SystemCallError] if secret key is undefined
|
158
182
|
def apply(zocket)
|
159
|
-
raise ArgumentError,
|
183
|
+
raise ArgumentError, format('invalid zocket argument %p', zocket) unless zocket
|
160
184
|
return ffi_delegate.apply(zocket) unless secret_key.nil?
|
161
|
-
|
185
|
+
|
186
|
+
raise_zmq_err('secret key is undefined')
|
162
187
|
end
|
163
188
|
|
189
|
+
|
164
190
|
# Duplicates the certificate.
|
165
191
|
# @return [Certificate]
|
166
192
|
# @raise [SystemCallError] if this fails
|
167
193
|
def dup
|
168
194
|
ptr = ffi_delegate.dup
|
169
195
|
return from_ffi_delegate(ptr) unless ptr.null?
|
170
|
-
|
196
|
+
|
197
|
+
raise_zmq_err('unable to duplicate certificate')
|
171
198
|
end
|
172
199
|
|
200
|
+
|
173
201
|
# Compares this certificate to another.
|
174
202
|
# @param other [Cert] other certificate
|
175
203
|
# @return [Boolean] whether they have the same keys
|
176
204
|
def ==(other)
|
177
205
|
ffi_delegate.eq(other.ffi_delegate)
|
178
206
|
end
|
207
|
+
|
179
208
|
end
|
180
209
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CZTop
|
2
4
|
class Config
|
3
5
|
|
@@ -7,11 +9,13 @@ module CZTop
|
|
7
9
|
# are accessible.
|
8
10
|
# @return [CommentsAccessor]
|
9
11
|
def comments
|
10
|
-
|
12
|
+
CommentsAccessor.new(self)
|
11
13
|
end
|
12
14
|
|
15
|
+
|
13
16
|
# Used to access a {Config}'s comments.
|
14
17
|
class CommentsAccessor
|
18
|
+
|
15
19
|
include Enumerable
|
16
20
|
|
17
21
|
# @param config [Config]
|
@@ -19,26 +23,30 @@ module CZTop
|
|
19
23
|
@config = config
|
20
24
|
end
|
21
25
|
|
26
|
+
|
22
27
|
# Adds a new comment.
|
23
28
|
# @param new_comment [String]
|
24
29
|
# @return [self]
|
25
30
|
def <<(new_comment)
|
26
|
-
@config.ffi_delegate.set_comment(
|
27
|
-
|
31
|
+
@config.ffi_delegate.set_comment('%s', :string, new_comment)
|
32
|
+
self
|
28
33
|
end
|
29
34
|
|
35
|
+
|
30
36
|
# Deletes all comments for this {Config} item.
|
31
37
|
# @return [void]
|
32
38
|
def delete_all
|
33
39
|
@config.ffi_delegate.set_comment(nil)
|
34
40
|
end
|
35
41
|
|
42
|
+
|
36
43
|
# Yields all comments for this {Config} item.
|
37
44
|
# @yieldparam comment [String]
|
38
45
|
# @return [void]
|
39
46
|
def each
|
40
47
|
while comment = _zlist.next
|
41
48
|
break if comment.null?
|
49
|
+
|
42
50
|
yield comment.read_string
|
43
51
|
end
|
44
52
|
rescue CZMQ::FFI::Zlist::DestroyedError
|
@@ -46,6 +54,7 @@ module CZTop
|
|
46
54
|
nil
|
47
55
|
end
|
48
56
|
|
57
|
+
|
49
58
|
# Returns the number of comments for this {Config} item.
|
50
59
|
# @return [Integer] number of comments
|
51
60
|
def size
|
@@ -61,6 +70,8 @@ module CZTop
|
|
61
70
|
def _zlist
|
62
71
|
@config.ffi_delegate.comments
|
63
72
|
end
|
73
|
+
|
64
74
|
end
|
75
|
+
|
65
76
|
end
|
66
77
|
end
|
@@ -1,19 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Methods used around serialization of {CZTop::Config} items.
|
2
4
|
module CZTop::Config::Serialization
|
5
|
+
|
3
6
|
# Serialize to a string in the ZPL format.
|
4
7
|
# @return [String]
|
5
8
|
def to_s
|
6
9
|
ffi_delegate.str_save.read_string
|
7
10
|
end
|
8
11
|
|
12
|
+
|
9
13
|
# Returns the path/filename of the file this {Config} tree was loaded from.
|
10
14
|
# @return [String]
|
11
15
|
def filename
|
12
16
|
ffi_delegate.filename
|
13
17
|
end
|
14
18
|
|
19
|
+
|
15
20
|
# Some class methods for {Config} related to serialization.
|
16
21
|
module ClassMethods
|
22
|
+
|
17
23
|
# Loads a {Config} tree from a string.
|
18
24
|
# @param string [String] the tree
|
19
25
|
# @return [Config]
|
@@ -21,6 +27,7 @@ module CZTop::Config::Serialization
|
|
21
27
|
from_ffi_delegate CZMQ::FFI::Zconfig.str_load(string)
|
22
28
|
end
|
23
29
|
|
30
|
+
|
24
31
|
# Loads a {Config} tree from a file.
|
25
32
|
# @param path [String, Pathname, #to_s] the path to the ZPL config file
|
26
33
|
# @raise [SystemCallError] if this fails
|
@@ -28,10 +35,13 @@ module CZTop::Config::Serialization
|
|
28
35
|
def load(path)
|
29
36
|
ptr = CZMQ::FFI::Zconfig.load(path.to_s)
|
30
37
|
return from_ffi_delegate(ptr) unless ptr.null?
|
38
|
+
|
31
39
|
CZTop::HasFFIDelegate.raise_zmq_err(
|
32
|
-
|
40
|
+
format('error while reading the file %p', path.to_s)
|
41
|
+
)
|
33
42
|
end
|
34
43
|
|
44
|
+
|
35
45
|
# Loads a {Config} tree from a marshalled string.
|
36
46
|
# @note This method is automatically used by Marshal.load.
|
37
47
|
# @param string [String] marshalled {Config}
|
@@ -39,18 +49,22 @@ module CZTop::Config::Serialization
|
|
39
49
|
def _load(string)
|
40
50
|
from_string(string)
|
41
51
|
end
|
52
|
+
|
42
53
|
end
|
43
54
|
|
55
|
+
|
44
56
|
# Saves the Config tree to a file.
|
45
57
|
# @param path [String, Pathname, #to_s] the path to the ZPL config file
|
46
58
|
# @return [void]
|
47
59
|
# @raise [SystemCallError] if this fails
|
48
60
|
def save(path)
|
49
61
|
rc = ffi_delegate.save(path.to_s)
|
50
|
-
return if rc
|
51
|
-
|
62
|
+
return if rc.zero?
|
63
|
+
|
64
|
+
raise_zmq_err(format('error while saving to the file %s', path))
|
52
65
|
end
|
53
66
|
|
67
|
+
|
54
68
|
# Reload config tree from same file that it was previously loaded from.
|
55
69
|
# @raise [TypeError] if this is an in-memory config
|
56
70
|
# @raise [SystemCallError] if this fails (no existing data will be
|
@@ -62,11 +76,13 @@ module CZTop::Config::Serialization
|
|
62
76
|
# swap out the FFI delegate.
|
63
77
|
filename = filename() or
|
64
78
|
raise TypeError, "can't reload in-memory config"
|
65
|
-
ptr
|
79
|
+
ptr = CZMQ::FFI::Zconfig.load(filename)
|
66
80
|
return attach_ffi_delegate(ptr) unless ptr.null?
|
67
|
-
|
81
|
+
|
82
|
+
raise_zmq_err(format('error while reloading from the file %p', filename))
|
68
83
|
end
|
69
84
|
|
85
|
+
|
70
86
|
# Serialize (marshal) this Config and all its children.
|
71
87
|
#
|
72
88
|
# @note This method is automatically used by Marshal.dump.
|
@@ -74,9 +90,13 @@ module CZTop::Config::Serialization
|
|
74
90
|
def _dump(_level)
|
75
91
|
to_s
|
76
92
|
end
|
93
|
+
|
77
94
|
end
|
78
95
|
|
96
|
+
|
79
97
|
class CZTop::Config
|
98
|
+
|
80
99
|
include Serialization
|
81
100
|
extend Serialization::ClassMethods
|
101
|
+
|
82
102
|
end
|