xchan.rb 0.17.1 → 0.18.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/.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