xchan.rb 0.17.0 → 0.17.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf2db46e92eb29ed0182377c209a8ff7511460d22a73225616c719cd05372884
4
- data.tar.gz: 05b70d50483d354f79e29f9219628c44cb6f4b25e63cbd5aee7e0c0a14da8865
3
+ metadata.gz: 9ce716f918994e2481e8fbd333dbdb7e0b258341a09e32d464a54a2331edbd09
4
+ data.tar.gz: f17d86bf4004ca1ebef87950ff9d20c7aaad29940e8c031ff84d817ade3adeb9
5
5
  SHA512:
6
- metadata.gz: f8fb7b0f73b09c4593dfa7892fd3e0061dce37242c0e9bebab0edd204cda399dd16c8fdbadce2ee6a5fac1fc6c1d583ad60eadd85c7ece4a145fdb18358aff0a
7
- data.tar.gz: ef454d2422ae0a719a19b7fad3bf670a3cc4b46154ea6e87786a4c6404bbe9f7af5437e81b3ed8651fa9e1f438ee7f43c38d10497ca9b50facd3aea3ededc7f0
6
+ metadata.gz: c77e364ea0f6365d4fddae810cd14e999cf584dd301e5a651a5565d78fcce157909f0b96a155f67f8f22bb50f1c7d5c2535653e4871f2e5dab862c1e38f207f3
7
+ data.tar.gz: a4f2d57cf1843407d7056c4528d7e41372ed84378e33f6ca2777b6b2dde93b80fe5e57d33d2b01337a089d808b11e3e6d92c91b4f57f86aeefc4a3e4782c6afa
@@ -12,7 +12,7 @@ jobs:
12
12
  fail-fast: false
13
13
  matrix:
14
14
  os: [ubuntu-latest, macos-latest]
15
- ruby: [3.1, 3.2]
15
+ ruby: [3.1, 3.2, 3.3]
16
16
  runs-on: ${{ matrix.os }}
17
17
  steps:
18
18
  - uses: actions/checkout@v2
@@ -23,4 +23,4 @@ jobs:
23
23
  - run: SERIALIZER=marshal bundle exec rake
24
24
  - run: SERIALIZER=json bundle exec rake
25
25
  - run: SERIALIZER=yaml bundle exec rake
26
- - run: SERIALIZER=plain bundle exec rake
26
+ - run: SERIALIZER=pure bundle exec rake
data/.gitlab-ci.yml CHANGED
@@ -9,4 +9,4 @@ test-ruby32:
9
9
  - SERIALIZER=marshal bundle exec rake
10
10
  - SERIALIZER=json bundle exec rake
11
11
  - SERIALIZER=yaml bundle exec rake
12
- - SERIALIZER=plain bundle exec rake
12
+ - SERIALIZER=pure bundle exec rake
data/README.md CHANGED
@@ -52,13 +52,14 @@ the parent process writes to the channel:
52
52
  require "xchan"
53
53
 
54
54
  ch = xchan(:marshal)
55
- Process.detach fork {
55
+ fork do
56
56
  print "Received a random number (child process): ", ch.recv, "\n"
57
- }
57
+ end
58
58
  sleep(1)
59
59
  print "Send a random number (from parent process)", "\n"
60
60
  ch.send(rand(21))
61
61
  ch.close
62
+ Process.wait
62
63
 
63
64
  ##
64
65
  # Send a random number (from parent process)
data/lib/xchan/bytes.rb CHANGED
@@ -1,39 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ##
4
- # The {Chan::Bytes Chan::Bytes} class is similar
5
- # to an array, where each element represents the
6
- # number of bytes used to store an object on a
7
- # channel. When an object is written to a channel,
8
- # the array increases in size, and when an object
9
- # is read from a channel, the array decreases in
10
- # size.
4
+ # {Chan::Bytes Chan::Bytes} represents a collection
5
+ # of byte counts for each object stored on a channel.
6
+ # When an object is written to a channel, the collection
7
+ # increases in size, and when an object is read from
8
+ # a channel, the collection decreases in size.
11
9
  class Chan::Bytes
12
10
  require "json"
13
- require_relative "stat"
14
-
15
- ##
16
- # @return [Chan::Stat]
17
- attr_reader :stat
11
+ require_relative "counter"
18
12
 
19
13
  ##
20
14
  # @param [String] tmpdir
21
- # Path to a directory where temporary files will be stored.
15
+ # Directory where temporary files are stored
22
16
  #
23
17
  # @return [Chan::Bytes]
24
18
  def initialize(tmpdir)
25
- @serializer = JSON
26
- @io = Chan.temporary_file("xchan.bytes", tmpdir:)
19
+ @io = Chan.temporary_file(%w[bytes .json], tmpdir:)
27
20
  @io.sync = true
28
- @stat = Chan::Stat.new(tmpdir)
29
21
  write(@io, [])
30
22
  end
31
23
 
32
24
  ##
33
- # Insert a byte count at the head of the array
25
+ # Adds a count to the start of the collection
34
26
  #
35
27
  # @param [Integer] len
36
- # Number of bytes
28
+ # The bytesize of an object
37
29
  #
38
30
  # @return [void]
39
31
  def unshift(len)
@@ -41,15 +33,14 @@ class Chan::Bytes
41
33
  bytes = read(@io)
42
34
  bytes.unshift(len)
43
35
  write(@io, bytes)
44
- @stat.store(bytes_written: len)
45
36
  len
46
37
  end
47
38
 
48
39
  ##
49
- # Insert a byte count at the tail of the array
40
+ # Adds a count to the end of the collection
50
41
  #
51
42
  # @param [Integer] len
52
- # Number of bytes
43
+ # The bytesize of an object
53
44
  #
54
45
  # @return [void]
55
46
  def push(len)
@@ -57,25 +48,25 @@ class Chan::Bytes
57
48
  bytes = read(@io)
58
49
  bytes.push(len)
59
50
  write(@io, bytes)
60
- @stat.store(bytes_written: len)
61
51
  len
62
52
  end
63
53
 
64
54
  ##
55
+ # Removes a count from the start of the collection
56
+ #
65
57
  # @return [Integer]
66
- # Returns (and removes) a byte count from the head of the array
58
+ # Returns the removed byte count
67
59
  def shift
68
60
  bytes = read(@io)
69
61
  return 0 if bytes.size.zero?
70
62
  len = bytes.shift
71
63
  write(@io, bytes)
72
- @stat.store(bytes_read: len)
73
64
  len
74
65
  end
75
66
 
76
67
  ##
77
68
  # @return [Integer]
78
- # Returns the size of the array
69
+ # Returns the number of objects in the collection
79
70
  def size
80
71
  read(@io).size
81
72
  end
@@ -100,10 +91,10 @@ class Chan::Bytes
100
91
  end
101
92
 
102
93
  def serialize(bytes)
103
- @serializer.dump(bytes)
94
+ JSON.dump(bytes)
104
95
  end
105
96
 
106
97
  def deserialize(bytes)
107
- @serializer.load(bytes)
98
+ JSON.load(bytes)
108
99
  end
109
100
  end
@@ -1,33 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ##
4
- # The {Chan::Stat Chan::Stat} class provides statistics
5
- # (eg number of bytes read, number of bytes written) for
6
- # a given channel.
7
- class Chan::Stat
4
+ # {Chan::Counter Chan::Counter} provides a counter
5
+ # for the number of written and received bytes on a
6
+ # given channel.
7
+ class Chan::Counter
8
8
  require "json"
9
9
 
10
10
  ##
11
11
  # @param [String] tmpdir
12
- # Path to a directory where temporary files will be stored.
12
+ # Directory where temporary files are stored
13
13
  #
14
- # @return [Chan::Stat]
14
+ # @return [Chan::Counter]
15
15
  def initialize(tmpdir)
16
- @serializer = JSON
17
- @io = Chan.temporary_file("xchan.stat", tmpdir:)
16
+ @io = Chan.temporary_file(%w[counter .json], tmpdir:)
18
17
  write(@io, {"bytes_read" => 0, "bytes_written" => 0})
19
18
  end
20
19
 
21
20
  ##
22
21
  # @return [Integer]
23
- # Returns the number of bytes written to a channel.
22
+ # Returns the number of bytes written to a channel
24
23
  def bytes_written
25
24
  read(@io).fetch("bytes_written")
26
25
  end
27
26
 
28
27
  ##
29
28
  # @return [Integer]
30
- # Returns the number of bytes read from a channel.
29
+ # Returns the number of bytes read from a channel
31
30
  def bytes_read
32
31
  read(@io).fetch("bytes_read")
33
32
  end
@@ -36,7 +35,7 @@ class Chan::Stat
36
35
  # @param [Hash] new_stat
37
36
  # @return [void]
38
37
  # @private
39
- def store(new_stat)
38
+ def increment!(new_stat)
40
39
  stat = read(@io)
41
40
  new_stat.each { stat[_1.to_s] += _2 }
42
41
  write(@io, stat)
@@ -54,10 +53,10 @@ class Chan::Stat
54
53
  end
55
54
 
56
55
  def serialize(bytes)
57
- @serializer.dump(bytes)
56
+ JSON.dump(bytes)
58
57
  end
59
58
 
60
59
  def deserialize(bytes)
61
- @serializer.load(bytes)
60
+ JSON.load(bytes)
62
61
  end
63
62
  end
@@ -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>] serializer
33
+ # @param [Symbol, <#dump, #load>] s
28
34
  # The name of a serializer
29
35
  #
30
36
  # @param [Integer] sock_type
@@ -35,32 +41,26 @@ class Chan::UNIXSocket
35
41
  #
36
42
  # @return [Chan::UNIXSocket]
37
43
  # Returns an instance of {Chan::UNIXSocket Chan::UNIXSocket}
38
- def initialize(serializer, sock_type: Socket::SOCK_DGRAM, tmpdir: Dir.tmpdir)
39
- @serializer = Chan.serializers[serializer]&.call || serializer
44
+ def initialize(s, sock_type: Socket::SOCK_DGRAM, tmpdir: Dir.tmpdir)
45
+ @s = Chan.shortcuts[s]&.call || s
40
46
  @r, @w = ::UNIXSocket.pair(sock_type)
41
47
  @bytes = Chan::Bytes.new(tmpdir)
42
- @lock = LockFile.new Chan.temporary_file("xchan.lock", tmpdir:)
43
- end
44
-
45
- ##
46
- # @return [<#dump, #load>]
47
- # Returns the serializer used by the channel.
48
- def serializer
49
- @serializer
48
+ @counter = Chan::Counter.new(tmpdir)
49
+ @lock = LockFile.new Chan.temporary_file(%w[xchan .lock], tmpdir:)
50
50
  end
51
51
 
52
52
  ##
53
53
  # @return [Boolean]
54
- # Returns true when the channel is closed.
54
+ # Returns true when the channel is closed
55
55
  def closed?
56
56
  @r.closed? and @w.closed?
57
57
  end
58
58
 
59
59
  ##
60
- # Closes the channel.
60
+ # Closes the channel
61
61
  #
62
62
  # @raise [IOError]
63
- # When the channel is closed.
63
+ # When the channel is closed
64
64
  #
65
65
  # @return [void]
66
66
  def close
@@ -79,13 +79,13 @@ class Chan::UNIXSocket
79
79
  # Performs a blocking write
80
80
  #
81
81
  # @param [Object] object
82
- # An object to write to the channel.
82
+ # An object
83
83
  #
84
84
  # @raise [IOError]
85
- # When the channel is closed.
85
+ # When the channel is closed
86
86
  #
87
87
  # @return [Object]
88
- # Returns the number of bytes written to the channel.
88
+ # Returns the number of bytes written to the channel
89
89
  def send(object)
90
90
  send_nonblock(object)
91
91
  rescue Chan::WaitWritable, Chan::WaitLockable
@@ -97,24 +97,25 @@ class Chan::UNIXSocket
97
97
  # Performs a non-blocking write
98
98
  #
99
99
  # @param [Object] object
100
- # An object to write to the channel.
100
+ # An object
101
101
  #
102
102
  # @raise [IOError]
103
- # When the channel is closed.
103
+ # When the channel is closed
104
104
  #
105
105
  # @raise [Chan::WaitWritable]
106
- # When a write to the underlying IO blocks.
106
+ # When a write to {#w} blocks
107
107
  #
108
108
  # @raise [Chan::WaitLockable]
109
- # When a write blocks because of a lock held by another process.
109
+ # When a write blocks because of a lock held by another process
110
110
  #
111
111
  # @return [Integer, nil]
112
- # Returns the number of bytes written to the channel.
112
+ # Returns the number of bytes written to the channel
113
113
  def send_nonblock(object)
114
114
  @lock.lock_nonblock
115
115
  raise IOError, "channel closed" if closed?
116
116
  len = @w.write_nonblock(serialize(object))
117
117
  @bytes.push(len)
118
+ @counter.increment!(bytes_written: len)
118
119
  len.tap { @lock.release }
119
120
  rescue IOError, IO::WaitWritable, Errno::ENOBUFS => ex
120
121
  @lock.release
@@ -134,10 +135,10 @@ class Chan::UNIXSocket
134
135
  # Performs a blocking read
135
136
  #
136
137
  # @raise [IOError]
137
- # When the channel is closed.
138
+ # When the channel is closed
138
139
  #
139
140
  # @return [Object]
140
- # Returns an object from the channel.
141
+ # Returns an object from the channel
141
142
  def recv
142
143
  recv_nonblock
143
144
  rescue Chan::WaitReadable
@@ -152,21 +153,22 @@ class Chan::UNIXSocket
152
153
  # Performs a non-blocking read
153
154
  #
154
155
  # @raise [IOError]
155
- # When the channel is closed.
156
+ # When the channel is closed
156
157
  #
157
158
  # @raise [Chan::WaitReadable]
158
- # When a read from the underlying IO blocks.
159
+ # When a read from {#r} blocks
159
160
  #
160
161
  # @raise [Chan::WaitLockable]
161
- # When a read blocks because of a lock held by another process.
162
+ # When a read blocks because of a lock held by another process
162
163
  #
163
164
  # @return [Object]
164
- # Returns an object from the channel.
165
+ # Returns an object from the channel
165
166
  def recv_nonblock
166
167
  @lock.lock_nonblock
167
168
  raise IOError, "closed channel" if closed?
168
169
  len = @bytes.shift
169
170
  obj = deserialize(@r.read_nonblock(len.zero? ? 1 : len))
171
+ @counter.increment!(bytes_read: len)
170
172
  obj.tap { @lock.release }
171
173
  rescue IOError => ex
172
174
  @lock.release
@@ -190,7 +192,7 @@ class Chan::UNIXSocket
190
192
  # ch.to_a.last # => 4
191
193
  #
192
194
  # @return [Array<Object>]
193
- # Returns the consumed contents of the channel.
195
+ # Returns the contents of the channel
194
196
  def to_a
195
197
  lock do
196
198
  [].tap { _1.push(recv) until empty? }
@@ -199,7 +201,7 @@ class Chan::UNIXSocket
199
201
 
200
202
  ##
201
203
  # @return [Boolean]
202
- # Returns true when the channel is empty.
204
+ # Returns true when the channel is empty
203
205
  def empty?
204
206
  return true if closed?
205
207
  lock { size.zero? }
@@ -210,23 +212,23 @@ class Chan::UNIXSocket
210
212
 
211
213
  ##
212
214
  # @return [Integer]
213
- # Returns the total number of bytes written to the channel.
215
+ # Returns the total number of bytes written to the channel
214
216
  def bytes_sent
215
- lock { @bytes.stat.bytes_written }
217
+ lock { @counter.bytes_written }
216
218
  end
217
219
  alias_method :bytes_written, :bytes_sent
218
220
 
219
221
  ##
220
222
  # @return [Integer]
221
- # Returns the total number of bytes read from the channel.
223
+ # Returns the total number of bytes read from the channel
222
224
  def bytes_received
223
- lock { @bytes.stat.bytes_read }
225
+ lock { @counter.bytes_read }
224
226
  end
225
227
  alias_method :bytes_read, :bytes_received
226
228
 
227
229
  ##
228
230
  # @return [Integer]
229
- # Returns the number of objects waiting to be read.
231
+ # Returns the number of objects waiting to be read
230
232
  def size
231
233
  lock { @bytes.size }
232
234
  end
@@ -238,25 +240,25 @@ class Chan::UNIXSocket
238
240
  # @group Wait methods
239
241
 
240
242
  ##
241
- # Waits for the channel to become readable.
243
+ # Waits for the channel to become readable
242
244
  #
243
245
  # @param [Float, Integer, nil] s
244
- # The number of seconds to wait. Waits indefinitely when "nil".
246
+ # The number of seconds to wait. Waits indefinitely with no arguments.
245
247
  #
246
248
  # @return [Chan::UNIXSocket, nil]
247
- # Returns self when the channel is readable, otherwise returns nil.
249
+ # Returns self when the channel is readable, otherwise returns nil
248
250
  def wait_readable(s = nil)
249
251
  @r.wait_readable(s) and self
250
252
  end
251
253
 
252
254
  ##
253
- # Waits for the channel to become writable.
255
+ # Waits for the channel to become writable
254
256
  #
255
257
  # @param [Float, Integer, nil] s
256
- # The number of seconds to wait. Waits indefinitely when "nil".
258
+ # The number of seconds to wait. Waits indefinitely with no arguments.
257
259
  #
258
260
  # @return [Chan::UNIXSocket, nil]
259
- # Returns self when the channel is writable, otherwise returns nil.
261
+ # Returns self when the channel is writable, otherwise returns nil
260
262
  def wait_writable(s = nil)
261
263
  @w.wait_writable(s) and self
262
264
  end
@@ -274,10 +276,10 @@ class Chan::UNIXSocket
274
276
  end
275
277
 
276
278
  def serialize(obj)
277
- @serializer.dump(obj)
279
+ @s.dump(obj)
278
280
  end
279
281
 
280
282
  def deserialize(str)
281
- @serializer.load(str)
283
+ @s.load(str)
282
284
  end
283
285
  end
data/lib/xchan/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Chan
4
- VERSION = "0.17.0"
4
+ VERSION = "0.17.2"
5
5
  end
data/lib/xchan.rb CHANGED
@@ -47,8 +47,8 @@ module Chan
47
47
 
48
48
  ##
49
49
  # @return [Hash<Symbol, Proc>]
50
- # A mapping of serializers
51
- def self.serializers
50
+ # Maps a short name to a serializer
51
+ def self.shortcuts
52
52
  {
53
53
  pure: lambda { Pure },
54
54
  marshal: lambda { Marshal },
@@ -72,11 +72,11 @@ module Kernel
72
72
  # ch.recv.pop # => 3
73
73
  # ch.close
74
74
  #
75
- # @param serializer (see Chan::UNIXSocket#initialize)
75
+ # @param s (see Chan::UNIXSocket#initialize)
76
76
  # @param sock_type (see Chan::UNIXSocket#initialize)
77
77
  # @param tmpdir (see Chan::UNIXSocket#initialize)
78
78
  # @return (see Chan::UNIXSocket#initialize)
79
- def xchan(serializer, **kw_args)
80
- Chan::UNIXSocket.new(serializer, **kw_args)
79
+ def xchan(s, **kw_args)
80
+ Chan::UNIXSocket.new(s, **kw_args)
81
81
  end
82
82
  end
@@ -5,13 +5,14 @@ require "xchan"
5
5
 
6
6
  $stdout.sync = true
7
7
  ch = xchan(:marshal)
8
- Process.detach fork {
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)
data/test/xchan_test.rb CHANGED
@@ -4,7 +4,7 @@ require_relative "setup"
4
4
 
5
5
  class Chan::Test < Test::Unit::TestCase
6
6
  def setup
7
- @ch = xchan ENV.fetch("SERIALIZER", "marshal").to_sym
7
+ @ch = xchan(serializer)
8
8
  end
9
9
 
10
10
  def teardown
@@ -17,9 +17,13 @@ class Chan::Test < Test::Unit::TestCase
17
17
  @ch
18
18
  end
19
19
 
20
+ def serializer
21
+ ENV.fetch("SERIALIZER", "pure").to_sym
22
+ end
23
+
20
24
  def object
21
- case ENV["SERIALIZER"]
22
- when "plain" then "xchan"
25
+ case serializer
26
+ when :pure then "xchan"
23
27
  else %w[xchan]
24
28
  end
25
29
  end
@@ -190,3 +194,32 @@ class Chan::BytesReadTest < Chan::Test
190
194
  assert_equal object_size * 2, ch.bytes_read
191
195
  end
192
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
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.17.0
4
+ version: 0.17.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - '0x1eef'
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-10 00:00:00.000000000 Z
11
+ date: 2024-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lockf.rb
@@ -127,7 +127,7 @@ files:
127
127
  - Rakefile.rb
128
128
  - lib/xchan.rb
129
129
  - lib/xchan/bytes.rb
130
- - lib/xchan/stat.rb
130
+ - lib/xchan/counter.rb
131
131
  - lib/xchan/tempfile.rb
132
132
  - lib/xchan/unix_socket.rb
133
133
  - lib/xchan/version.rb