xchan.rb 0.19.0 → 0.20.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/README.md +39 -15
- data/lib/xchan/bytes.rb +7 -5
- data/lib/xchan/counter.rb +32 -17
- data/lib/xchan/null_lock.rb +4 -4
- data/lib/xchan/unix_socket.rb +26 -8
- data/lib/xchan/version.rb +1 -1
- data/lib/xchan.rb +1 -1
- data/test/xchan_test.rb +39 -0
- data/xchan.rb.gemspec +1 -1
- metadata +5 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3faafc7100339e243744b476b137e7ee96591179027c862509006c60df6c7fe1
|
|
4
|
+
data.tar.gz: b7abc06e3a789f1f80a171e520c2cd3a64126c11e8bc01795bf90c4fcb5601bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1a14ea31ccd8ae96a56d4dfc667fe0141906a8d0123f3ba184e524ad07b4586b205501c870aec02f0af33d47d6ac8b788f023174afcddf8d6d1b90e853a3b40b
|
|
7
|
+
data.tar.gz: 98c9e23a2abd39852fdd315011c44385b6c57a1a9a000e0d9430580a36a3970091857482d7866c58c765015fce7cbb6523a9b3e411daa2c5520b79f911efaf05
|
data/README.md
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
|
+
> **Designed for minimalism** <br>
|
|
2
|
+
> One direct runtime dependency ([lockf.rb](https://github.com/0x1eef/lockf.rb#readme)) <br>
|
|
3
|
+
> Zero indirect dependencies outside Ruby's standard library
|
|
4
|
+
|
|
1
5
|
## About
|
|
2
6
|
|
|
3
|
-
xchan.rb is an easy to use library for
|
|
4
|
-
Communication (IPC). The library provides a channel
|
|
7
|
+
xchan.rb is an easy to use, minimalist library for
|
|
8
|
+
InterProcess Communication (IPC). The library provides a channel
|
|
5
9
|
that can help facilitate communication between Ruby
|
|
6
10
|
processes who have a parent <=> child relationship.
|
|
11
|
+
A channel lock is provided by
|
|
12
|
+
[lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3) and a temporary, unlinked file to protect against race conditions
|
|
13
|
+
that can happen when multiple processes access the same channel
|
|
14
|
+
at the same time.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
* Minimalist Inter-Process Communication (IPC) for parent <=> child processes.
|
|
19
|
+
* Channel-based communication.
|
|
20
|
+
* Support for multiple serializers (`:marshal`, `:json`, `:yaml`) and raw string communication (`:pure`).
|
|
21
|
+
* Blocking (`#send`, `#recv`) and non-blocking (`#send_nonblock`, `#recv_nonblock`) operations.
|
|
22
|
+
* Built-in file-based locking ([lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3)) to prevent race conditions.
|
|
23
|
+
* Option to use a null lock for scenarios where locking is not needed.
|
|
24
|
+
* Access to underlying UNIX sockets for fine-grained control over socket options.
|
|
25
|
+
* Mac, BSD, and Linux support.
|
|
26
|
+
* Good docs.
|
|
7
27
|
|
|
8
28
|
## Examples
|
|
9
29
|
|
|
@@ -26,7 +46,7 @@ require "xchan"
|
|
|
26
46
|
# Marshal as the serializer
|
|
27
47
|
ch = xchan(:marshal)
|
|
28
48
|
Process.wait fork { ch.send(5) }
|
|
29
|
-
|
|
49
|
+
puts "#{ch.recv} + 7 = 12"
|
|
30
50
|
ch.close
|
|
31
51
|
|
|
32
52
|
##
|
|
@@ -40,7 +60,7 @@ ch.close
|
|
|
40
60
|
The `ch.recv` method performs a blocking read. A read
|
|
41
61
|
can block when a lock is held by another process, or
|
|
42
62
|
when a read from
|
|
43
|
-
[Chan::UNIXSocket#r](https://0x1eef.github
|
|
63
|
+
[Chan::UNIXSocket#r](https://0x1eef.github.io/x/xchan.rb/Chan/UNIXSocket.html#r-instance_method)
|
|
44
64
|
blocks. The example performs a read that blocks until
|
|
45
65
|
the parent process writes to the channel:
|
|
46
66
|
|
|
@@ -53,7 +73,7 @@ fork do
|
|
|
53
73
|
print "Received a random number (child process): ", ch.recv, "\n"
|
|
54
74
|
end
|
|
55
75
|
sleep(1)
|
|
56
|
-
|
|
76
|
+
puts "Send a random number (from parent process)"
|
|
57
77
|
ch.send(rand(21))
|
|
58
78
|
ch.close
|
|
59
79
|
Process.wait
|
|
@@ -69,7 +89,7 @@ The non-blocking counterpart to `#recv` is `#recv_nonblock`.
|
|
|
69
89
|
The `#recv_nonblock` method raises `Chan::WaitLockable` when
|
|
70
90
|
a read blocks because of a lock held by another process, and
|
|
71
91
|
the method raises `Chan::WaitReadable` when a read from
|
|
72
|
-
[Chan::UNIXSocket#r](https://0x1eef.github
|
|
92
|
+
[Chan::UNIXSocket#r](https://0x1eef.github.io/x/xchan.rb/Chan/UNIXSocket.html#r-instance_method)
|
|
73
93
|
blocks:
|
|
74
94
|
|
|
75
95
|
```ruby
|
|
@@ -79,11 +99,12 @@ require "xchan"
|
|
|
79
99
|
def read(ch)
|
|
80
100
|
ch.recv_nonblock
|
|
81
101
|
rescue Chan::WaitReadable
|
|
82
|
-
|
|
102
|
+
puts "Wait 1 second for channel to be readable"
|
|
83
103
|
ch.wait_readable(1)
|
|
84
104
|
retry
|
|
85
105
|
rescue Chan::WaitLockable
|
|
86
|
-
|
|
106
|
+
puts "Wait 1 second for channel to be lockable"
|
|
107
|
+
ch.wait_lockable(1)
|
|
87
108
|
retry
|
|
88
109
|
end
|
|
89
110
|
trap("SIGINT") { exit(1) }
|
|
@@ -102,7 +123,7 @@ read(xchan(:marshal))
|
|
|
102
123
|
The `ch.send` method performs a blocking write.
|
|
103
124
|
A write can block when a lock is held by another
|
|
104
125
|
process, or when a write to
|
|
105
|
-
[Chan::UNIXSocket#w](https://0x1eef.github
|
|
126
|
+
[Chan::UNIXSocket#w](https://0x1eef.github.io/x/xchan.rb/Chan/UNIXSocket.html#w-instance_method)
|
|
106
127
|
blocks. The example fills the send buffer:
|
|
107
128
|
|
|
108
129
|
```ruby
|
|
@@ -123,7 +144,7 @@ The non-blocking counterpart to `#send` is
|
|
|
123
144
|
`Chan::WaitLockable` when a write blocks because of
|
|
124
145
|
a lock held by another process, and the method raises
|
|
125
146
|
`Chan::WaitWritable` when a write to
|
|
126
|
-
[Chan::UNIXSocket#w](https://0x1eef.github
|
|
147
|
+
[Chan::UNIXSocket#w](https://0x1eef.github.io/x/xchan.rb/Chan/UNIXSocket.html#w-instance_method)
|
|
127
148
|
blocks. The example frees space on the send buffer:
|
|
128
149
|
|
|
129
150
|
```ruby
|
|
@@ -133,11 +154,11 @@ require "xchan"
|
|
|
133
154
|
def send_nonblock(ch, buf)
|
|
134
155
|
ch.send_nonblock(buf)
|
|
135
156
|
rescue Chan::WaitWritable
|
|
136
|
-
|
|
157
|
+
puts "Blocked - free send buffer"
|
|
137
158
|
ch.recv
|
|
138
159
|
retry
|
|
139
160
|
rescue Chan::WaitLockable
|
|
140
|
-
|
|
161
|
+
ch.wait_lockable
|
|
141
162
|
retry
|
|
142
163
|
end
|
|
143
164
|
|
|
@@ -166,6 +187,9 @@ processes:
|
|
|
166
187
|
#!/usr/bin/env ruby
|
|
167
188
|
require "xchan"
|
|
168
189
|
|
|
190
|
+
##
|
|
191
|
+
# 'lock: :file' is added just for the example
|
|
192
|
+
# It is the default behavior, and not necessary
|
|
169
193
|
ch = xchan(:marshal, lock: :file)
|
|
170
194
|
5.times.map do
|
|
171
195
|
fork do
|
|
@@ -179,7 +203,7 @@ end.each { Process.wait(_1) }
|
|
|
179
203
|
The null lock is the same as using no lock at all. The null lock is
|
|
180
204
|
implemented as a collection of no-op operations. The null lock is
|
|
181
205
|
implemented in the
|
|
182
|
-
[Chan::NullLock](https://0x1eef.github
|
|
206
|
+
[Chan::NullLock](https://0x1eef.github.io/x/xchan.rb/Chan/NullLock.html)
|
|
183
207
|
class, and in certain situations, it can be useful and preferable
|
|
184
208
|
to using a file lock:
|
|
185
209
|
|
|
@@ -200,9 +224,9 @@ Process.wait
|
|
|
200
224
|
|
|
201
225
|
A channel has one socket for read operations and another
|
|
202
226
|
socket for write operations.
|
|
203
|
-
[Chan::UNIXSocket#r](https://0x1eef.github
|
|
227
|
+
[Chan::UNIXSocket#r](https://0x1eef.github.io/x/xchan.rb/Chan/UNIXSocket.html#r-instance_method)
|
|
204
228
|
returns the socket used for read operations, and
|
|
205
|
-
[Chan::UNIXSocket#w](https://0x1eef.github
|
|
229
|
+
[Chan::UNIXSocket#w](https://0x1eef.github.io/x/xchan.rb/Chan/UNIXSocket.html#w-instance_method)
|
|
206
230
|
returns the socket used for write operations:
|
|
207
231
|
|
|
208
232
|
```ruby
|
data/lib/xchan/bytes.rb
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
# increases in size, and when an object is read from
|
|
8
8
|
# a channel, the collection decreases in size.
|
|
9
9
|
class Chan::Bytes
|
|
10
|
-
require "json"
|
|
11
10
|
require_relative "counter"
|
|
12
11
|
|
|
13
12
|
##
|
|
@@ -15,7 +14,8 @@ class Chan::Bytes
|
|
|
15
14
|
# Directory where temporary files are stored
|
|
16
15
|
# @return [Chan::Bytes]
|
|
17
16
|
def initialize(tmpdir)
|
|
18
|
-
@io = Chan.temporary_file(%w[bytes .
|
|
17
|
+
@io = Chan.temporary_file(%w[bytes .bin], tmpdir:)
|
|
18
|
+
@io.binmode
|
|
19
19
|
@io.sync = true
|
|
20
20
|
write(@io, [])
|
|
21
21
|
end
|
|
@@ -79,15 +79,17 @@ class Chan::Bytes
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def write(io, bytes)
|
|
82
|
+
io.rewind
|
|
82
83
|
io.truncate(0)
|
|
83
|
-
io.write(serialize(bytes))
|
|
84
|
+
io.write(serialize(bytes))
|
|
85
|
+
io.rewind
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
def serialize(bytes)
|
|
87
|
-
|
|
89
|
+
bytes.pack("Q>*")
|
|
88
90
|
end
|
|
89
91
|
|
|
90
92
|
def deserialize(bytes)
|
|
91
|
-
|
|
93
|
+
bytes.unpack("Q>*")
|
|
92
94
|
end
|
|
93
95
|
end
|
data/lib/xchan/counter.rb
CHANGED
|
@@ -5,57 +5,72 @@
|
|
|
5
5
|
# for the number of written and received bytes on a
|
|
6
6
|
# given channel.
|
|
7
7
|
class Chan::Counter
|
|
8
|
-
require "json"
|
|
9
|
-
|
|
10
8
|
##
|
|
11
9
|
# @param [String] tmpdir
|
|
12
10
|
# Directory where temporary files are stored
|
|
13
11
|
# @return [Chan::Counter]
|
|
14
12
|
def initialize(tmpdir)
|
|
15
|
-
@io = Chan.temporary_file(%w[counter .
|
|
16
|
-
|
|
13
|
+
@io = Chan.temporary_file(%w[counter .bin], tmpdir:)
|
|
14
|
+
@io.binmode
|
|
15
|
+
@io.sync = true
|
|
16
|
+
write(@io, 0, 0)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
##
|
|
20
20
|
# @return [Integer]
|
|
21
21
|
# Returns the number of bytes written to a channel
|
|
22
22
|
def bytes_written
|
|
23
|
-
read(@io)
|
|
23
|
+
_, bytes = read(@io)
|
|
24
|
+
bytes
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
##
|
|
27
28
|
# @return [Integer]
|
|
28
29
|
# Returns the number of bytes read from a channel
|
|
29
30
|
def bytes_read
|
|
30
|
-
read(@io)
|
|
31
|
+
bytes, _ = read(@io)
|
|
32
|
+
bytes
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
##
|
|
34
|
-
# @param [
|
|
36
|
+
# @param [Integer] bytes_read
|
|
37
|
+
# Number of bytes read to increment the counter by
|
|
38
|
+
# @param [Integer] bytes_written
|
|
39
|
+
# Number of bytes written to increment the counter by
|
|
35
40
|
# @return [void]
|
|
36
41
|
# @private
|
|
37
|
-
def increment!(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
def increment!(bytes_read: 0, bytes_written: 0)
|
|
43
|
+
bytes_in, bytes_out = read(@io)
|
|
44
|
+
bytes_in += bytes_read
|
|
45
|
+
bytes_out += bytes_written
|
|
46
|
+
write(@io, bytes_in, bytes_out)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Close the counter
|
|
51
|
+
# @return [void]
|
|
52
|
+
def close
|
|
53
|
+
@io.close
|
|
41
54
|
end
|
|
42
55
|
|
|
43
56
|
private
|
|
44
57
|
|
|
45
|
-
def write(io,
|
|
58
|
+
def write(io, bytes_read, bytes_written)
|
|
59
|
+
io.rewind
|
|
46
60
|
io.truncate(0)
|
|
47
|
-
io.write(serialize(
|
|
61
|
+
io.write(serialize(bytes_read, bytes_written))
|
|
62
|
+
io.rewind
|
|
48
63
|
end
|
|
49
64
|
|
|
50
65
|
def read(io)
|
|
51
66
|
deserialize(io.read).tap { io.rewind }
|
|
52
67
|
end
|
|
53
68
|
|
|
54
|
-
def serialize(
|
|
55
|
-
|
|
69
|
+
def serialize(bytes_read, bytes_written)
|
|
70
|
+
[bytes_read, bytes_written].pack("Q>Q>")
|
|
56
71
|
end
|
|
57
72
|
|
|
58
|
-
def deserialize(
|
|
59
|
-
|
|
73
|
+
def deserialize(payload)
|
|
74
|
+
payload.unpack("Q>Q>")
|
|
60
75
|
end
|
|
61
76
|
end
|
data/lib/xchan/null_lock.rb
CHANGED
data/lib/xchan/unix_socket.rb
CHANGED
|
@@ -61,8 +61,8 @@ class Chan::UNIXSocket
|
|
|
61
61
|
# @return [void]
|
|
62
62
|
def close
|
|
63
63
|
@lock.lock
|
|
64
|
-
raise IOError, "channel
|
|
65
|
-
[@r, @w, @bytes, @lock].each(&:close)
|
|
64
|
+
raise IOError, "closed channel" if closed?
|
|
65
|
+
[@r, @w, @bytes, @counter, @lock].each(&:close)
|
|
66
66
|
rescue IOError => ex
|
|
67
67
|
@lock.release
|
|
68
68
|
raise(ex)
|
|
@@ -222,22 +222,36 @@ class Chan::UNIXSocket
|
|
|
222
222
|
|
|
223
223
|
##
|
|
224
224
|
# Waits for the channel to become readable
|
|
225
|
-
# @param [Float, Integer, nil]
|
|
225
|
+
# @param [Float, Integer, nil] timeout
|
|
226
226
|
# The number of seconds to wait. Waits indefinitely with no arguments.
|
|
227
227
|
# @return [Chan::UNIXSocket, nil]
|
|
228
228
|
# Returns self when the channel is readable, otherwise returns nil
|
|
229
|
-
def wait_readable(
|
|
230
|
-
@r.wait_readable(
|
|
229
|
+
def wait_readable(timeout = nil)
|
|
230
|
+
@r.wait_readable(timeout) and self
|
|
231
231
|
end
|
|
232
232
|
|
|
233
233
|
##
|
|
234
234
|
# Waits for the channel to become writable
|
|
235
|
-
# @param [Float, Integer, nil]
|
|
235
|
+
# @param [Float, Integer, nil] timeout
|
|
236
236
|
# The number of seconds to wait. Waits indefinitely with no arguments.
|
|
237
237
|
# @return [Chan::UNIXSocket, nil]
|
|
238
238
|
# Returns self when the channel is writable, otherwise returns nil
|
|
239
|
-
def wait_writable(
|
|
240
|
-
@w.wait_writable(
|
|
239
|
+
def wait_writable(timeout = nil)
|
|
240
|
+
@w.wait_writable(timeout) and self
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
##
|
|
244
|
+
# Waits for the channel to become lockable
|
|
245
|
+
# @param [Float, Integer, nil] timeout
|
|
246
|
+
# The number of seconds to wait before timeout
|
|
247
|
+
# @return [Chan::UNIXSocket, nil]
|
|
248
|
+
def wait_lockable(timeout = nil)
|
|
249
|
+
start = (timeout ? gettime : nil)
|
|
250
|
+
loop do
|
|
251
|
+
break(nil) if start && (gettime - start) >= timeout
|
|
252
|
+
break(self) if @lock.lockable?
|
|
253
|
+
sleep 0.01
|
|
254
|
+
end
|
|
241
255
|
end
|
|
242
256
|
|
|
243
257
|
##
|
|
@@ -259,4 +273,8 @@ class Chan::UNIXSocket
|
|
|
259
273
|
def deserialize(str)
|
|
260
274
|
@s.load(str)
|
|
261
275
|
end
|
|
276
|
+
|
|
277
|
+
def gettime
|
|
278
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
279
|
+
end
|
|
262
280
|
end
|
data/lib/xchan/version.rb
CHANGED
data/lib/xchan.rb
CHANGED
|
@@ -65,7 +65,7 @@ module Chan
|
|
|
65
65
|
def self.locks
|
|
66
66
|
{
|
|
67
67
|
null: lambda { |_tmpdir| Chan::NullLock },
|
|
68
|
-
file: lambda { |tmpdir|
|
|
68
|
+
file: lambda { |tmpdir| Lockf.new Chan.temporary_file(%w[xchan lock], tmpdir:) }
|
|
69
69
|
}
|
|
70
70
|
end
|
|
71
71
|
end
|
data/test/xchan_test.rb
CHANGED
|
@@ -223,3 +223,42 @@ class Chan::TemporaryFileTest < Chan::Test
|
|
|
223
223
|
@file ||= Chan.temporary_file %w[foobar .txt]
|
|
224
224
|
end
|
|
225
225
|
end
|
|
226
|
+
|
|
227
|
+
##
|
|
228
|
+
# Chan::UNIXSocket#wait_lockable
|
|
229
|
+
class Chan::WaitLockableTest < Chan::Test
|
|
230
|
+
def test_wait_lockable_on_lockable_channel
|
|
231
|
+
assert_instance_of Chan::UNIXSocket, ch.wait_lockable
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def test_wait_lockable_on_locked_channel
|
|
235
|
+
aux = xchan(:pure)
|
|
236
|
+
lock! do
|
|
237
|
+
Process.wait fork { aux.send ch.wait_lockable(0.1).class.to_s }
|
|
238
|
+
end
|
|
239
|
+
assert_equal "NilClass", aux.recv
|
|
240
|
+
ensure
|
|
241
|
+
aux.close
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def test_wait_lockable_on_null_lock
|
|
245
|
+
ch = xchan(:pure, lock: :null)
|
|
246
|
+
assert_instance_of Chan::UNIXSocket, ch.wait_lockable
|
|
247
|
+
ensure
|
|
248
|
+
ch.close
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
private
|
|
252
|
+
|
|
253
|
+
def lock!
|
|
254
|
+
ch.instance_variable_get(:@lock).lock
|
|
255
|
+
yield
|
|
256
|
+
ensure
|
|
257
|
+
release!
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def release!
|
|
261
|
+
ch.instance_variable_get(:@lock).release
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
data/xchan.rb.gemspec
CHANGED
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
|
18
18
|
gem.require_paths = ["lib"]
|
|
19
19
|
gem.summary = "An easy to use InterProcess Communication (IPC) library"
|
|
20
20
|
gem.description = gem.summary
|
|
21
|
-
gem.add_runtime_dependency "lockf.rb", "~>
|
|
21
|
+
gem.add_runtime_dependency "lockf.rb", "~> 3.0"
|
|
22
22
|
gem.add_development_dependency "test-unit", "~> 3.5.7"
|
|
23
23
|
gem.add_development_dependency "yard", "~> 0.9"
|
|
24
24
|
gem.add_development_dependency "kramdown", "~> 2.5"
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xchan.rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.20.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- '0x1eef'
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: lockf.rb
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '3.0'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '3.0'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: test-unit
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -153,7 +152,6 @@ homepage: https://github.com/0x1eef/xchan.rb#readme
|
|
|
153
152
|
licenses:
|
|
154
153
|
- 0BSD
|
|
155
154
|
metadata: {}
|
|
156
|
-
post_install_message:
|
|
157
155
|
rdoc_options: []
|
|
158
156
|
require_paths:
|
|
159
157
|
- lib
|
|
@@ -168,8 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
168
166
|
- !ruby/object:Gem::Version
|
|
169
167
|
version: '0'
|
|
170
168
|
requirements: []
|
|
171
|
-
rubygems_version: 3.
|
|
172
|
-
signing_key:
|
|
169
|
+
rubygems_version: 3.6.9
|
|
173
170
|
specification_version: 4
|
|
174
171
|
summary: An easy to use InterProcess Communication (IPC) library
|
|
175
172
|
test_files: []
|