xchan.rb 0.16.4
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 +7 -0
- data/.github/workflows/tests.yml +26 -0
- data/.gitignore +9 -0
- data/.gitlab-ci.yml +12 -0
- data/.projectile +1 -0
- data/.rubocop.yml +34 -0
- data/.yardopts +4 -0
- data/Gemfile +6 -0
- data/LICENSE +15 -0
- data/README.md +242 -0
- data/Rakefile.rb +9 -0
- data/lib/xchan/bytes.rb +109 -0
- data/lib/xchan/mixin.rb +20 -0
- data/lib/xchan/stat.rb +63 -0
- data/lib/xchan/tempfile.rb +347 -0
- data/lib/xchan/unix_socket.rb +329 -0
- data/lib/xchan/version.rb +5 -0
- data/lib/xchan.rb +53 -0
- data/share/xchan.rb/examples/read_operations/1_blocking_read.rb +20 -0
- data/share/xchan.rb/examples/read_operations/2_nonblocking_read.rb +17 -0
- data/share/xchan.rb/examples/serialization/1_serializers.rb +20 -0
- data/share/xchan.rb/examples/setup.rb +4 -0
- data/share/xchan.rb/examples/socket/2_options.rb +12 -0
- data/share/xchan.rb/examples/stress_tests/1_parallel_access_stress_test.rb +10 -0
- data/share/xchan.rb/examples/write_operations/1_blocking_write.rb +10 -0
- data/share/xchan.rb/examples/write_operations/2_nonblocking_write.rb +24 -0
- data/test/readme_test.rb +44 -0
- data/test/setup.rb +7 -0
- data/test/xchan_test.rb +192 -0
- data/xchan.rb.gemspec +22 -0
- metadata +170 -0
data/lib/xchan.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Chan
|
4
|
+
require_relative "xchan/version"
|
5
|
+
require_relative "xchan/unix_socket"
|
6
|
+
require_relative "xchan/tempfile"
|
7
|
+
require_relative "xchan/mixin"
|
8
|
+
|
9
|
+
WaitReadable = Class.new(IO::EAGAINWaitReadable)
|
10
|
+
WaitWritable = Class.new(IO::EAGAINWaitWritable)
|
11
|
+
WaitLockable = Class.new(Errno::EWOULDBLOCK)
|
12
|
+
Plain = Class.new do
|
13
|
+
def self.dump(str) = str.to_s
|
14
|
+
def self.load(str) = str.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Returns an unlinked {Chan::Tempfile Chan::Tempfile} object
|
19
|
+
# that can be read from, and written to by the process that
|
20
|
+
# created it, inclusive of its child processes, but not of
|
21
|
+
# processes other than that.
|
22
|
+
#
|
23
|
+
# @param [String] basename
|
24
|
+
# Basename of the temporary file.
|
25
|
+
#
|
26
|
+
# @param [String] tmpdir
|
27
|
+
# Parent directory of the temporary file.
|
28
|
+
#
|
29
|
+
# @return [Chan::Tempfile]
|
30
|
+
# Returns an instance of {Chan::Tempfile Chan::Tempfile}.
|
31
|
+
def self.temporary_file(basename, tmpdir: Dir.tmpdir)
|
32
|
+
Chan::Tempfile.new(basename, tmpdir, perm: 0).tap(&:unlink)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.serializers
|
36
|
+
{
|
37
|
+
plain: lambda { Plain },
|
38
|
+
marshal: lambda { Marshal },
|
39
|
+
json: lambda {
|
40
|
+
require "json" unless defined?(JSON)
|
41
|
+
JSON
|
42
|
+
},
|
43
|
+
yaml: lambda {
|
44
|
+
require "yaml" unless defined?(YAML)
|
45
|
+
YAML
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Object
|
52
|
+
include Chan::Mixin
|
53
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../setup"
|
4
|
+
require "xchan"
|
5
|
+
|
6
|
+
$stdout.sync = true
|
7
|
+
ch = xchan
|
8
|
+
pid = fork do
|
9
|
+
print "Received random number (child process): ", ch.recv, "\n"
|
10
|
+
end
|
11
|
+
# Delay for a second to let a process fork, and call "ch.recv"
|
12
|
+
sleep(1)
|
13
|
+
print "Send a random number (from parent process)", "\n"
|
14
|
+
ch.send(rand(21))
|
15
|
+
Process.wait(pid)
|
16
|
+
ch.close
|
17
|
+
|
18
|
+
##
|
19
|
+
# Send a random number (from parent process)
|
20
|
+
# Received random number (child process): XX
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../setup"
|
4
|
+
require "xchan"
|
5
|
+
|
6
|
+
def read(ch)
|
7
|
+
ch.recv_nonblock
|
8
|
+
rescue Chan::WaitReadable
|
9
|
+
print "Wait 1 second for channel to be readable", "\n"
|
10
|
+
ch.wait_readable(1)
|
11
|
+
retry
|
12
|
+
rescue Chan::WaitLockable
|
13
|
+
sleep 0.01
|
14
|
+
retry
|
15
|
+
end
|
16
|
+
trap("SIGINT") { exit(1) }
|
17
|
+
read(xchan)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../setup"
|
4
|
+
require "xchan"
|
5
|
+
|
6
|
+
##
|
7
|
+
# This channel uses Marshal to serialize objects.
|
8
|
+
ch = xchan
|
9
|
+
pid = fork { print "Received message: ", ch.recv[:msg], "\n" }
|
10
|
+
ch.send(msg: "serialized by Marshal")
|
11
|
+
ch.close
|
12
|
+
Process.wait(pid)
|
13
|
+
|
14
|
+
##
|
15
|
+
# This channel also uses Marshal to serialize objects.
|
16
|
+
ch = xchan(:marshal)
|
17
|
+
pid = fork { print "Received message: ", ch.recv[:msg], "\n" }
|
18
|
+
ch.send(msg: "serialized by Marshal")
|
19
|
+
ch.close
|
20
|
+
Process.wait(pid)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "xchan"
|
2
|
+
ch = xchan(:marshal)
|
3
|
+
|
4
|
+
##
|
5
|
+
# Print the value of SO_RCVBUF
|
6
|
+
rcvbuf = ch.getsockopt(:reader, Socket::SOL_SOCKET, Socket::SO_RCVBUF)
|
7
|
+
print "The read buffer can contain a maximum of: ", rcvbuf.int, " bytes.\n"
|
8
|
+
|
9
|
+
##
|
10
|
+
# Print the value of SO_SNDBUF
|
11
|
+
sndbuf = ch.getsockopt(:writer, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
|
12
|
+
print "The maximum size of a single message is: ", sndbuf.int, " bytes.\n"
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../setup"
|
4
|
+
require "xchan"
|
5
|
+
|
6
|
+
pids = []
|
7
|
+
ch = xchan
|
8
|
+
pids.concat 50.times.map { fork { ch.send(["a" * rand(200)]) } }
|
9
|
+
pids.concat 50.times.map { fork { print "PID: ", Process.pid, ", buf size:", ch.recv[0].size, "\n" } }
|
10
|
+
pids.each { Process.wait(_1) }
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../setup"
|
4
|
+
require "xchan"
|
5
|
+
|
6
|
+
ch = xchan(:marshal, socket: Socket::SOCK_STREAM)
|
7
|
+
sndbuf = ch.getsockopt(:reader, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
|
8
|
+
while ch.bytes_sent <= sndbuf.int
|
9
|
+
ch.send(1)
|
10
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../setup"
|
4
|
+
require "xchan"
|
5
|
+
|
6
|
+
def send_nonblock(ch, buf)
|
7
|
+
ch.send_nonblock(buf)
|
8
|
+
rescue Chan::WaitWritable
|
9
|
+
print "Blocked - free send buffer", "\n"
|
10
|
+
ch.recv
|
11
|
+
retry
|
12
|
+
rescue Chan::WaitLockable
|
13
|
+
sleep 0.01
|
14
|
+
retry
|
15
|
+
end
|
16
|
+
|
17
|
+
ch = xchan(:marshal, socket: Socket::SOCK_STREAM)
|
18
|
+
sndbuf = ch.getsockopt(:writer, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
|
19
|
+
while ch.bytes_sent <= sndbuf.int
|
20
|
+
send_nonblock(ch, 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Blocked - free send buffer
|
data/test/readme_test.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "setup"
|
4
|
+
require "test/cmd"
|
5
|
+
|
6
|
+
class Chan::ReadmeTest < Test::Unit::TestCase
|
7
|
+
include Test::Cmd
|
8
|
+
|
9
|
+
def test_serialization_1_serializers
|
10
|
+
assert_equal "Received message: serialized by Marshal\n" * 2,
|
11
|
+
readme_example("serialization/1_serializers.rb").stdout
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_read_operations_1_blocking_read
|
15
|
+
r = 'Send a random number \(from parent process\)\s*' \
|
16
|
+
'Received random number \(child process\): \d+'
|
17
|
+
assert_match Regexp.new(r),
|
18
|
+
readme_example("read_operations/1_blocking_read.rb").stdout
|
19
|
+
.tr("\n", " ")
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_write_operations_2_non_blocking_write
|
23
|
+
assert_equal ["Blocked - free send buffer\n"],
|
24
|
+
readme_example("write_operations/2_nonblocking_write.rb").stdout
|
25
|
+
.each_line
|
26
|
+
.uniq
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_socket_2_options
|
30
|
+
r = 'The read buffer can contain a maximum of: \d{1,6} bytes.\s*' \
|
31
|
+
'The maximum size of a single message is: \d{1,6} bytes.\s*'
|
32
|
+
assert_match Regexp.new(r),
|
33
|
+
readme_example("socket/2_options.rb").stdout
|
34
|
+
.tr("\n", " ")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def readme_example(path)
|
40
|
+
examples_dir = File.join(Dir.getwd, "share", "xchan.rb", "examples")
|
41
|
+
example = File.join(examples_dir, path)
|
42
|
+
cmd "bundle exec ruby #{example}"
|
43
|
+
end
|
44
|
+
end
|
data/test/setup.rb
ADDED
data/test/xchan_test.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "setup"
|
4
|
+
|
5
|
+
class Chan::Test < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@ch = xchan ENV.fetch("SERIALIZER", "marshal").to_sym
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
ch.close unless ch.closed?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def ch
|
17
|
+
@ch
|
18
|
+
end
|
19
|
+
|
20
|
+
def object
|
21
|
+
case ENV["SERIALIZER"]
|
22
|
+
when "plain" then "xchan"
|
23
|
+
else %w[xchan]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def object_size
|
28
|
+
ch.serializer.dump(object).bytesize
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Chan::UNIXSocket#send
|
34
|
+
class Chan::SendTest < Chan::Test
|
35
|
+
def test_send_return_value
|
36
|
+
assert_equal object_size, ch.send(object)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_send_with_multiple_objects
|
40
|
+
3.times { |i| Process.wait fork { ch.send(object) } }
|
41
|
+
assert_equal [object, object, object], 3.times.map { ch.recv }
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_send_race_condition
|
45
|
+
pids = 4.times.map { fork { exit(ch.recv.to_i) } }
|
46
|
+
sleep(0.1 * 4)
|
47
|
+
pids.each.with_index(1) { ch.send(object) }
|
48
|
+
assert_equal pids.map { 42 }, pids.map { Process.wait2(_1).last.exitstatus }
|
49
|
+
end
|
50
|
+
|
51
|
+
def object
|
52
|
+
42.to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Chan::UNIXSocket#recv
|
58
|
+
class Chan::RecvTest < Chan::Test
|
59
|
+
include Timeout
|
60
|
+
|
61
|
+
def test_recv_with_null_byte
|
62
|
+
ch.send(object.dup << "\x00")
|
63
|
+
assert_equal object.dup << "\x00", ch.recv
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_that_recv_blocks
|
67
|
+
assert_raises(Timeout::Error) do
|
68
|
+
timeout(0.3) { ch.recv }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Chan::UNIXSocket#recv_nonblock
|
75
|
+
class Chan::RecvNonBlockTest < Chan::Test
|
76
|
+
include Timeout
|
77
|
+
|
78
|
+
def test_recv_nonblock_with_empty_channel
|
79
|
+
assert_raise(Chan::WaitReadable) { ch.recv_nonblock }
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_recv_nonblock_with_a_lock
|
83
|
+
ch.instance_variable_get(:@lock).lock
|
84
|
+
pid = fork do
|
85
|
+
ch.recv_nonblock
|
86
|
+
exit(1)
|
87
|
+
rescue Chan::WaitLockable
|
88
|
+
exit(0)
|
89
|
+
end
|
90
|
+
Process.wait(pid)
|
91
|
+
assert_equal 0, $?.exitstatus
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Chan::UNIXSocket#empty?
|
97
|
+
class Chan::EmptyTest < Chan::Test
|
98
|
+
def test_empty_with_empty_channel
|
99
|
+
assert_equal true, ch.empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_empty_with_one_object
|
103
|
+
ch.send(object)
|
104
|
+
assert_equal false, ch.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_empty_after_recv
|
108
|
+
ch.send(object)
|
109
|
+
ch.recv
|
110
|
+
assert_equal true, ch.empty?
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_empty_on_closed_channel
|
114
|
+
ch.send(object)
|
115
|
+
ch.close
|
116
|
+
assert_equal true, ch.empty?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Chan::UNIXSocket#size
|
122
|
+
class Chan::SizeTest < Chan::Test
|
123
|
+
def test_size_with_one_object
|
124
|
+
ch.send(object)
|
125
|
+
assert_equal 1, ch.size
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_size_with_two_objects
|
129
|
+
2.times { ch.send(object) }
|
130
|
+
assert_equal 2, ch.size
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_size_after_recv
|
134
|
+
ch.send(object)
|
135
|
+
ch.recv
|
136
|
+
assert_equal 0, ch.size
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Chan::UNIXSocket#to_a
|
142
|
+
class Chan::ToArrayTest < Chan::Test
|
143
|
+
def test_to_a_with_splat
|
144
|
+
3.times { ch.send(object) }
|
145
|
+
assert_equal [object, object, object], splat(*ch)
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_to_a_with_last
|
149
|
+
3.times { ch.send(object) }
|
150
|
+
assert_equal object, ch.to_a.last
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_to_a_with_empty_channel
|
154
|
+
assert_equal [], ch.to_a
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def splat(*args)
|
160
|
+
args
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Chan::UNIXSocket#bytes_written
|
166
|
+
class Chan::BytesWrittenTest < Chan::Test
|
167
|
+
def test_bytes_written_with_one_object
|
168
|
+
Process.wait fork { ch.send(object) }
|
169
|
+
assert_equal object_size, ch.bytes_written
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_bytes_written_with_two_objects
|
173
|
+
2.times { Process.wait fork { ch.send(object) } }
|
174
|
+
assert_equal object_size * 2, ch.bytes_written
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
##
|
179
|
+
# Chan::UNIXSocket#bytes_read
|
180
|
+
class Chan::BytesReadTest < Chan::Test
|
181
|
+
def test_bytes_read_with_one_object
|
182
|
+
ch.send(object)
|
183
|
+
Process.wait fork { ch.recv }
|
184
|
+
assert_equal object_size, ch.bytes_read
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_bytes_read_with_two_objects
|
188
|
+
2.times { ch.send(object) }
|
189
|
+
2.times { Process.wait fork { ch.recv } }
|
190
|
+
assert_equal object_size * 2, ch.bytes_read
|
191
|
+
end
|
192
|
+
end
|
data/xchan.rb.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./lib/xchan/version"
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "xchan.rb"
|
6
|
+
gem.authors = ["0x1eef"]
|
7
|
+
gem.email = ["0x1eef@protonmail.com"]
|
8
|
+
gem.homepage = "https://github.com/0x1eef/xchan.rb#readme"
|
9
|
+
gem.version = Chan::VERSION
|
10
|
+
gem.licenses = ["0BSD"]
|
11
|
+
gem.files = `git ls-files`.split($/)
|
12
|
+
gem.require_paths = ["lib"]
|
13
|
+
gem.summary = "An easy to use InterProcess Communication (IPC) library."
|
14
|
+
gem.description = gem.summary
|
15
|
+
gem.add_runtime_dependency "lockf.rb", "~> 0.10.6"
|
16
|
+
gem.add_development_dependency "test-unit", "~> 3.5.7"
|
17
|
+
gem.add_development_dependency "yard", "~> 0.9"
|
18
|
+
gem.add_development_dependency "redcarpet", "~> 3.5"
|
19
|
+
gem.add_development_dependency "standard", "~> 1.13"
|
20
|
+
gem.add_development_dependency "test-cmd.rb", "~> 0.2"
|
21
|
+
gem.add_development_dependency "rake", "~> 13.1"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xchan.rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.16.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- '0x1eef'
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-01-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: lockf.rb
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.10.6
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.10.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: test-unit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.5.7
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.5.7
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: yard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.9'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: redcarpet
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.5'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.5'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: standard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.13'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.13'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: test-cmd.rb
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.2'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '13.1'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '13.1'
|
111
|
+
description: An easy to use InterProcess Communication (IPC) library.
|
112
|
+
email:
|
113
|
+
- 0x1eef@protonmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".github/workflows/tests.yml"
|
119
|
+
- ".gitignore"
|
120
|
+
- ".gitlab-ci.yml"
|
121
|
+
- ".projectile"
|
122
|
+
- ".rubocop.yml"
|
123
|
+
- ".yardopts"
|
124
|
+
- Gemfile
|
125
|
+
- LICENSE
|
126
|
+
- README.md
|
127
|
+
- Rakefile.rb
|
128
|
+
- lib/xchan.rb
|
129
|
+
- lib/xchan/bytes.rb
|
130
|
+
- lib/xchan/mixin.rb
|
131
|
+
- lib/xchan/stat.rb
|
132
|
+
- lib/xchan/tempfile.rb
|
133
|
+
- lib/xchan/unix_socket.rb
|
134
|
+
- lib/xchan/version.rb
|
135
|
+
- share/xchan.rb/examples/read_operations/1_blocking_read.rb
|
136
|
+
- share/xchan.rb/examples/read_operations/2_nonblocking_read.rb
|
137
|
+
- share/xchan.rb/examples/serialization/1_serializers.rb
|
138
|
+
- share/xchan.rb/examples/setup.rb
|
139
|
+
- share/xchan.rb/examples/socket/2_options.rb
|
140
|
+
- share/xchan.rb/examples/stress_tests/1_parallel_access_stress_test.rb
|
141
|
+
- share/xchan.rb/examples/write_operations/1_blocking_write.rb
|
142
|
+
- share/xchan.rb/examples/write_operations/2_nonblocking_write.rb
|
143
|
+
- test/readme_test.rb
|
144
|
+
- test/setup.rb
|
145
|
+
- test/xchan_test.rb
|
146
|
+
- xchan.rb.gemspec
|
147
|
+
homepage: https://github.com/0x1eef/xchan.rb#readme
|
148
|
+
licenses:
|
149
|
+
- 0BSD
|
150
|
+
metadata: {}
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
requirements: []
|
166
|
+
rubygems_version: 3.5.3
|
167
|
+
signing_key:
|
168
|
+
specification_version: 4
|
169
|
+
summary: An easy to use InterProcess Communication (IPC) library.
|
170
|
+
test_files: []
|