io-like 0.3.1 → 0.4.0.pre1

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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -1
  3. data/NEWS.md +14 -1
  4. data/README.md +75 -94
  5. data/lib/io/like.rb +1916 -1314
  6. data/lib/io/like_helpers/abstract_io.rb +512 -0
  7. data/lib/io/like_helpers/blocking_io.rb +86 -0
  8. data/lib/io/like_helpers/buffered_io.rb +555 -0
  9. data/lib/io/like_helpers/character_io/basic_reader.rb +122 -0
  10. data/lib/io/like_helpers/character_io/converter_reader.rb +252 -0
  11. data/lib/io/like_helpers/character_io.rb +529 -0
  12. data/lib/io/like_helpers/delegated_io.rb +250 -0
  13. data/lib/io/like_helpers/duplexed_io.rb +259 -0
  14. data/lib/io/like_helpers/io.rb +21 -0
  15. data/lib/io/like_helpers/io_wrapper.rb +290 -0
  16. data/lib/io/like_helpers/pipeline.rb +77 -0
  17. data/lib/io/like_helpers/ruby_facts.rb +33 -0
  18. data/lib/io/like_helpers.rb +14 -0
  19. metadata +107 -224
  20. data/.yardopts +0 -1
  21. data/Rakefile +0 -228
  22. data/ruby.1.8.mspec +0 -7
  23. data/spec/binmode_spec.rb +0 -29
  24. data/spec/close_read_spec.rb +0 -64
  25. data/spec/close_spec.rb +0 -36
  26. data/spec/close_write_spec.rb +0 -61
  27. data/spec/closed_spec.rb +0 -16
  28. data/spec/each_byte_spec.rb +0 -38
  29. data/spec/each_line_spec.rb +0 -11
  30. data/spec/each_spec.rb +0 -11
  31. data/spec/eof_spec.rb +0 -11
  32. data/spec/fixtures/classes.rb +0 -96
  33. data/spec/fixtures/gets.txt +0 -9
  34. data/spec/fixtures/numbered_lines.txt +0 -5
  35. data/spec/fixtures/one_byte.txt +0 -1
  36. data/spec/fixtures/paragraphs.txt +0 -7
  37. data/spec/fixtures/readlines.txt +0 -6
  38. data/spec/flush_spec.rb +0 -8
  39. data/spec/getc_spec.rb +0 -44
  40. data/spec/gets_spec.rb +0 -212
  41. data/spec/isatty_spec.rb +0 -6
  42. data/spec/lineno_spec.rb +0 -84
  43. data/spec/output_spec.rb +0 -47
  44. data/spec/pos_spec.rb +0 -53
  45. data/spec/print_spec.rb +0 -97
  46. data/spec/printf_spec.rb +0 -24
  47. data/spec/putc_spec.rb +0 -57
  48. data/spec/puts_spec.rb +0 -99
  49. data/spec/read_spec.rb +0 -162
  50. data/spec/readchar_spec.rb +0 -49
  51. data/spec/readline_spec.rb +0 -60
  52. data/spec/readlines_spec.rb +0 -140
  53. data/spec/readpartial_spec.rb +0 -92
  54. data/spec/rewind_spec.rb +0 -56
  55. data/spec/seek_spec.rb +0 -72
  56. data/spec/shared/each.rb +0 -204
  57. data/spec/shared/eof.rb +0 -116
  58. data/spec/shared/pos.rb +0 -39
  59. data/spec/shared/tty.rb +0 -12
  60. data/spec/shared/write.rb +0 -53
  61. data/spec/sync_spec.rb +0 -56
  62. data/spec/sysread_spec.rb +0 -87
  63. data/spec/sysseek_spec.rb +0 -68
  64. data/spec/syswrite_spec.rb +0 -60
  65. data/spec/tell_spec.rb +0 -7
  66. data/spec/to_io_spec.rb +0 -19
  67. data/spec/tty_spec.rb +0 -6
  68. data/spec/ungetc_spec.rb +0 -118
  69. data/spec/write_spec.rb +0 -61
  70. data/spec_helper.rb +0 -49
  71. /data/{LICENSE-rubyspec → rubyspec/LICENSE} +0 -0
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/like_helpers/abstract_io'
4
+ require 'io/like_helpers/ruby_facts.rb'
5
+
6
+ class IO; module LikeHelpers
7
+
8
+ ##
9
+ # This class implements {AbstractIO} by delegating most methods to a delegate
10
+ # stream. Use this class to implement streams that filter or mutate data sent
11
+ # through them.
12
+ class DelegatedIO < AbstractIO
13
+ ##
14
+ # Defines methods for instances of this class that delegate calls to another
15
+ # object.
16
+ #
17
+ # The delegation first calls an assert method to ensure the stream is in the
18
+ # nessary state to be able to perform the delegation.
19
+ #
20
+ # @param methods [Array<Symbol>] a list of methods to delegate
21
+ # @param to [Symbol] the target object
22
+ # @param assert [Symbol] the kind of assertion to call (`:open`, `:readable`,
23
+ # or `:writable`)
24
+ #
25
+ # @return [Array<Symbol>] the names of the defined methods
26
+ private_class_method def self.delegate(*methods, to: :delegate, assert: :open)
27
+ unless %i{open readable writable}.include?(assert)
28
+ raise ArgumentError, "Invalid assert: #{assert}"
29
+ end
30
+
31
+ location = caller_locations(1, 1).first
32
+ file, line = location.path, location.lineno
33
+
34
+ methods.map do |method|
35
+ args = if /[^\]]=$/.match?(method)
36
+ 'arg'
37
+ else
38
+ '*args, **kwargs, &b'
39
+ end
40
+
41
+ method_def = <<-EOM
42
+ def #{method}(#{args})
43
+ assert_#{assert}
44
+ #{to}.#{method}(#{args})
45
+ end
46
+ EOM
47
+ module_eval(method_def, file, line)
48
+ end
49
+ end
50
+
51
+ ##
52
+ # @param delegate [LikeHelpers::AbstractIO] a readable and/or writable stream
53
+ #
54
+ # @return [Proc] a proc to be used as a fializer that calls #close on
55
+ # `delegate` when an instance of this class it garbage collected
56
+ def self.create_finalizer(delegate)
57
+ proc { |id| delegate.close }
58
+ end
59
+
60
+ ##
61
+ # Creates a new intance of this class.
62
+ #
63
+ # @param delegate [LikeHelpers::AbstractIO] a readable and/or writable stream
64
+ # @param autoclose [Boolean] when `true` close the delegate when this stream
65
+ # is closed
66
+ def initialize(delegate, autoclose: true)
67
+ raise ArgumentError, 'delegate cannot be nil' if delegate.nil?
68
+ super()
69
+
70
+ @delegate = delegate
71
+ self.autoclose = autoclose
72
+ end
73
+
74
+ ##
75
+ # Sets whether or not to close the delegate when {#close} is called.
76
+ #
77
+ # @param autoclose [Boolean] delegate will be closed when `true`
78
+ def autoclose=(autoclose)
79
+ assert_open
80
+ @autoclose = autoclose ? true : false
81
+ @autoclose ? enable_finalizer : disable_finalizer
82
+ autoclose
83
+ end
84
+
85
+ ##
86
+ # Returns `true` if the delegate would be closed when {#close} is called
87
+ # and `false` otherwise.
88
+ #
89
+ # @return [Boolean]
90
+ def autoclose?
91
+ assert_open
92
+ @autoclose
93
+ end
94
+
95
+ ##
96
+ # Closes this stream.
97
+ #
98
+ # The delegate is closed if autoclose is enabled for the stream.
99
+ #
100
+ # @return [nil] on success
101
+ # @return [:wait_readable, :wait_writable] if the stream is non-blocking and
102
+ # the operation would block
103
+ def close
104
+ return nil if closed?
105
+
106
+ begin
107
+ result = delegate.close if @autoclose
108
+ ensure
109
+ # Complete the closing process if the delegate closed normally or an
110
+ # exception was raised.
111
+ unless Symbol === result
112
+ disable_finalizer
113
+ result = super
114
+ end
115
+ end
116
+
117
+ result
118
+ end
119
+
120
+ ##
121
+ # @return [String] a string representation of this object
122
+ def inspect
123
+ "<#{self.class}:#{delegate.inspect}#{' (closed)' if closed?}>"
124
+ end
125
+
126
+ ##
127
+ # Returns `true` if the stream is readable and `false` otherwise.
128
+ #
129
+ # @return [Boolean]
130
+ def readable?
131
+ return false if closed?
132
+ return @readable if defined?(@readable) && ! @readable.nil?
133
+ @readable = delegate.readable?
134
+ end
135
+
136
+ ##
137
+ # Returns `true` if the stream is writable and `false` otherwise.
138
+ #
139
+ # @return [Boolean]
140
+ def writable?
141
+ return false if closed?
142
+ return @writable if defined?(@writable) && ! @writable.nil?
143
+ @writable = delegate.writable?
144
+ end
145
+
146
+ ##
147
+ # @method close_on_exec=(value)
148
+ # Calls `delegate.close_on_exec = value` after asserting that the stream is
149
+ # open.
150
+ delegate :close_on_exec=
151
+
152
+ ##
153
+ # @method nonblock=(value)
154
+ # Calls `delegate.nonblock = value` after asserting that the stream is open.
155
+ delegate :nonblock=
156
+
157
+ # @!macro [attach] delegate_open
158
+ # @method $1(*args, **kwargs, &b)
159
+ # Calls `delegate.$1(*args, **kwargs, &b)` after asserting that the stream is open.
160
+ delegate :advise
161
+ delegate :close_on_exec?
162
+ delegate :fcntl
163
+ delegate :fdatasync
164
+ delegate :fileno
165
+ delegate :fsync
166
+ delegate :ioctl
167
+ delegate :nonblock?
168
+ delegate :path
169
+ delegate :pid
170
+ delegate :ready?
171
+ delegate :seek
172
+ delegate :stat
173
+ delegate :to_io
174
+ delegate :tty?
175
+ delegate :wait
176
+
177
+ ##
178
+ # @method pread(*args, **kwargs, &b)
179
+ # Calls `delegate.read(*args, **kwargs, &b)` after asserting that the stream is readable.
180
+ delegate :pread, assert: :readable
181
+
182
+ ##
183
+ # @method read(*args, **kwargs, &b)
184
+ # Calls `delegate.read(*args, **kwargs, &b)` after asserting that the stream is readable.
185
+ delegate :read, assert: :readable
186
+
187
+ ##
188
+ # @method nread(*args, **kwargs, &b)
189
+ # Calls `delegate.nread(*args, **kwargs, &b)` after asserting that the stream is readable.
190
+ delegate :nread, assert: :readable
191
+
192
+ ##
193
+ # @method pwrite(*args, **kwargs, &b)
194
+ # Calls `delegate.write(*args, **kwargs, &b)` after asserting that the stream is writable.
195
+ delegate :pwrite, assert: :writable
196
+
197
+ ##
198
+ # @method write(*args, **kwargs, &b)
199
+ # Calls `delegate.write(*args, **kwargs, &b)` after asserting that the stream is writable.
200
+ delegate :write, assert: :writable
201
+
202
+ protected
203
+
204
+ ##
205
+ # The delegate that receives delegated method calls.
206
+ attr_reader :delegate
207
+
208
+ private
209
+
210
+ ##
211
+ # Removes all finalizers for this object.
212
+ #
213
+ # @return [nil]
214
+ def disable_finalizer
215
+ ObjectSpace.undefine_finalizer(self)
216
+ nil
217
+ end
218
+
219
+ ##
220
+ # Defines a finalizer for this object.
221
+ #
222
+ # @return [nil]
223
+ def enable_finalizer
224
+ ObjectSpace.define_finalizer(self, self.class.create_finalizer(delegate))
225
+ nil
226
+ end
227
+
228
+ ##
229
+ # Creates an instance of this class that copies state from `other`.
230
+ #
231
+ # The delegate of `other` is `dup`'d.
232
+ #
233
+ # @param other [DelegatedIO] the instance to copy
234
+ #
235
+ # @return [nil]
236
+ #
237
+ # @raise [IOError] if `other` is closed
238
+ def initialize_copy(other)
239
+ super
240
+
241
+ disable_finalizer
242
+ @delegate = @delegate.dup
243
+ self.autoclose = true
244
+
245
+ nil
246
+ end
247
+ end
248
+ end; end
249
+
250
+ # vim: ts=2 sw=2 et
@@ -0,0 +1,259 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/like_helpers/delegated_io'
4
+
5
+ class IO; module LikeHelpers
6
+
7
+ ##
8
+ # This class encapsulates 2 streams (one readable, one writable) into a single
9
+ # stream. It is primarily intended to serve as an ancestor for {IO::Like} and
10
+ # should not be used directly.
11
+ class DuplexedIO < DelegatedIO
12
+ ##
13
+ # Creates a new intance of this class.
14
+ #
15
+ # @param delegate_r [LikeHelpers::AbstractIO] a readable stream
16
+ # @param delegate_w [LikeHelpers::AbstractIO] a writable stream
17
+ # @param autoclose [Boolean] when `true` close the delegate when this stream
18
+ # is closed
19
+ def initialize(delegate_r, delegate_w = delegate_r, autoclose: true)
20
+ raise ArgumentError, 'delegate_r cannot be nil' if delegate_r.nil?
21
+ raise ArgumentError, 'delegate_w cannot be nil' if delegate_w.nil?
22
+
23
+ @delegate_w = delegate_w
24
+ @closed_write = false
25
+
26
+ super(delegate_r, autoclose: autoclose)
27
+ end
28
+
29
+ ##
30
+ # Closes this stream.
31
+ #
32
+ # The delegates are closed if autoclose is enabled for the stream.
33
+ #
34
+ # @return [nil] on success
35
+ # @return [:wait_readable, :wait_writable] if the stream is non-blocking and
36
+ # the operation would block
37
+ def close
38
+ return nil if closed?
39
+
40
+ begin
41
+ result = close_write
42
+ ensure
43
+ # Complete the closing process if the writable delegate closed normally or
44
+ # an exception was raised.
45
+ result = close_read unless Symbol === result
46
+ end
47
+
48
+ result
49
+ end
50
+
51
+ ##
52
+ # Returns `true` if the readable delegate is closed and `false` otherwise.
53
+ #
54
+ # @return [Boolean]
55
+ alias_method :closed_read?, :closed?
56
+
57
+ ##
58
+ # Returns `true` if both delegates are closed and `false` otherwise.
59
+ #
60
+ # @return [Boolean]
61
+ def closed?
62
+ closed_read? && closed_write?
63
+ end
64
+
65
+ ##
66
+ # Returns `true` if the writable delegate is closed and `false` otherwise.
67
+ #
68
+ # @return [Boolean]
69
+ def closed_write?
70
+ @closed_write
71
+ end
72
+
73
+ ##
74
+ # Closes the readable delegate.
75
+ #
76
+ # @return [nil] on success
77
+ # @return [:wait_readable, :wait_writable] if the stream is non-blocking and
78
+ # the operation would block
79
+ def close_read
80
+ return nil if closed_read?
81
+
82
+ begin
83
+ result = delegate_r.close if @autoclose
84
+ ensure
85
+ # Complete the closing process if the delegate closed normally or an
86
+ # exception was raised.
87
+ unless Symbol === result
88
+ @closed_write = true unless duplexed?
89
+ @closed = true
90
+ @delegate = @delegate_w
91
+ disable_finalizer
92
+ enable_finalizer if @autoclose && ! closed?
93
+ end
94
+ end
95
+
96
+ result
97
+ end
98
+
99
+ ##
100
+ # Closes the writable delegate.
101
+ #
102
+ # @return [nil] on success
103
+ # @return [:wait_readable, :wait_writable] if the stream is non-blocking and
104
+ # the operation would block
105
+ def close_write
106
+ return nil if closed_write?
107
+
108
+ begin
109
+ result = delegate_w.close if @autoclose
110
+ ensure
111
+ # Complete the closing process if the delegate closed normally or an
112
+ # exception was raised.
113
+ unless Symbol === result
114
+ @closed = true unless duplexed?
115
+ @closed_write = true
116
+ @delegate_w = @delegate
117
+ disable_finalizer
118
+ enable_finalizer if @autoclose && ! closed?
119
+ end
120
+ end
121
+
122
+ result
123
+ end
124
+
125
+ ##
126
+ # Sets the close-on-exec flag for the underlying file descriptors of the
127
+ # delegates.
128
+ #
129
+ # Note that setting this to `false` can lead to file descriptor leaks in
130
+ # multithreaded applications that fork and exec or use the `system` method.
131
+ #
132
+ # @return [Boolean]
133
+ def close_on_exec=(close_on_exec)
134
+ return super unless duplexed?
135
+
136
+ assert_open
137
+
138
+ delegate_w.close_on_exec = delegate_r.close_on_exec = close_on_exec
139
+
140
+ close_on_exec
141
+ end
142
+
143
+ ##
144
+ # @return [String] a string representation of this object
145
+ def inspect
146
+ return super unless duplexed?
147
+ "<#{self.class}:#{delegate_r.inspect}, #{delegate_w.inspect}>"
148
+ end
149
+
150
+ ##
151
+ # Sets the blocking mode for the stream.
152
+ #
153
+ # @return [Boolean]
154
+ def nonblock=(nonblock)
155
+ return super unless duplexed?
156
+
157
+ assert_open
158
+
159
+ delegate_w.nonblock = delegate_r.nonblock = nonblock
160
+
161
+ nonblock
162
+ end
163
+
164
+ ##
165
+ # @method pwrite(*args, **kwargs, &b)
166
+ # Calls `delegate_w.write(*args, **kwargs, &b)` after asserting that the stream is writable.
167
+ delegate :pwrite, to: :delegate_w, assert: :writable
168
+
169
+ ##
170
+ # Returns `true` if the stream is readable and `false` otherwise.
171
+ #
172
+ # @return [Boolean]
173
+ def readable?
174
+ return false if closed_read?
175
+ return @readable if defined?(@readable) && ! @readable.nil?
176
+ @readable = delegate_r.readable?
177
+ end
178
+
179
+ ##
180
+ # @method write(*args, **kwargs, &b)
181
+ # Calls `delegate_w.write(*args, **kwargs, &b)` after asserting that the stream is writable.
182
+ delegate :write, to: :delegate_w, assert: :writable
183
+
184
+ ##
185
+ # Returns `true` if the stream is writable and `false` otherwise.
186
+ #
187
+ # @return [Boolean]
188
+ def writable?
189
+ return false if closed_write?
190
+ return @writable if defined?(@writable) && ! @writable.nil?
191
+ @writable = delegate_w.writable?
192
+ end
193
+
194
+ protected
195
+
196
+ ##
197
+ # Returns `true` if the stream is duplexed and `false` otherwise.
198
+ #
199
+ # Note that a duplexed stream can become non-duplexed if one of the delegates
200
+ # is closed via {#close_read} or {#close_write}.
201
+ #
202
+ # @return [Boolean]
203
+ def duplexed?
204
+ delegate_r != delegate_w
205
+ end
206
+
207
+ private
208
+
209
+ ##
210
+ # Defines a finalizer for this object.
211
+ #
212
+ # @return [nil]
213
+ def enable_finalizer
214
+ if duplexed?
215
+ ObjectSpace.define_finalizer(
216
+ self, self.class.create_finalizer(delegate_w)
217
+ )
218
+ end
219
+ super
220
+ end
221
+
222
+ ##
223
+ # Creates an instance of this class that copies state from `other`.
224
+ #
225
+ # The delegates of `other` are `dup`'d.
226
+ #
227
+ # @param other [DuplexedIO] the instance to copy
228
+ #
229
+ # @return [nil]
230
+ #
231
+ # @raise [IOError] if `other` is closed
232
+ def initialize_copy(other)
233
+ super
234
+
235
+ disable_finalizer
236
+ begin
237
+ @delegate_w = other.duplexed? ? @delegate_w.dup : @delegate
238
+ self.autoclose = true
239
+ rescue
240
+ delegate_r.close rescue nil
241
+ raise
242
+ end
243
+
244
+ nil
245
+ end
246
+
247
+ ##
248
+ # The writable delegate.
249
+ attr_reader :delegate_w
250
+
251
+ ##
252
+ # @!attribute [r] __ignore__
253
+ # @overload delegate_r
254
+ # The readable delegate. Aliases {#delegate}.
255
+ alias_method :delegate_r, :delegate
256
+ end
257
+ end; end
258
+
259
+ # vim: ts=2 sw=2 et
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IO
4
+ unless const_defined?(:PRIORITY)
5
+ ##
6
+ # Defined only if IO::PRIORITY is not defined.
7
+ PRIORITY = 2
8
+ end
9
+
10
+ unless const_defined?(:READABLE)
11
+ ##
12
+ # Defined only if IO::READABLE is not defined.
13
+ READABLE = 1
14
+ end
15
+
16
+ unless const_defined?(:WRITABLE)
17
+ ##
18
+ # Defined only if IO::WRITABLE is not defined.
19
+ WRITABLE = 4
20
+ end
21
+ end