io-extra 1.4.0 → 1.5.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.
@@ -0,0 +1,179 @@
1
+ ################################################################################
2
+ # io_extra_spec.rb
3
+ #
4
+ # Tests for the io-extra library. These should be run via the 'rake spec' task.
5
+ ################################################################################
6
+ require 'rspec'
7
+ require 'rbconfig'
8
+ require 'io/nonblock'
9
+ require 'io-extra'
10
+ require 'fileutils'
11
+
12
+ describe IO do
13
+ let(:linux) { RbConfig::CONFIG['host_os'] =~ /linux/i }
14
+ let(:osx) { RbConfig::CONFIG['host_os'] =~ /darwin/i }
15
+
16
+ before do
17
+ @file = 'delete_this.txt'
18
+ @fh = File.open(@file, 'w+')
19
+ @fh.puts "The quick brown fox jumped over the lazy dog's back"
20
+ end
21
+
22
+ after do
23
+ @fh.close rescue nil
24
+ @fh = nil
25
+ FileUtils.rm_f(@file)
26
+ end
27
+
28
+ context 'constants' do
29
+ example 'EXTRA_VERSION' do
30
+ expect(IO::EXTRA_VERSION).to eq('1.5.0')
31
+ expect(IO::EXTRA_VERSION).to be_frozen
32
+ end
33
+
34
+ example 'DIRECT' do
35
+ skip 'Skipped unless Linux' unless linux
36
+ # O_DIRECT value varies by architecture (e.g., 040000 on x86_64, 0100000 on others)
37
+ # Just verify it's defined and is a valid flag (positive integer)
38
+ expect(IO::DIRECT).to be_a(Integer)
39
+ expect(IO::DIRECT).to be > 0
40
+ expect(IO::DIRECT).to eq(File::DIRECT)
41
+ end
42
+
43
+ example 'DIRECTIO_ON and DIRECTIO_OFF' do
44
+ expect(IO::DIRECTIO_ON).not_to be_nil
45
+ expect(IO::DIRECTIO_OFF).not_to be_nil
46
+ end
47
+
48
+ example 'IOV_MAX_constant' do
49
+ expect(IO::IOV_MAX).to be_a(Integer)
50
+ end
51
+ end
52
+
53
+ context 'flags' do
54
+ example 'DIRECT flag' do
55
+ skip 'Skipped unless Linux' unless linux
56
+ expect { File.open(@fh.path, IO::RDWR | IO::DIRECT).close }.not_to raise_error
57
+ end
58
+
59
+ example 'directio? method' do
60
+ expect(@fh).to respond_to(:directio?)
61
+ expect{ @fh.directio? }.not_to raise_error
62
+ end
63
+
64
+ example 'set DIRECTIO_ON' do
65
+ expect(@fh).to respond_to(:directio=)
66
+ expect{ @fh.directio = 99 }.to raise_error(StandardError)
67
+ expect{ @fh.directio = IO::DIRECTIO_ON }.not_to raise_error
68
+ end
69
+ end
70
+
71
+ context 'fdwalk' do
72
+ example 'fdwalk basic functionality' do
73
+ expect(IO).to respond_to(:fdwalk)
74
+ expect{ IO.fdwalk(0){} }.not_to raise_error
75
+ end
76
+
77
+ example 'fdwalk_honors_lowfd' do
78
+ IO.fdwalk(1){ |f| expect(f.fileno >= 1).to be(true) }
79
+ end
80
+ end
81
+
82
+ context 'closefrom' do
83
+ example 'closefrom' do
84
+ expect(IO).to respond_to(:closefrom)
85
+ expect{ IO.closefrom(3) }.not_to raise_error
86
+ end
87
+ end
88
+
89
+ context 'pread' do
90
+ example 'pread basic functionality' do
91
+ @fh.close rescue nil
92
+ @fh = File.open(@file)
93
+ expect(IO).to respond_to(:pread)
94
+ expect(IO.pread(@fh, 5, 4)).to eq('quick')
95
+ end
96
+
97
+ example 'pread works in binary mode' do
98
+ @fh.close rescue nil
99
+ @fh = File.open(@file, 'ab')
100
+ @fh.binmode
101
+ size = @fh.stat.size
102
+ expect { @fh.syswrite("FOO\0HELLO") }.not_to raise_error
103
+ @fh.close rescue nil
104
+ @fh = File.open(@file)
105
+ expect(IO.pread(@fh, 3, size + 2)).to eq("O\0H")
106
+ end
107
+
108
+ example 'pread with offset works as expected' do
109
+ @fh.close rescue nil
110
+ @fh = File.open(@file)
111
+ size = @fh.stat.size
112
+ expect(IO.pread(@fh, 5, size - 3)).to eq("ck\n")
113
+ end
114
+ end
115
+
116
+ context 'pwrite' do
117
+ example 'pwrite basic functionality' do
118
+ expect(IO).to respond_to(:pwrite)
119
+ expect{ IO.pwrite(@fh, 'HAL', 0) }.not_to raise_error
120
+ end
121
+ end
122
+
123
+ context 'writev' do
124
+ example 'writev' do
125
+ expect(IO).to respond_to(:writev)
126
+ expect(IO.writev(@fh, %w[hello world])).to eq(10)
127
+ end
128
+ end
129
+
130
+ context 'writev_retry' do
131
+ skip 'no /dev/urandom found, skipping' unless File.exist?('/dev/urandom')
132
+ # rubocop:disable Metrics/BlockLength
133
+ example 'writev with retry works as expected' do
134
+ empty = ''
135
+
136
+ if empty.respond_to?(:force_encoding)
137
+ empty.force_encoding(Encoding::BINARY)
138
+ end
139
+
140
+ # bs * count should be > PIPE_BUF
141
+ [true, false].each do |nonblock|
142
+ [[512, 512], [131073, 3], [4098, 64]].each do |(bs, count)|
143
+ rd, wr = IO.pipe
144
+ wr.nonblock = nonblock
145
+ buf = File.open('/dev/urandom', 'rb') { |fp| fp.sysread(bs) }
146
+ vec = (1..count).map { buf }
147
+
148
+ pid = fork do
149
+ wr.close
150
+ tmp = []
151
+ sleep 0.1
152
+ loop do
153
+ tmp << rd.readpartial(8192, buf)
154
+ rescue EOFError
155
+ break
156
+ end
157
+ ok = (vec.join(empty) == tmp.join(empty))
158
+ exit! ok
159
+ end
160
+
161
+ expect { rd.close }.not_to raise_error
162
+ expect(IO.writev(wr.fileno, vec)).to eq(bs * count)
163
+ expect { wr.close }.not_to raise_error
164
+ _, status = Process.waitpid2(pid)
165
+ expect(status.success?).to be(true)
166
+ end
167
+ end
168
+ # rubocop:enable Metrics/BlockLength
169
+ end
170
+ end
171
+
172
+ example 'ttyname' do
173
+ expect(@fh).to respond_to(:ttyname)
174
+ expect(@fh.ttyname).to be_nil
175
+
176
+ skip 'skipping ttyname spec in CI environment' if ENV['CI']
177
+ expect($stdout.ttyname).to be_a(String)
178
+ end
179
+ end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,11 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-extra
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain:
11
10
  - |
@@ -35,22 +34,64 @@ cert_chain:
35
34
  ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
36
35
  WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
37
36
  -----END CERTIFICATE-----
38
- date:
37
+ date: 1980-01-02 00:00:00.000000000 Z
39
38
  dependencies:
40
39
  - !ruby/object:Gem::Dependency
41
- name: test-unit
40
+ name: rake
41
+ requirement: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
42
55
  requirement: !ruby/object:Gem::Requirement
43
56
  requirements:
44
57
  - - "~>"
45
58
  - !ruby/object:Gem::Version
46
- version: '3.0'
59
+ version: '3.9'
47
60
  type: :development
48
61
  prerelease: false
49
62
  version_requirements: !ruby/object:Gem::Requirement
50
63
  requirements:
51
64
  - - "~>"
52
65
  - !ruby/object:Gem::Version
53
- version: '3.0'
66
+ version: '3.9'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rubocop
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: rubocop-rspec
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
54
95
  description: |2
55
96
  Adds the IO.closefrom, IO.fdwalk, IO.pread, IO.pwrite, and IO.writev
56
97
  singleton methods as well as the IO#directio, IO#directio? and IO#ttyname
@@ -60,23 +101,42 @@ executables: []
60
101
  extensions:
61
102
  - ext/extconf.rb
62
103
  extra_rdoc_files:
63
- - CHANGES
64
- - README
65
- - MANIFEST
104
+ - CHANGES.md
105
+ - MANIFEST.md
106
+ - README.md
66
107
  - ext/io/extra.c
67
108
  files:
109
+ - CHANGES.md
110
+ - Gemfile
111
+ - LICENSE
112
+ - MANIFEST.md
113
+ - README.md
114
+ - Rakefile
115
+ - certs/djberg96_pub.pem
116
+ - doc/io_extra.txt
117
+ - examples/example_io_extra.rb
118
+ - examples/example_pread.rb
119
+ - examples/writev_benchmark.rb
120
+ - ext/extconf.rb
121
+ - ext/extra.bundle.dSYM/Contents/Info.plist
122
+ - ext/extra.bundle.dSYM/Contents/Resources/Relocations/aarch64/extra.bundle.yml
123
+ - ext/io/extra.c
124
+ - io-extra.gemspec
68
125
  - lib/io-extra.rb
126
+ - spec/io_extra_spec.rb
69
127
  homepage: https://github.com/djberg96/io-extra
70
128
  licenses:
71
129
  - Apache-2.0
72
130
  metadata:
73
131
  homepage_uri: https://github.com/djberg96/io-extra
74
132
  bug_tracker_uri: https://github.com/djberg96/io-extra/issues
75
- changelog_uri: https://github.com/djberg96/io-extra/blob/master/CHANGES
133
+ changelog_uri: https://github.com/djberg96/io-extra/blob/main/CHANGES.md
76
134
  documentation_uri: https://github.com/djberg96/io-extra/wiki
77
135
  source_code_uri: https://github.com/djberg96/io-extra
78
136
  wiki_uri: https://github.com/djberg96/io-extra/wiki
79
- post_install_message:
137
+ rubygems_mfa_required: 'true'
138
+ github_repo: https://github.com/djberg96/io-extra
139
+ funding_uri: https://github.com/sponsors/djberg96
80
140
  rdoc_options: []
81
141
  require_paths:
82
142
  - lib
@@ -84,16 +144,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
84
144
  requirements:
85
145
  - - ">="
86
146
  - !ruby/object:Gem::Version
87
- version: 2.5.0
147
+ version: '0'
88
148
  required_rubygems_version: !ruby/object:Gem::Requirement
89
149
  requirements:
90
150
  - - ">="
91
151
  - !ruby/object:Gem::Version
92
152
  version: '0'
93
153
  requirements: []
94
- rubygems_version: 3.0.6
95
- signing_key:
154
+ rubygems_version: 3.6.9
96
155
  specification_version: 4
97
156
  summary: Adds extra methods to the IO class
98
157
  test_files:
99
- - test/test_io_extra.rb
158
+ - spec/io_extra_spec.rb
metadata.gz.sig CHANGED
Binary file
data/README DELETED
@@ -1,97 +0,0 @@
1
- = Description
2
- The io-extra library provides a few extra IO methods that you may find
3
- handy. They are IO.closefrom, IO.fdwalk, IO.pread, IO.pread_ptr, IO.pwrite,
4
- IO.writev, IO#directio? and IO#directio=.
5
-
6
- This library is not supported on MS Windows.
7
-
8
- Support for OS X is limited. See below for details.
9
-
10
- = Installation
11
- gem install io-extra
12
-
13
- = Synopsis
14
- require 'io-extra' # Do not use 'io/extra'
15
-
16
- # Close all file descriptors from 3 up.
17
- IO.closefrom(3)
18
-
19
- # Inspect all the open handles
20
- IO.fdwalk(0){ |handle|
21
- puts "=" * 40
22
- p handle
23
- p handle.fileno
24
- }
25
-
26
- # Write an array to an IO object efficiently
27
- IO.writev(fh.fileno, %w[a b c])
28
-
29
- = Developer's Notes
30
- The "require 'io-extra'" is preferred over 'io/extra' because this is a mix
31
- of pure Ruby and C extension. The former require's the latter, so that way
32
- you get all the methods and constants that you expect.
33
-
34
- You might be wondering what the difference is between my implementation of
35
- IO.closefrom and a pure Ruby version that looks something like this:
36
-
37
- def IO.closefrom(n)
38
- 0.upto(n) do |fd|
39
- IO.for_fd(fd).close
40
- end
41
- end
42
-
43
- The primary difference is that this walks all file descriptors, rather
44
- than only open file descriptors. However, I should note that this only
45
- applies if your platform supports the closefrom() function. In that case,
46
- the only advantage is speed.
47
-
48
- You might also be wondering what the difference is between my implementation
49
- of IO.fdwalk and a pure Ruby version that looks something like this:
50
-
51
- def IO.fdwalk(n)
52
- ObjectSpace.each_object(File){ |f|
53
- yield f if f.fileno >= n
54
- }
55
- end
56
-
57
- The primary difference is that this only closes Ruby file objects, not
58
- necessarily every file handle opened by the Ruby process. For example, handles
59
- opened via system() calls.
60
-
61
- = Note to OS X Users
62
- The OS X platform does not support closefrom() or fdwalk(). The hand-
63
- crafted IO.closefrom function will not work because the getrlimit()
64
- function on OS X does not work. Patches welcome.
65
-
66
- = Documentation
67
- For further documentation, see the io_extra.txt file or the inline
68
- documentation that was generated by RDoc (if you did a gem install).
69
-
70
- = Known Issues
71
- The IO.writev tests fail on Solaris. Short test scripts seem to work,
72
- however. We're not sure what the issue is yet. Help wanted.
73
-
74
- Update: As of 2018 Solaris is basically dead, so I'm not going to worry
75
- about supporting Solaris unless there's an outcry.
76
-
77
- Please file any bug reports on the project page at
78
- https://github.com/djberg96/io-extra
79
-
80
- = Acknowledgements
81
- Eric Wong for some great work on Linux compatibility and other fixes, as
82
- well as the code for the IO.writev method.
83
-
84
- = License
85
- Apache-2.0
86
-
87
- = Copyright
88
- (C) 2003-2020 Daniel J. Berger
89
- All Rights Reserved
90
-
91
- = Warranty
92
- This package is provided "as is" and without any express or
93
- implied warranties, including, without limitation, the implied
94
- warranties of merchantability and fitness for a particular purpose.
95
-
96
- = Author
97
- Daniel J. Berger
@@ -1,148 +0,0 @@
1
- ###########################################################################
2
- # test_io_extra.rb
3
- #
4
- # Test suite for the io-extra library. This test should be run via the
5
- # 'rake test' task.
6
- ###########################################################################
7
- require 'test-unit'
8
- require 'rbconfig'
9
- require 'io/nonblock'
10
- require 'io-extra'
11
-
12
- class TC_IO_Extra < Test::Unit::TestCase
13
- def setup
14
- @file = 'delete_this.txt'
15
- @fh = File.open(@file, 'w+')
16
- @fh.puts "The quick brown fox jumped over the lazy dog's back"
17
- end
18
-
19
- def test_version
20
- assert_equal('1.4.0', IO::EXTRA_VERSION)
21
- assert_true(IO::EXTRA_VERSION.frozen?)
22
- end
23
-
24
- def test_open_direct
25
- omit_unless(RbConfig::CONFIG['host_os'] =~ /linux/i, 'Linux-only')
26
- assert_nothing_raised do
27
- fh = File.open(@fh.path, IO::RDWR|IO::DIRECT)
28
- fh.close
29
- end
30
- end
31
-
32
- def test_directio
33
- assert_respond_to(@fh, :directio?)
34
- assert_nothing_raised{ @fh.directio? }
35
- end
36
-
37
- def test_directio_set
38
- assert_respond_to(@fh, :directio=)
39
- assert_raises(StandardError){ @fh.directio = 99 }
40
- assert_nothing_raised{ @fh.directio = IO::DIRECTIO_ON }
41
- end
42
-
43
- def test_constants
44
- assert_not_nil(IO::DIRECTIO_ON)
45
- assert_not_nil(IO::DIRECTIO_OFF)
46
- end
47
-
48
- def test_IOV_MAX_constant
49
- assert_kind_of(Integer, IO::IOV_MAX)
50
- end
51
-
52
- def test_fdwalk
53
- omit_if(RbConfig::CONFIG['host_os'] =~ /darwin/i, 'unsupported')
54
- assert_respond_to(IO, :fdwalk)
55
- assert_nothing_raised{ IO.fdwalk(0){ } }
56
- end
57
-
58
- def test_fdwalk_honors_lowfd
59
- omit_if(RbConfig::CONFIG['host_os'] =~ /darwin/i, 'unsupported')
60
- IO.fdwalk(1){ |f| assert_true(f.fileno >= 1) }
61
- end
62
-
63
- def test_closefrom
64
- assert_respond_to(IO, :closefrom)
65
- assert_nothing_raised{ IO.closefrom(3) }
66
- end
67
-
68
- def test_pread
69
- @fh.close rescue nil
70
- @fh = File.open(@file)
71
- assert_respond_to(IO, :pread)
72
- assert_equal("quick", IO.pread(@fh, 5, 4))
73
- end
74
-
75
- def test_pread_binary
76
- @fh.close rescue nil
77
- @fh = File.open(@file, "ab")
78
- @fh.binmode
79
- size = @fh.stat.size
80
- assert_nothing_raised { @fh.syswrite("FOO\0HELLO") }
81
- @fh.close rescue nil
82
- @fh = File.open(@file)
83
- assert_equal("O\0H", IO.pread(@fh, 3, size + 2))
84
- end
85
-
86
- def test_pread_last
87
- @fh.close rescue nil
88
- @fh = File.open(@file)
89
- size = @fh.stat.size
90
- assert_equal("ck\n", IO.pread(@fh, 5, size - 3))
91
- end
92
-
93
- def test_pwrite
94
- assert_respond_to(IO, :pwrite)
95
- assert_nothing_raised{ IO.pwrite(@fh, "HAL", 0) }
96
- end
97
-
98
- def test_writev
99
- assert_respond_to(IO, :writev)
100
- assert_equal(10, IO.writev(@fh, %w[hello world]))
101
- end
102
-
103
- def test_writev_retry
104
- empty = ""
105
- if empty.respond_to?(:force_encoding)
106
- empty.force_encoding(Encoding::BINARY)
107
- end
108
-
109
- # bs * count should be > PIPE_BUF
110
- [ true, false ].each do |nonblock|
111
- [ [ 512, 512 ], [ 131073, 3 ], [ 4098, 64 ] ].each do |(bs,count)|
112
- rd, wr = IO.pipe
113
- wr.nonblock = nonblock
114
- buf = File.open("/dev/urandom", "rb") { |fp| fp.sysread(bs) }
115
- vec = (1..count).map { buf }
116
- pid = fork do
117
- wr.close
118
- tmp = []
119
- sleep 0.1
120
- begin
121
- tmp << rd.readpartial(8192, buf)
122
- rescue EOFError
123
- break
124
- end while true
125
- ok = (vec.join(empty) == tmp.join(empty))
126
- exit! ok
127
- end
128
- assert_nothing_raised { rd.close }
129
- assert_equal(bs * count, IO.writev(wr.fileno, vec))
130
- assert_nothing_raised { wr.close }
131
- _, status = Process.waitpid2(pid)
132
- assert status.success?
133
- end
134
- end
135
- end
136
-
137
- def test_ttyname
138
- assert_respond_to(@fh, :ttyname)
139
- assert_nil(@fh.ttyname)
140
- assert_kind_of(String, STDOUT.ttyname)
141
- end
142
-
143
- def teardown
144
- @fh.close rescue nil
145
- @fh = nil
146
- File.delete(@file) if File.exist?(@file)
147
- end
148
- end