xchan.rb 0.17.1 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/tests.yml +5 -5
- data/.projectile +2 -1
- data/README.md +20 -24
- data/lib/xchan/bytes.rb +2 -2
- data/lib/xchan/counter.rb +2 -2
- data/lib/xchan/tempfile.rb +23 -17
- data/lib/xchan/unix_socket.rb +35 -36
- data/lib/xchan/version.rb +1 -1
- data/lib/xchan.rb +9 -12
- data/share/xchan.rb/examples/read_operations/1_blocking_read.rb +3 -2
- data/share/xchan.rb/examples/serialization/1_serializers.rb +4 -3
- data/test/readme_test.rb +15 -13
- data/test/setup.rb +1 -0
- data/test/xchan_test.rb +30 -1
- data/xchan.rb.gemspec +2 -2
- metadata +7 -8
- data/.gitlab-ci.yml +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49c3ec314ad19f47d91dd30c854e94ad184fd70a0cb5ed7fd0139b506b295c6e
|
4
|
+
data.tar.gz: 1ba90eb765464330c3c6c35385c6ce6732f0bd95cd21e85be236576b7a0ba7c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d29f66b50713d1277b25ad2900bbcd5a99ac1589a921e0b0a8ccd2028e534204ffc8a382b68f3ba48947aa2be8fda90843adda2fbdf133db3cf9fb230f2a5873
|
7
|
+
data.tar.gz: 218b603663b0cac0af9c6780e942c74decb096045e9a7eef14daf5c6644286bd2ee8e1969445260952425bfad08b4bd2fb588a6a1d1f3be0b45d0913337575b5
|
data/.github/workflows/tests.yml
CHANGED
@@ -12,7 +12,7 @@ jobs:
|
|
12
12
|
fail-fast: false
|
13
13
|
matrix:
|
14
14
|
os: [ubuntu-latest, macos-latest]
|
15
|
-
ruby: [3.
|
15
|
+
ruby: [3.2, 3.3]
|
16
16
|
runs-on: ${{ matrix.os }}
|
17
17
|
steps:
|
18
18
|
- uses: actions/checkout@v2
|
@@ -20,7 +20,7 @@ jobs:
|
|
20
20
|
with:
|
21
21
|
ruby-version: ${{ matrix.ruby }}
|
22
22
|
- run: bundle install
|
23
|
-
- run: SERIALIZER=marshal
|
24
|
-
- run: SERIALIZER=json
|
25
|
-
- run: SERIALIZER=yaml
|
26
|
-
- run: SERIALIZER=pure
|
23
|
+
- run: SERIALIZER=marshal; for t in *_test.rb; do ruby test/${t}; done
|
24
|
+
- run: SERIALIZER=json; for t in *_test.rb; do ruby test/${t}; done
|
25
|
+
- run: SERIALIZER=yaml; for t in *_test.rb; do ruby test/${t}; done
|
26
|
+
- run: SERIALIZER=pure; for t in *_test.rb; do ruby test/${t}; done
|
data/.projectile
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
xchan.rb is an easy to use library for InterProcess
|
4
4
|
Communication (IPC). The library provides a channel
|
5
|
-
that can
|
6
|
-
|
5
|
+
that can help facilitate communication between Ruby
|
6
|
+
processes who have a parent <=> child relationship.
|
7
7
|
|
8
8
|
## Examples
|
9
9
|
|
@@ -11,30 +11,25 @@ with a parent <-> child relationship.
|
|
11
11
|
|
12
12
|
#### Options
|
13
13
|
|
14
|
-
The first argument
|
15
|
-
that
|
16
|
-
in
|
17
|
-
available as `xchan(:pure)
|
18
|
-
|
19
|
-
|
20
|
-
a Ruby object is serialized (on write) or deserialized
|
21
|
-
(on read). The serializers available to choose from
|
22
|
-
are `xchan(:marshal)`, `xchan(:json)`, and `xchan(:yaml)`.
|
23
|
-
The example uses
|
24
|
-
[`Marshal`](https://www.rubydoc.info/stdlib/core/Marshal):
|
14
|
+
The first argument provided to xchan is the serializer
|
15
|
+
that should be used. A channel that will communicate
|
16
|
+
purely in strings (in other words: without serialization)
|
17
|
+
is available as `xchan(:pure)` - otherwise a wide range of
|
18
|
+
serializers are available by default: `xchan(:marshal)`,
|
19
|
+
`xchan(:json)`, and `xchan(:yaml)`.
|
25
20
|
|
26
21
|
```ruby
|
27
22
|
require "xchan"
|
28
23
|
|
29
24
|
##
|
30
|
-
#
|
25
|
+
# Marshal as the serializer
|
31
26
|
ch = xchan(:marshal)
|
32
27
|
Process.wait fork { ch.send(5) }
|
33
|
-
print "
|
28
|
+
print "#{ch.recv} + 7 = 12", "\n"
|
34
29
|
ch.close
|
35
30
|
|
36
31
|
##
|
37
|
-
#
|
32
|
+
# 5 + 7 = 12
|
38
33
|
```
|
39
34
|
|
40
35
|
### Read operations
|
@@ -52,13 +47,14 @@ the parent process writes to the channel:
|
|
52
47
|
require "xchan"
|
53
48
|
|
54
49
|
ch = xchan(:marshal)
|
55
|
-
|
50
|
+
fork do
|
56
51
|
print "Received a random number (child process): ", ch.recv, "\n"
|
57
|
-
|
52
|
+
end
|
58
53
|
sleep(1)
|
59
54
|
print "Send a random number (from parent process)", "\n"
|
60
55
|
ch.send(rand(21))
|
61
56
|
ch.close
|
57
|
+
Process.wait
|
62
58
|
|
63
59
|
##
|
64
60
|
# Send a random number (from parent process)
|
@@ -183,7 +179,7 @@ print "The maximum size of a single message is: ", sndbuf.int, " bytes.\n"
|
|
183
179
|
## Documentation
|
184
180
|
|
185
181
|
A complete API reference is available at
|
186
|
-
[0x1eef.github.io/x/xchan.rb](https://0x1eef.github.io/x/xchan.rb/)
|
182
|
+
[0x1eef.github.io/x/xchan.rb](https://0x1eef.github.io/x/xchan.rb/)
|
187
183
|
|
188
184
|
## Install
|
189
185
|
|
@@ -193,11 +189,11 @@ xchan.rb can be installed via rubygems.org:
|
|
193
189
|
|
194
190
|
## Sources
|
195
191
|
|
196
|
-
* [
|
197
|
-
* [
|
192
|
+
* [GitHub](https://github.com/0x1eef/xchan.rb#readme)
|
193
|
+
* [GitLab](https://gitlab.com/0x1eef/xchan.rb#about)
|
198
194
|
|
199
|
-
##
|
195
|
+
## License
|
200
196
|
|
201
|
-
[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/)
|
197
|
+
[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/)
|
202
198
|
<br>
|
203
|
-
See [LICENSE](./LICENSE)
|
199
|
+
See [LICENSE](./LICENSE)
|
data/lib/xchan/bytes.rb
CHANGED
@@ -16,7 +16,7 @@ class Chan::Bytes
|
|
16
16
|
#
|
17
17
|
# @return [Chan::Bytes]
|
18
18
|
def initialize(tmpdir)
|
19
|
-
@io = Chan.temporary_file(
|
19
|
+
@io = Chan.temporary_file(%w[bytes .json], tmpdir:)
|
20
20
|
@io.sync = true
|
21
21
|
write(@io, [])
|
22
22
|
end
|
@@ -95,6 +95,6 @@ class Chan::Bytes
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def deserialize(bytes)
|
98
|
-
JSON.
|
98
|
+
JSON.parse(bytes)
|
99
99
|
end
|
100
100
|
end
|
data/lib/xchan/counter.rb
CHANGED
@@ -13,7 +13,7 @@ class Chan::Counter
|
|
13
13
|
#
|
14
14
|
# @return [Chan::Counter]
|
15
15
|
def initialize(tmpdir)
|
16
|
-
@io = Chan.temporary_file(
|
16
|
+
@io = Chan.temporary_file(%w[counter .json], tmpdir:)
|
17
17
|
write(@io, {"bytes_read" => 0, "bytes_written" => 0})
|
18
18
|
end
|
19
19
|
|
@@ -57,6 +57,6 @@ class Chan::Counter
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def deserialize(bytes)
|
60
|
-
JSON.
|
60
|
+
JSON.parse(bytes)
|
61
61
|
end
|
62
62
|
end
|
data/lib/xchan/tempfile.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
require "tmpdir"
|
3
5
|
|
4
6
|
##
|
5
7
|
# @private
|
@@ -63,12 +65,12 @@ class Chan::Tempfile < DelegateClass(File)
|
|
63
65
|
#
|
64
66
|
# Related: Tempfile.create.
|
65
67
|
#
|
66
|
-
def initialize(basename="", tmpdir=nil, mode: 0, perm:
|
68
|
+
def initialize(basename = "", tmpdir = nil, mode: 0, perm: 0o600, **kwargs)
|
67
69
|
warn "Tempfile.new doesn't call the given block.", uplevel: 1 if block_given?
|
68
70
|
|
69
71
|
@unlinked = false
|
70
|
-
@mode = mode|File::RDWR|File::CREAT|File::EXCL
|
71
|
-
::Dir::Tmpname.create(basename, tmpdir, **
|
72
|
+
@mode = mode | File::RDWR | File::CREAT | File::EXCL
|
73
|
+
::Dir::Tmpname.create(basename, tmpdir, **kwargs) do |tmpname, n, opts|
|
72
74
|
@tmpfile = File.open(tmpname, @mode, perm, **opts)
|
73
75
|
@perm = perm
|
74
76
|
@opts = opts.freeze
|
@@ -81,7 +83,7 @@ class Chan::Tempfile < DelegateClass(File)
|
|
81
83
|
# Opens or reopens the file with mode "r+".
|
82
84
|
def open
|
83
85
|
_close
|
84
|
-
mode = @mode & ~(File::CREAT|File::EXCL)
|
86
|
+
mode = @mode & ~(File::CREAT | File::EXCL)
|
85
87
|
@tmpfile = File.open(@tmpfile.path, mode, **@opts)
|
86
88
|
__setobj__(@tmpfile)
|
87
89
|
end
|
@@ -97,7 +99,7 @@ class Chan::Tempfile < DelegateClass(File)
|
|
97
99
|
#
|
98
100
|
# If you don't explicitly unlink the temporary file, the removal
|
99
101
|
# will be delayed until the object is finalized.
|
100
|
-
def close(unlink_now=false)
|
102
|
+
def close(unlink_now = false)
|
101
103
|
_close
|
102
104
|
unlink if unlink_now
|
103
105
|
end
|
@@ -153,7 +155,7 @@ class Chan::Tempfile < DelegateClass(File)
|
|
153
155
|
ObjectSpace.undefine_finalizer(self)
|
154
156
|
@unlinked = true
|
155
157
|
end
|
156
|
-
|
158
|
+
alias_method :delete, :unlink
|
157
159
|
|
158
160
|
# Returns the full path name of the temporary file.
|
159
161
|
# This will be nil if #unlink has been called.
|
@@ -170,7 +172,7 @@ class Chan::Tempfile < DelegateClass(File)
|
|
170
172
|
File.size(@tmpfile.path)
|
171
173
|
end
|
172
174
|
end
|
173
|
-
|
175
|
+
alias_method :length, :size
|
174
176
|
|
175
177
|
# :stopdoc:
|
176
178
|
def inspect
|
@@ -190,7 +192,7 @@ class Chan::Tempfile < DelegateClass(File)
|
|
190
192
|
def call(*args)
|
191
193
|
return if @pid != Process.pid
|
192
194
|
|
193
|
-
|
195
|
+
warn "removing #{@tmpfile.path}..." if $DEBUG
|
194
196
|
|
195
197
|
@tmpfile.close
|
196
198
|
begin
|
@@ -198,7 +200,7 @@ class Chan::Tempfile < DelegateClass(File)
|
|
198
200
|
rescue Errno::ENOENT
|
199
201
|
end
|
200
202
|
|
201
|
-
|
203
|
+
warn "done" if $DEBUG
|
202
204
|
end
|
203
205
|
end
|
204
206
|
|
@@ -240,8 +242,8 @@ class Chan::Tempfile < DelegateClass(File)
|
|
240
242
|
# ensure
|
241
243
|
# f.close
|
242
244
|
# end
|
243
|
-
def self.open(*args, **
|
244
|
-
tempfile = new(*args, **
|
245
|
+
def self.open(*args, **kwargs)
|
246
|
+
tempfile = new(*args, **kwargs)
|
245
247
|
|
246
248
|
if block_given?
|
247
249
|
begin
|
@@ -313,10 +315,10 @@ module Chan
|
|
313
315
|
#
|
314
316
|
# Related: Tempfile.new.
|
315
317
|
#
|
316
|
-
def Tempfile.create(basename="", tmpdir=nil, mode: 0, perm:
|
318
|
+
def Tempfile.create(basename = "", tmpdir = nil, mode: 0, perm: 0o600, **kwargs)
|
317
319
|
tmpfile = nil
|
318
|
-
Dir::Tmpname.create(basename, tmpdir, **
|
319
|
-
mode |= File::RDWR|File::CREAT|File::EXCL
|
320
|
+
Dir::Tmpname.create(basename, tmpdir, **kwargs) do |tmpname, n, opts|
|
321
|
+
mode |= File::RDWR | File::CREAT | File::EXCL
|
320
322
|
tmpfile = File.open(tmpname, mode, perm, **opts)
|
321
323
|
end
|
322
324
|
if block_given?
|
@@ -325,7 +327,11 @@ module Chan
|
|
325
327
|
ensure
|
326
328
|
unless tmpfile.closed?
|
327
329
|
if File.identical?(tmpfile, tmpfile.path)
|
328
|
-
unlinked =
|
330
|
+
unlinked = begin
|
331
|
+
File.unlink tmpfile.path
|
332
|
+
rescue
|
333
|
+
nil
|
334
|
+
end
|
329
335
|
end
|
330
336
|
tmpfile.close
|
331
337
|
end
|
data/lib/xchan/unix_socket.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
##
|
4
|
-
# An easy-to-use InterProcess Communication (IPC) library
|
4
|
+
# An easy-to-use InterProcess Communication (IPC) library
|
5
5
|
class Chan::UNIXSocket
|
6
6
|
require "socket"
|
7
7
|
require "lockf"
|
@@ -17,6 +17,12 @@ class Chan::UNIXSocket
|
|
17
17
|
# Returns a socket used for write operations
|
18
18
|
attr_reader :w
|
19
19
|
|
20
|
+
##
|
21
|
+
# @return [<#dump, #load>]
|
22
|
+
# Returns the serializer used by the channel
|
23
|
+
attr_reader :s
|
24
|
+
alias_method :serializer, :s
|
25
|
+
|
20
26
|
##
|
21
27
|
# @example
|
22
28
|
# ch = Chan::UNIXSocket.new(:marshal)
|
@@ -24,7 +30,7 @@ class Chan::UNIXSocket
|
|
24
30
|
# ch.recv.pop # => 3
|
25
31
|
# ch.close
|
26
32
|
#
|
27
|
-
# @param [Symbol, <#dump, #load>]
|
33
|
+
# @param [Symbol, <#dump, #load>] s
|
28
34
|
# The name of a serializer
|
29
35
|
#
|
30
36
|
# @param [Integer] sock_type
|
@@ -35,19 +41,12 @@ class Chan::UNIXSocket
|
|
35
41
|
#
|
36
42
|
# @return [Chan::UNIXSocket]
|
37
43
|
# Returns an instance of {Chan::UNIXSocket Chan::UNIXSocket}
|
38
|
-
def initialize(
|
39
|
-
@
|
44
|
+
def initialize(s, sock_type: Socket::SOCK_DGRAM, tmpdir: Dir.tmpdir)
|
45
|
+
@s = Chan.serializers[s]&.call || s
|
40
46
|
@r, @w = ::UNIXSocket.pair(sock_type)
|
41
47
|
@bytes = Chan::Bytes.new(tmpdir)
|
42
48
|
@counter = Chan::Counter.new(tmpdir)
|
43
|
-
@
|
44
|
-
end
|
45
|
-
|
46
|
-
##
|
47
|
-
# @return [<#dump, #load>]
|
48
|
-
# Returns the serializer used by the channel
|
49
|
-
def serializer
|
50
|
-
@serializer
|
49
|
+
@lockf = Lock::File.new Chan.temporary_file(%w[xchan .lock], tmpdir:)
|
51
50
|
end
|
52
51
|
|
53
52
|
##
|
@@ -61,15 +60,15 @@ class Chan::UNIXSocket
|
|
61
60
|
# Closes the channel
|
62
61
|
#
|
63
62
|
# @raise [IOError]
|
64
|
-
# When the channel is closed
|
63
|
+
# When the channel is closed
|
65
64
|
#
|
66
65
|
# @return [void]
|
67
66
|
def close
|
68
|
-
@
|
67
|
+
@lockf.lock
|
69
68
|
raise IOError, "channel is closed" if closed?
|
70
|
-
[@r, @w, @bytes, @
|
69
|
+
[@r, @w, @bytes, @lockf].each(&:close)
|
71
70
|
rescue IOError => ex
|
72
|
-
@
|
71
|
+
@lockf.release
|
73
72
|
raise(ex)
|
74
73
|
end
|
75
74
|
|
@@ -112,14 +111,14 @@ class Chan::UNIXSocket
|
|
112
111
|
# @return [Integer, nil]
|
113
112
|
# Returns the number of bytes written to the channel
|
114
113
|
def send_nonblock(object)
|
115
|
-
@
|
114
|
+
@lockf.lock_nonblock
|
116
115
|
raise IOError, "channel closed" if closed?
|
117
116
|
len = @w.write_nonblock(serialize(object))
|
118
117
|
@bytes.push(len)
|
119
118
|
@counter.increment!(bytes_written: len)
|
120
|
-
len.tap { @
|
119
|
+
len.tap { @lockf.release }
|
121
120
|
rescue IOError, IO::WaitWritable, Errno::ENOBUFS => ex
|
122
|
-
@
|
121
|
+
@lockf.release
|
123
122
|
raise Chan::WaitWritable, ex.message
|
124
123
|
rescue Errno::EWOULDBLOCK => ex
|
125
124
|
raise Chan::WaitLockable, ex.message
|
@@ -165,18 +164,18 @@ class Chan::UNIXSocket
|
|
165
164
|
# @return [Object]
|
166
165
|
# Returns an object from the channel
|
167
166
|
def recv_nonblock
|
168
|
-
@
|
167
|
+
@lockf.lock_nonblock
|
169
168
|
raise IOError, "closed channel" if closed?
|
170
169
|
len = @bytes.shift
|
171
170
|
obj = deserialize(@r.read_nonblock(len.zero? ? 1 : len))
|
172
171
|
@counter.increment!(bytes_read: len)
|
173
|
-
obj.tap { @
|
172
|
+
obj.tap { @lockf.release }
|
174
173
|
rescue IOError => ex
|
175
|
-
@
|
174
|
+
@lockf.release
|
176
175
|
raise(ex)
|
177
176
|
rescue IO::WaitReadable => ex
|
178
177
|
@bytes.unshift(len)
|
179
|
-
@
|
178
|
+
@lockf.release
|
180
179
|
raise Chan::WaitReadable, ex.message
|
181
180
|
rescue Errno::EAGAIN => ex
|
182
181
|
raise Chan::WaitLockable, ex.message
|
@@ -188,9 +187,9 @@ class Chan::UNIXSocket
|
|
188
187
|
|
189
188
|
##
|
190
189
|
# @example
|
191
|
-
# ch = xchan
|
190
|
+
# ch = xchan(:pure)
|
192
191
|
# 1.upto(4) { ch.send(_1) }
|
193
|
-
# ch.to_a.last # => 4
|
192
|
+
# ch.to_a.last # => "4"
|
194
193
|
#
|
195
194
|
# @return [Array<Object>]
|
196
195
|
# Returns the contents of the channel
|
@@ -229,7 +228,7 @@ class Chan::UNIXSocket
|
|
229
228
|
|
230
229
|
##
|
231
230
|
# @return [Integer]
|
232
|
-
# Returns the number of objects waiting to be read
|
231
|
+
# Returns the number of objects waiting to be read
|
233
232
|
def size
|
234
233
|
lock { @bytes.size }
|
235
234
|
end
|
@@ -241,25 +240,25 @@ class Chan::UNIXSocket
|
|
241
240
|
# @group Wait methods
|
242
241
|
|
243
242
|
##
|
244
|
-
# Waits for the channel to become readable
|
243
|
+
# Waits for the channel to become readable
|
245
244
|
#
|
246
245
|
# @param [Float, Integer, nil] s
|
247
|
-
# The number of seconds to wait. Waits indefinitely
|
246
|
+
# The number of seconds to wait. Waits indefinitely with no arguments.
|
248
247
|
#
|
249
248
|
# @return [Chan::UNIXSocket, nil]
|
250
|
-
# Returns self when the channel is readable, otherwise returns nil
|
249
|
+
# Returns self when the channel is readable, otherwise returns nil
|
251
250
|
def wait_readable(s = nil)
|
252
251
|
@r.wait_readable(s) and self
|
253
252
|
end
|
254
253
|
|
255
254
|
##
|
256
|
-
# Waits for the channel to become writable
|
255
|
+
# Waits for the channel to become writable
|
257
256
|
#
|
258
257
|
# @param [Float, Integer, nil] s
|
259
|
-
# The number of seconds to wait. Waits indefinitely
|
258
|
+
# The number of seconds to wait. Waits indefinitely with no arguments.
|
260
259
|
#
|
261
260
|
# @return [Chan::UNIXSocket, nil]
|
262
|
-
# Returns self when the channel is writable, otherwise returns nil
|
261
|
+
# Returns self when the channel is writable, otherwise returns nil
|
263
262
|
def wait_writable(s = nil)
|
264
263
|
@w.wait_writable(s) and self
|
265
264
|
end
|
@@ -270,17 +269,17 @@ class Chan::UNIXSocket
|
|
270
269
|
private
|
271
270
|
|
272
271
|
def lock
|
273
|
-
@
|
272
|
+
@lockf.lock
|
274
273
|
yield
|
275
274
|
ensure
|
276
|
-
@
|
275
|
+
@lockf.release
|
277
276
|
end
|
278
277
|
|
279
278
|
def serialize(obj)
|
280
|
-
@
|
279
|
+
@s.dump(obj)
|
281
280
|
end
|
282
281
|
|
283
282
|
def deserialize(str)
|
284
|
-
@
|
283
|
+
@s.load(str)
|
285
284
|
end
|
286
285
|
end
|
data/lib/xchan/version.rb
CHANGED
data/lib/xchan.rb
CHANGED
@@ -10,17 +10,14 @@ module Chan
|
|
10
10
|
WaitLockable = Class.new(Errno::EWOULDBLOCK)
|
11
11
|
|
12
12
|
##
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# can be useful when you want to communicate
|
17
|
-
# purely in strings.
|
13
|
+
# Coerces an object to a string for a
|
14
|
+
# channel communicating in raw strings
|
15
|
+
# (in other words: without serialization)
|
18
16
|
#
|
19
17
|
# @example
|
20
18
|
# ch = xchan(:pure)
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# }
|
19
|
+
# fork { ch.send "Hello world" }
|
20
|
+
# Process.wait
|
24
21
|
# puts ch.recv
|
25
22
|
Pure = Class.new do
|
26
23
|
def self.dump(str) = str.to_s
|
@@ -47,7 +44,7 @@ module Chan
|
|
47
44
|
|
48
45
|
##
|
49
46
|
# @return [Hash<Symbol, Proc>]
|
50
|
-
#
|
47
|
+
# Returns the default serializers
|
51
48
|
def self.serializers
|
52
49
|
{
|
53
50
|
pure: lambda { Pure },
|
@@ -72,11 +69,11 @@ module Kernel
|
|
72
69
|
# ch.recv.pop # => 3
|
73
70
|
# ch.close
|
74
71
|
#
|
75
|
-
# @param
|
72
|
+
# @param s (see Chan::UNIXSocket#initialize)
|
76
73
|
# @param sock_type (see Chan::UNIXSocket#initialize)
|
77
74
|
# @param tmpdir (see Chan::UNIXSocket#initialize)
|
78
75
|
# @return (see Chan::UNIXSocket#initialize)
|
79
|
-
def xchan(
|
80
|
-
Chan::UNIXSocket.new(
|
76
|
+
def xchan(s, ...)
|
77
|
+
Chan::UNIXSocket.new(s, ...)
|
81
78
|
end
|
82
79
|
end
|
@@ -5,13 +5,14 @@ require "xchan"
|
|
5
5
|
|
6
6
|
$stdout.sync = true
|
7
7
|
ch = xchan(:marshal)
|
8
|
-
|
8
|
+
fork do
|
9
9
|
print "Received random number (child process): ", ch.recv, "\n"
|
10
|
-
|
10
|
+
end
|
11
11
|
sleep(1)
|
12
12
|
print "Send a random number (from parent process)", "\n"
|
13
13
|
ch.send(rand(21))
|
14
14
|
ch.close
|
15
|
+
Process.wait
|
15
16
|
|
16
17
|
##
|
17
18
|
# Send a random number (from parent process)
|
@@ -2,13 +2,14 @@
|
|
2
2
|
|
3
3
|
require_relative "../setup"
|
4
4
|
require "xchan"
|
5
|
+
require "xchan"
|
5
6
|
|
6
7
|
##
|
7
|
-
#
|
8
|
+
# Marshal as the serializer
|
8
9
|
ch = xchan(:marshal)
|
9
10
|
Process.wait fork { ch.send(5) }
|
10
|
-
print "
|
11
|
+
print "#{ch.recv} + 7 = 12", "\n"
|
11
12
|
ch.close
|
12
13
|
|
13
14
|
##
|
14
|
-
#
|
15
|
+
# 5 + 7 = 12
|
data/test/readme_test.rb
CHANGED
@@ -1,42 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "setup"
|
4
|
-
require "test
|
4
|
+
require "test-cmd"
|
5
5
|
|
6
6
|
class Chan::ReadmeTest < Test::Unit::TestCase
|
7
7
|
def test_serialization_1_serializers
|
8
|
-
assert_equal "
|
9
|
-
readme_example("serialization/1_serializers.rb").stdout
|
8
|
+
assert_equal "5 + 7 = 12\n",
|
9
|
+
cmd("ruby", readme_example("serialization/1_serializers.rb")).stdout
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_read_operations_1_blocking_read
|
13
13
|
r = 'Send a random number \(from parent process\)\s*' \
|
14
14
|
'Received random number \(child process\): \d+'
|
15
15
|
assert_match Regexp.new(r),
|
16
|
-
readme_example("read_operations/1_blocking_read.rb")
|
17
|
-
|
16
|
+
cmd("ruby", readme_example("read_operations/1_blocking_read.rb"))
|
17
|
+
.stdout
|
18
|
+
.tr("\n", " ")
|
18
19
|
end
|
19
20
|
|
20
21
|
def test_write_operations_2_non_blocking_write
|
21
22
|
assert_equal ["Blocked - free send buffer\n"],
|
22
|
-
readme_example("write_operations/2_nonblocking_write.rb")
|
23
|
-
|
24
|
-
|
23
|
+
cmd("ruby", readme_example("write_operations/2_nonblocking_write.rb"))
|
24
|
+
.stdout
|
25
|
+
.each_line
|
26
|
+
.uniq
|
25
27
|
end
|
26
28
|
|
27
29
|
def test_socket_2_options
|
28
30
|
r = 'The read buffer can contain a maximum of: \d{1,6} bytes.\s*' \
|
29
31
|
'The maximum size of a single message is: \d{1,6} bytes.\s*'
|
30
32
|
assert_match Regexp.new(r),
|
31
|
-
readme_example("socket/2_options.rb")
|
32
|
-
|
33
|
+
cmd("ruby", readme_example("socket/2_options.rb"))
|
34
|
+
.stdout
|
35
|
+
.tr("\n", " ")
|
33
36
|
end
|
34
37
|
|
35
38
|
private
|
36
39
|
|
37
40
|
def readme_example(path)
|
38
|
-
|
39
|
-
|
40
|
-
cmd "bundle exec ruby #{example}"
|
41
|
+
dir = File.join(Dir.getwd, "share", "xchan.rb", "examples")
|
42
|
+
File.join(dir, path)
|
41
43
|
end
|
42
44
|
end
|
data/test/setup.rb
CHANGED
data/test/xchan_test.rb
CHANGED
@@ -84,7 +84,7 @@ class Chan::RecvNonBlockTest < Chan::Test
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def test_recv_nonblock_with_a_lock
|
87
|
-
ch.instance_variable_get(:@
|
87
|
+
ch.instance_variable_get(:@lockf).lock
|
88
88
|
pid = fork do
|
89
89
|
ch.recv_nonblock
|
90
90
|
exit(1)
|
@@ -194,3 +194,32 @@ class Chan::BytesReadTest < Chan::Test
|
|
194
194
|
assert_equal object_size * 2, ch.bytes_read
|
195
195
|
end
|
196
196
|
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# Chan.temporary_file
|
200
|
+
class Chan::TemporaryFileTest < Chan::Test
|
201
|
+
def test_temporary_file_mode
|
202
|
+
assert_equal 0, file.stat.mode & 0o777
|
203
|
+
ensure
|
204
|
+
file.close
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_temporary_file_path
|
208
|
+
assert_match %r{#{Regexp.escape(Dir.tmpdir)}/foobar[a-zA-Z0-9-]+\.txt},
|
209
|
+
file.to_path
|
210
|
+
ensure
|
211
|
+
file.close
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_temporary_file_unlinked
|
215
|
+
refute File.exist?(file.to_path)
|
216
|
+
ensure
|
217
|
+
file.close
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
def file
|
223
|
+
@file ||= Chan.temporary_file %w[foobar .txt]
|
224
|
+
end
|
225
|
+
end
|
data/xchan.rb.gemspec
CHANGED
@@ -12,11 +12,11 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.require_paths = ["lib"]
|
13
13
|
gem.summary = "An easy to use InterProcess Communication (IPC) library"
|
14
14
|
gem.description = gem.summary
|
15
|
-
gem.add_runtime_dependency "lockf.rb", "~> 1
|
15
|
+
gem.add_runtime_dependency "lockf.rb", "~> 2.1"
|
16
16
|
gem.add_development_dependency "test-unit", "~> 3.5.7"
|
17
17
|
gem.add_development_dependency "yard", "~> 0.9"
|
18
18
|
gem.add_development_dependency "redcarpet", "~> 3.5"
|
19
19
|
gem.add_development_dependency "standard", "~> 1.13"
|
20
|
-
gem.add_development_dependency "test-cmd.rb", "~> 0.
|
20
|
+
gem.add_development_dependency "test-cmd.rb", "~> 0.12.4"
|
21
21
|
gem.add_development_dependency "rake", "~> 13.1"
|
22
22
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xchan.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- '0x1eef'
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lockf.rb
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1
|
19
|
+
version: '2.1'
|
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
|
26
|
+
version: '2.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: test-unit
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 0.12.4
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 0.12.4
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: rake
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,7 +117,6 @@ extra_rdoc_files: []
|
|
117
117
|
files:
|
118
118
|
- ".github/workflows/tests.yml"
|
119
119
|
- ".gitignore"
|
120
|
-
- ".gitlab-ci.yml"
|
121
120
|
- ".projectile"
|
122
121
|
- ".rubocop.yml"
|
123
122
|
- ".yardopts"
|
@@ -162,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
161
|
- !ruby/object:Gem::Version
|
163
162
|
version: '0'
|
164
163
|
requirements: []
|
165
|
-
rubygems_version: 3.5.
|
164
|
+
rubygems_version: 3.5.11
|
166
165
|
signing_key:
|
167
166
|
specification_version: 4
|
168
167
|
summary: An easy to use InterProcess Communication (IPC) library
|
data/.gitlab-ci.yml
DELETED