io-like 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,8 @@
6
6
 
7
7
  = Bug Fixes
8
8
 
9
+ * Jarred Holman <jarred.holman at gmail dot com>
10
+
9
11
 
10
12
  = Testers
11
13
 
data/HACKING CHANGED
@@ -17,8 +17,8 @@ some cases, but such cases will be rare.
17
17
  === Build
18
18
 
19
19
  * rubygems 0.9.0 or greater
20
- * rake 0.8.1
21
- * rubyforge 1.0.0 (optional - used for publishing releases)
20
+ * rake 0.8.3 or greater
21
+ * mspec 1.5.9 (optional - used for testing)
22
22
  * allison 2.0.3 (optional - used for documentation only, if available)
23
23
  * rsync (optional - used for publishing documentation)
24
24
 
data/LEGAL CHANGED
@@ -5,4 +5,52 @@
5
5
  The following file(s) are provided under a license or licenses separate from
6
6
  this project.
7
7
 
8
- None at present
8
+ See LICENSE.rubyspec for terms of use for the following:
9
+ spec/binmode_spec.rb
10
+ spec/close_read_spec.rb
11
+ spec/close_spec.rb
12
+ spec/close_write_spec.rb
13
+ spec/closed_spec.rb
14
+ spec/each_byte_spec.rb
15
+ spec/each_line_spec.rb
16
+ spec/each_spec.rb
17
+ spec/eof_spec.rb
18
+ spec/fixtures/classes.rb
19
+ spec/fixtures/gets.txt
20
+ spec/fixtures/numbered_lines.txt
21
+ spec/fixtures/one_byte.txt
22
+ spec/fixtures/paragraphs.txt
23
+ spec/fixtures/readlines.txt
24
+ spec/flush_spec.rb
25
+ spec/getc_spec.rb
26
+ spec/gets_spec.rb
27
+ spec/isatty_spec.rb
28
+ spec/lineno_spec.rb
29
+ spec/output_spec.rb
30
+ spec/pos_spec.rb
31
+ spec/print_spec.rb
32
+ spec/printf_spec.rb
33
+ spec/putc_spec.rb
34
+ spec/puts_spec.rb
35
+ spec/read_spec.rb
36
+ spec/readchar_spec.rb
37
+ spec/readline_spec.rb
38
+ spec/readlines_spec.rb
39
+ spec/readpartial_spec.rb
40
+ spec/rewind_spec.rb
41
+ spec/seek_spec.rb
42
+ spec/shared/each.rb
43
+ spec/shared/eof.rb
44
+ spec/shared/pos.rb
45
+ spec/shared/tty.rb
46
+ spec/shared/write.rb
47
+ spec/sync_spec.rb
48
+ spec/sysread_spec.rb
49
+ spec/sysseek_spec.rb
50
+ spec/syswrite_spec.rb
51
+ spec/tell_spec.rb
52
+ spec/to_io_spec.rb
53
+ spec/tty_spec.rb
54
+ spec/ungetc_spec.rb
55
+ spec/write_spec.rb
56
+ spec_helper.rb
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2008 Engine Yard, Inc. All rights reserved.
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/MANIFEST CHANGED
@@ -3,8 +3,8 @@ GPL
3
3
  HACKING
4
4
  LEGAL
5
5
  LICENSE
6
+ LICENSE.rubyspec
6
7
  MANIFEST
7
8
  NEWS
8
9
  README
9
10
  lib/io/like.rb
10
- test/lib/likestringio.rb
data/NEWS CHANGED
@@ -6,6 +6,13 @@ detailed information is available in the rest of the documentation.
6
6
  <b>NOTE:</b> Date stamps in the following entries are in YYYY/MM/DD format.
7
7
 
8
8
 
9
+ == v0.2.0 (2009/03/11)
10
+
11
+ * Added mspec tests borrowed from the rubyspec project
12
+ * Fixed many, many defects related to IO compatibility (Mostly obscure corner
13
+ cases)
14
+
15
+
9
16
  == v0.1.0 (2008/07/03)
10
17
 
11
18
  * Initial release
data/README CHANGED
@@ -12,11 +12,14 @@ methods.
12
12
 
13
13
  == License
14
14
 
15
- Copyright © 2008 Jeremy Bopp <jeremy at bopp dot net>
15
+ Copyright © 2008,2009 Jeremy Bopp <jeremy at bopp dot net>
16
16
 
17
17
  Licensed under the same terms as Ruby -- See the included LICENSE file for
18
18
  details
19
19
 
20
+ Some parts licensed under the same terms as the rubyspec project -- See the
21
+ included LEGAL and LICENSE.rubyspec files for details
22
+
20
23
 
21
24
  == Installation/Removal
22
25
 
@@ -125,8 +128,6 @@ A simple ROT13 codec:
125
128
  collected. Define a class open method in the manner of File.open which
126
129
  guarantees that an appropriate close method will be called after executing a
127
130
  block. Other than that, be diligent about calling the close methods.
128
- 3. Testcases needed. Maybe use some of rubyspec along with some test classes
129
- act like the important parts of IO, File, and/or StringIO.
130
131
 
131
132
 
132
133
  == Contributing
@@ -100,77 +100,65 @@ class IO # :nodoc:
100
100
  # returns +true+ and then sets a flag so that #closed? will return +true+.
101
101
  def close
102
102
  raise IOError, 'closed stream' if closed?
103
- if duplexed? then
104
- close_read unless closed_read?
105
- close_write unless closed_write?
106
- else
107
- flush if writable?
108
- @__io_like__closed = true
109
- end
103
+ @__io_like__closed_read = true
104
+ flush if writable?
105
+ @__io_like__closed_write = true
110
106
  nil
111
107
  end
112
108
 
113
109
  # call-seq:
114
110
  # ios.close_read -> nil
115
111
  #
116
- # For duplexed objects, arranges for #closed_read? to return +true+.
112
+ # Closes the read end of a duplexed object or the whole object if the object
113
+ # is read-only.
117
114
  #
118
- # Raises IOError if #duplexed returns +false+. Raises IOError if
119
- # #closed_read? returns +true+.
115
+ # Raises IOError if #closed? returns +true+. Raises IOError for duplexed
116
+ # objects if called more than once. Raises IOError for non-duplexed objects
117
+ # if #writable? returns +true+.
120
118
  def close_read
121
- raise IOError, 'closed stream' if closed_read?
122
- raise IOError, 'closing non-duplex IO for reading' unless duplexed?
123
- @__io_like__closed_read = true
119
+ raise IOError, 'closed stream' if closed?
120
+ if @__io_like__closed_read || ! duplexed? && writable? then
121
+ raise IOError, 'closing non-duplex IO for reading'
122
+ end
123
+ if duplexed? then
124
+ @__io_like__closed_read = true
125
+ else
126
+ close
127
+ end
124
128
  nil
125
129
  end
126
130
 
127
131
  # call-seq:
128
132
  # ios.close_write -> nil
129
133
  #
130
- # For duplexed objects, calls #flush and arranges for #closed_write? to
131
- # return +true+.
134
+ # Closes the write end of a duplexed object or the whole object if the
135
+ # object is write-only.
132
136
  #
133
- # Raises IOError if #duplexed? returns +false+. Raises IOError if
134
- # #closed_write? returns +true+.
137
+ # Raises IOError if #closed? returns +true+. Raises IOError for duplexed
138
+ # objects if called more than once. Raises IOError for non-duplexed objects
139
+ # if #readable? returns +true+.
135
140
  def close_write
136
- raise IOError, 'closed stream' if closed_write?
137
- raise IOError, 'closing non-duplex IO for writing' unless duplexed?
138
- flush
139
- @__io_like__closed_write = true
141
+ raise IOError, 'closed stream' if closed?
142
+ if @__io_like__closed_write || ! duplexed? && readable? then
143
+ raise IOError, 'closing non-duplex IO for reading'
144
+ end
145
+ if duplexed? then
146
+ flush
147
+ @__io_like__closed_write = true
148
+ else
149
+ close
150
+ end
140
151
  nil
141
152
  end
142
153
 
143
154
  # call-seq:
144
155
  # ios.closed? -> true or false
145
156
  #
146
- # For non-duplexed objects, returns +true+ if #close was called, +false+
147
- # otherwise. For duplexed objects, returns +true+ if both #closed_read?
148
- # and #closed_write? return true.
157
+ # Returns +true+ if this object is closed or otherwise unusable for read and
158
+ # write operations.
149
159
  def closed?
150
- return closed_read? && closed_write? if duplexed?
151
- @__io_like__closed || false
152
- end
153
-
154
- # call-seq:
155
- # ios.closed_read? -> true or false
156
- #
157
- # Returns the result of calling #closed? for non-duplexed objects. For
158
- # duplexed objects, returns +true+ if close_read was called, +false+
159
- # otherwise.
160
- def closed_read?
161
- return closed? unless duplexed?
162
- @__io_like__closed_read || false
163
- end
164
-
165
- # call-seq:
166
- # ios.closed_write? -> true or false
167
- #
168
- # Returns the result of calling #closed? for non-duplexed objects. For
169
- # duplexed objects, returns +true+ if close_write was called, +false+
170
- # otherwise.
171
- def closed_write?
172
- return closed? unless duplexed?
173
- @__io_like__closed_read || false
160
+ (@__io_like__closed_read || ! readable?) &&
161
+ (@__io_like__closed_write || ! writable?)
174
162
  end
175
163
 
176
164
  # call-seq:
@@ -183,12 +171,12 @@ class IO # :nodoc:
183
171
  end
184
172
 
185
173
  # call-seq:
186
- # ios.each_byte {|byte| block} -> ios
174
+ # ios.each_byte { |byte| block } -> ios
187
175
  #
188
176
  # Reads each byte (0..255) from the stream using #getc and calls the given
189
177
  # block once for each byte, passing the byte as an argument.
190
178
  #
191
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
179
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
192
180
  # #unbuffered_read. Therefore, this method always blocks. Aside from that
193
181
  # exception and the conversion of EOFError results into +nil+ results, this
194
182
  # method will also raise the same errors and block at the same times as
@@ -201,17 +189,17 @@ class IO # :nodoc:
201
189
  end
202
190
 
203
191
  # call-seq:
204
- # ios.each_line(sep_string = $/) {|line| block } -> ios
205
- # ios.each(sep_string = $/) {|line| block } -> ios
192
+ # ios.each_line(sep_string = $/) { |line| block } -> ios
193
+ # ios.each(sep_string = $/) { |line| block } -> ios
206
194
  #
207
195
  # Reads each line from the stream using #gets and calls the given block once
208
196
  # for each line, passing the line as an argument.
209
197
  #
210
- # NOTE: When _sep_string_ is not +nil+, this method ignores Errno::EAGAIN
211
- # and Errno::EINTR raised by #unbuffered_read. Therefore, this method
212
- # always blocks. Aside from that exception and the conversion of EOFError
213
- # results into +nil+ results, this method will also raise the same errors
214
- # and block at the same times as #unbuffered_read.
198
+ # <b>NOTE:</b> When _sep_string_ is not +nil+, this method ignores
199
+ # Errno::EAGAIN and Errno::EINTR raised by #unbuffered_read. Therefore,
200
+ # this method always blocks. Aside from that exception and the conversion
201
+ # of EOFError results into +nil+ results, this method will also raise the
202
+ # same errors and block at the same times as #unbuffered_read.
215
203
  def each_line(sep_string = $/)
216
204
  while (line = gets(sep_string)) do
217
205
  yield(line)
@@ -230,7 +218,7 @@ class IO # :nodoc:
230
218
  # put the character back if one was fetched. It may be a good idea to
231
219
  # replace this implementation in derivative classes.
232
220
  #
233
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
221
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
234
222
  # #unbuffered_read. Therefore, this method always blocks. Aside from that
235
223
  # exception and the conversion of EOFError results into +nil+ results, this
236
224
  # method will also raise the same errors and block at the same times as
@@ -247,7 +235,7 @@ class IO # :nodoc:
247
235
  # call-seq:
248
236
  # ios.fcntl
249
237
  #
250
- # Raises NotImplementedError
238
+ # Raises NotImplementedError.
251
239
  def fcntl(*args)
252
240
  raise NotImplementedError, 'not implemented'
253
241
  end
@@ -267,10 +255,10 @@ class IO # :nodoc:
267
255
  # buffer needs to be refilled. Unless set explicitly via #fill_size=, this
268
256
  # defaults to 4096.
269
257
  #
270
- # Raises IOError if #closed_read? returns +true+. Raises IOError if the
258
+ # Raises IOError if #closed? returns +true+. Raises IOError if the
271
259
  # stream is not opened for reading.
272
260
  def fill_size
273
- raise IOError, 'closed stream' if closed_read?
261
+ raise IOError, 'closed stream' if closed?
274
262
  raise IOError, 'not opened for reading' unless readable?
275
263
 
276
264
  @__io_like__fill_size ||= 4096
@@ -283,10 +271,10 @@ class IO # :nodoc:
283
271
  # buffer needs to be refilled. The new value must be a number greater than
284
272
  # or equal to 0. Setting this to 0 effectively disables buffering.
285
273
  #
286
- # Raises IOError if #closed_read? returns +true+. Raises IOError if the
274
+ # Raises IOError if #closed? returns +true+. Raises IOError if the
287
275
  # stream is not opened for reading.
288
276
  def fill_size=(fill_size)
289
- raise IOError, 'closed stream' if closed_read?
277
+ raise IOError, 'closed stream' if closed?
290
278
  raise IOError, 'not opened for reading' unless readable?
291
279
 
292
280
  unless fill_size >= 0 then
@@ -304,17 +292,15 @@ class IO # :nodoc:
304
292
  # during writing, this method will block until either all the data is
305
293
  # flushed or until an error is raised.
306
294
  #
307
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
295
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
308
296
  # #writable? returns +true+.
309
297
  #
310
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
298
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
311
299
  # #unbuffered_write. Therefore, this method always blocks if unable to
312
300
  # flush the internal write buffer. Aside from that exception, this
313
301
  # method will also raise the same errors and block at the same times as
314
302
  # #unbuffered_write.
315
303
  def flush
316
- raise IOError, 'closed stream' if closed_write?
317
-
318
304
  begin
319
305
  buffered_flush
320
306
  rescue Errno::EAGAIN, Errno::EINTR
@@ -330,10 +316,10 @@ class IO # :nodoc:
330
316
  # automatically to the data stream. Unless set explicitly via #flush_size=,
331
317
  # this defaults to 4096.
332
318
  #
333
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
319
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
334
320
  # #writable? returns +true+.
335
321
  def flush_size
336
- raise IOError, 'closed stream' if closed_write?
322
+ raise IOError, 'closed stream' if closed?
337
323
  raise IOError, 'not opened for writing' unless writable?
338
324
 
339
325
  @__io_like__flush_size ||= 4096
@@ -346,10 +332,10 @@ class IO # :nodoc:
346
332
  # automatically to the data stream. The new value must be a number greater
347
333
  # than or equal to 0. Setting this to 0 effectively disables buffering.
348
334
  #
349
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
335
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
350
336
  # #writable? returns +true+.
351
337
  def flush_size=(flush_size)
352
- raise IOError, 'closed stream' if closed_write?
338
+ raise IOError, 'closed stream' if closed?
353
339
  raise IOError, 'not opened for writing' unless writable?
354
340
 
355
341
  unless flush_size >= 0 then
@@ -364,11 +350,11 @@ class IO # :nodoc:
364
350
  # Calls #readchar and either returns the result or +nil+ if #readchar raises
365
351
  # EOFError.
366
352
  #
367
- # Raises IOError if #closed_read? returns +true+. Raises IOError unless
353
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
368
354
  # #readable? returns +true+. Raises all errors raised by #unbuffered_read
369
355
  # except for EOFError.
370
356
  #
371
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
357
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
372
358
  # #unbuffered_read. Therefore, this method always blocks. Aside from that
373
359
  # exception and the conversion of EOFError results into +nil+ results, this
374
360
  # method will also raise the same errors and block at the same times as
@@ -387,15 +373,15 @@ class IO # :nodoc:
387
373
  # data, the returned data is assigned to <tt>$_</tt> and <tt>$.</tt> is set
388
374
  # to the value of #lineno.
389
375
  #
390
- # Raises IOError if #closed_read? returns +true+. Raises IOError unless
376
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
391
377
  # #readable? returns +true+. Raises all errors raised by #unbuffered_read
392
378
  # except for EOFError.
393
379
  #
394
- # NOTE: When _sep_string_ is not +nil+, this method ignores Errno::EAGAIN
395
- # and Errno::EINTR raised by #unbuffered_read. Therefore, this method
396
- # always blocks. Aside from that exception and the conversion of EOFError
397
- # results into +nil+ results, this method will also raise the same errors
398
- # and block at the same times as #unbuffered_read.
380
+ # <b>NOTE:</b> When _sep_string_ is not +nil+, this method ignores
381
+ # Errno::EAGAIN and Errno::EINTR raised by #unbuffered_read. Therefore,
382
+ # this method always blocks. Aside from that exception and the conversion
383
+ # of EOFError results into +nil+ results, this method will also raise the
384
+ # same errors and block at the same times as #unbuffered_read.
399
385
  def gets(sep_string = $/)
400
386
  # Set the last read line in the global.
401
387
  $_ = readline(sep_string)
@@ -411,7 +397,10 @@ class IO # :nodoc:
411
397
  # ios.isatty -> false
412
398
  #
413
399
  # Returns +false+. Just for compatibility with IO.
400
+ #
401
+ # Raises IOError if #closed? returns +true+.
414
402
  def isatty
403
+ raise IOError, 'closed stream' if closed?
415
404
  false
416
405
  end
417
406
  alias :tty? :isatty
@@ -424,10 +413,10 @@ class IO # :nodoc:
424
413
  # the other line-based reading methods with a non-default value for
425
414
  # _sep_string_ or after changing <tt>$/</tt> will affect this.
426
415
  #
427
- # Raises IOError if #closed_read? returns +true+. Raises IOError unless
416
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
428
417
  # #readable? returns +true+.
429
418
  def lineno
430
- raise IOError, 'closed stream' if closed_read?
419
+ raise IOError, 'closed stream' if closed?
431
420
  raise IOError, 'not opened for reading' unless readable?
432
421
  @__io_like__lineno ||= 0
433
422
  end
@@ -436,14 +425,20 @@ class IO # :nodoc:
436
425
  # ios.lineno = lineno -> lineno
437
426
  #
438
427
  # Sets the current line number to the given value. <tt>$.</tt> is updated
439
- # by the _next_ call to #gets.
428
+ # by the _next_ call to #gets. If the object given is not an integer, it is
429
+ # converted to one using its to_int method.
440
430
  #
441
- # Raises IOError if #closed_read? returns +true+. Raises IOError unless
431
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
442
432
  # #readable? returns +true+.
443
433
  def lineno=(integer)
444
- raise IOError, 'closed stream' if closed_read?
434
+ raise IOError, 'closed stream' if closed?
445
435
  raise IOError, 'not opened for reading' unless readable?
446
- @__io_like__lineno = integer
436
+ if integer.nil? then
437
+ raise TypeError, 'no implicit conversion from nil to integer'
438
+ elsif ! integer.respond_to?(:to_int) then
439
+ raise TypeError, "can't convert #{integer.class} into Integer"
440
+ end
441
+ @__io_like__lineno = integer.to_int
447
442
  end
448
443
 
449
444
  # call-seq:
@@ -464,14 +459,36 @@ class IO # :nodoc:
464
459
  # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
465
460
  # #seekable? returns +true+.
466
461
  #
467
- # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
468
- # (when the internal write buffer is not empty), it will also raise the same
469
- # errors and block at the same times as those functions.
462
+ # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
463
+ # #unbuffered_write (when the internal write buffer is not empty), it will
464
+ # also raise the same errors and block at the same times as those functions.
470
465
  def pos=(position)
471
466
  seek(position, IO::SEEK_SET)
472
467
  position
473
468
  end
474
469
 
470
+ # call-seq:
471
+ # ios.pos -> integer
472
+ #
473
+ # Returns the current offest of ios.
474
+ #
475
+ # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
476
+ # #seekable? returns +true+.
477
+ #
478
+ # As a side effect, the internal write buffer is flushed unless this is
479
+ # a writable, non-duplexed object. This is for compatibility with the
480
+ # behavior of IO#pos.
481
+ #
482
+ # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
483
+ # #unbuffered_write (when the internal write buffer is not empty), it will
484
+ # also raise the same errors and block at the same times as those functions.
485
+ def pos
486
+ # Flush the internal write buffer for writable, non-duplexed objects.
487
+ buffered_flush if writable? && ! duplexed?
488
+ buffered_seek(0, IO::SEEK_CUR)
489
+ end
490
+ alias :tell :pos
491
+
475
492
  # call-seq:
476
493
  # ios.print([obj, ...]) -> nil
477
494
  #
@@ -482,10 +499,10 @@ class IO # :nodoc:
482
499
  # record separator (<tt>$\\</tt>) is written after all other data if it is
483
500
  # not nil.
484
501
  #
485
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
502
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
486
503
  # #writable? returns +true+.
487
504
  #
488
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
505
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
489
506
  # #unbuffered_write. Therefore, this method always blocks if unable to
490
507
  # immediately write +[obj, ...]+ completely. Aside from that exception,
491
508
  # this method will also raise the same errors and block at the same times as
@@ -522,10 +539,10 @@ class IO # :nodoc:
522
539
  # Writes the String returned by calling Kernel.sprintf using the given
523
540
  # arguments.
524
541
  #
525
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
542
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
526
543
  # #writable? returns +true+.
527
544
  #
528
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
545
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
529
546
  # #unbuffered_write. Therefore, this method always blocks if unable to
530
547
  # immediately write its arguments completely. Aside from that exception,
531
548
  # this method will also raise the same errors and block at the same times as
@@ -538,23 +555,23 @@ class IO # :nodoc:
538
555
  # call-seq:
539
556
  # ios.putc(obj) -> obj
540
557
  #
541
- # If _obj_ is Numeric, write the result of <tt>obj.chr</tt>; otherwise,
542
- # write the first character of <tt>obj.to_s</tt>.
558
+ # If _obj_ is a String, write the first byte; otherwise, convert _obj_ to a
559
+ # integer using its _to_int_ method and write the low order byte.
543
560
  #
544
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
561
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
545
562
  # #writable? returns +true+.
546
563
  #
547
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
564
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
548
565
  # #unbuffered_write. Therefore, this method always blocks if unable to
549
566
  # immediately write _obj_ completely. Aside from that exception, this
550
567
  # method will also raise the same errors and block at the same times as
551
568
  # #unbuffered_write.
552
569
  def putc(obj)
553
570
  char = case obj
554
- when Numeric
555
- obj.chr
571
+ when String
572
+ obj[0].chr
556
573
  else
557
- obj.to_s[0].chr
574
+ [obj.to_int].pack('V')[0].chr
558
575
  end
559
576
  write(char)
560
577
  obj
@@ -569,18 +586,18 @@ class IO # :nodoc:
569
586
  # is written after each object which does not end with the record separator
570
587
  # already. If no objects are given, a single record separator is written.
571
588
  #
572
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
589
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
573
590
  # #writable? returns +true+.
574
591
  #
575
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
592
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
576
593
  # #unbuffered_write. Therefore, this method always blocks if unable to
577
594
  # immediately write +[obj, ...]+ completely. Aside from that exception,
578
595
  # this method will also raise the same errors and block at the same times as
579
596
  # #unbuffered_write.
580
597
  #
581
- # NOTE: In order to be compatible with IO#puts, the record separator is
582
- # currently hardcoded to be a single newline (<tt>"\n"</tt>) even though the
583
- # documentation implies that the output record separator (<tt>$\\</tt>)
598
+ # <b>NOTE:</b> In order to be compatible with IO#puts, the record separator
599
+ # is currently hardcoded to be a single newline (<tt>"\n"</tt>) even though
600
+ # the documentation implies that the output record separator (<tt>$\\</tt>)
584
601
  # should be used.
585
602
  def puts(*args)
586
603
  # Set the output record separator such that this method is compatible with
@@ -596,13 +613,13 @@ class IO # :nodoc:
596
613
  # Write each argument followed by the record separator. Recursively
597
614
  # process arguments which are Array instances.
598
615
  args.each do |arg|
599
- if arg.kind_of?(Array) then
600
- puts(*arg)
601
- else
602
- line = arg.nil? ? 'nil' : arg.to_s
603
- line += ors if line.index(ors, -ors.length).nil?
604
- write(line)
605
- end
616
+ line = arg.nil? ?
617
+ 'nil' :
618
+ arg.kind_of?(Array) ?
619
+ array_join(arg, ors) :
620
+ arg.to_s
621
+ line += ors if line.index(ors, -ors.length).nil?
622
+ write(line)
606
623
  end
607
624
 
608
625
  nil
@@ -619,22 +636,22 @@ class IO # :nodoc:
619
636
  # If _length_ is unspecified or +nil+, all remaining data is returned. If
620
637
  # no data would be returned at all, an empty String is returned.
621
638
  #
622
- # If _buffer_ is specified, it is assumed to be a String and will be filled
623
- # with the returned data if any.
639
+ # If _buffer_ is specified, it will be converted to a String using its
640
+ # +to_str+ method if necessary and will be filled with the returned data if
641
+ # any.
624
642
  #
625
- # Raises IOError if #closed_read? returns +true+. Raises IOError unless
643
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
626
644
  # #readable? returns +true+.
627
645
  #
628
- # NOTE: Because this method relies on #unbuffered_read, it will also raise
629
- # the same errors and block at the same times as that function.
646
+ # <b>NOTE:</b> Because this method relies on #unbuffered_read, it will also
647
+ # raise the same errors and block at the same times as that function.
630
648
  def read(length = nil, buffer = nil)
631
649
  # Check the validity of the method arguments.
632
650
  unless length.nil? || length >= 0 then
633
651
  raise ArgumentError, "negative length #{length} given"
634
652
  end
635
- buffer = '' if buffer.nil?
636
- # Flush the buffer.
637
- buffer.slice!(0..-1)
653
+ buffer = buffer.nil? ? '' : buffer.to_str
654
+ buffer.slice!(0..-1) unless buffer.empty?
638
655
 
639
656
  if length.nil? then
640
657
  # Read and return everything.
@@ -667,8 +684,8 @@ class IO # :nodoc:
667
684
  # This default implementation of #read_ready? is a hack which should be able
668
685
  # to work for both real IO objects and IO-like objects; however, it is
669
686
  # inefficient since it merely sleeps for 1 second and then returns +true+ as
670
- # long as #closed_read? returns +false+. IO.select should be used for real
671
- # IO objects to wait for a readable condition on platforms with support for
687
+ # long as #closed? returns +false+. IO.select should be used for real IO
688
+ # objects to wait for a readable condition on platforms with support for
672
689
  # IO.select. Other solutions should be found as necessary to improve this
673
690
  # implementation on a case by case basis.
674
691
  #
@@ -684,12 +701,12 @@ class IO # :nodoc:
684
701
  #
685
702
  # Returns +true+ if the stream is both open and readable, +false+ otherwise.
686
703
  #
687
- # This implementation calls #closed_read? and checks to see if
688
- # #unbuffered_read is defined in order to make its determination. Override
689
- # this if the implementing class always provides the #unbuffered_read method
690
- # but may not always be open in a readable mode.
704
+ # This implementation checks to see if #unbuffered_read is defined in order
705
+ # to make its determination. Override this if the implementing class always
706
+ # provides the #unbuffered_read method but may not always be open in a
707
+ # readable mode.
691
708
  def readable?
692
- ! closed_read? && respond_to?(:unbuffered_read, true)
709
+ ! @__io_like__closed_read && respond_to?(:unbuffered_read, true)
693
710
  end
694
711
 
695
712
  # call-seq:
@@ -698,17 +715,17 @@ class IO # :nodoc:
698
715
  # Reads and returns _length_ bytes from the data stream.
699
716
  #
700
717
  # Raises EOFError if reading begins at the end of the stream. Raises
701
- # IOError if #closed_read? returns +true+. Raises IOError unless
702
- # #readable? returns +true+. Raises TruncatedDataError if insufficient
703
- # data is immediately available to satisfy the request.
718
+ # IOError if #closed? returns +true+. Raises IOError unless #readable?
719
+ # returns +true+. Raises TruncatedDataError if insufficient data is
720
+ # immediately available to satisfy the request.
704
721
  #
705
722
  # In the case of TruncatedDataError being raised, the retrieved data can be
706
723
  # fetched from the _data_ attribute of the exception.
707
724
  #
708
725
  # This method is basically copied from IO#readbytes.
709
726
  #
710
- # NOTE: Because this method relies on #unbuffered_read, it will also raise
711
- # the same errors and block at the same times as that function.
727
+ # <b>NOTE:</b> Because this method relies on #unbuffered_read, it will also
728
+ # raise the same errors and block at the same times as that function.
712
729
  def readbytes(length)
713
730
  buffer = read(length)
714
731
  if buffer.nil? then
@@ -726,15 +743,14 @@ class IO # :nodoc:
726
743
  # Returns the next 8-bit byte (0..255) from the stream.
727
744
  #
728
745
  # Raises EOFError when there is no more data in the stream. Raises IOError
729
- # if #closed_read? returns +true+. Raises IOError unless #readable? returns
746
+ # if #closed? returns +true+. Raises IOError unless #readable? returns
730
747
  # +true+.
731
748
  #
732
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
749
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
733
750
  # #unbuffered_read. Therefore, this method always blocks. Aside from that
734
751
  # exception, this method will also raise the same errors and block at the
735
752
  # same times as #unbuffered_read.
736
753
  def readchar
737
- raise IOError, 'closed stream' if closed_read?
738
754
  buffered_read(1)[0]
739
755
  rescue Errno::EAGAIN, Errno::EINTR
740
756
  retry if read_ready?
@@ -747,22 +763,28 @@ class IO # :nodoc:
747
763
  # _sep_string_. Increments #lineno.
748
764
  #
749
765
  # If _sep_string_ is +nil+, a line is defined as the remaining contents of
750
- # the stream. If _sep_string_ is empty, a paragraph is returned, where a
751
- # paragraph is defined as data followed by 2 or more successive newline
752
- # characters (only 2 newlines are returned at the end of the returned data).
766
+ # the stream. If _sep_string_ is not a String, it is converted to one using
767
+ # its +to_str+ method. If _sep_string_ is empty, a paragraph is returned,
768
+ # where a paragraph is defined as data followed by 2 or more successive
769
+ # newline characters (only 2 newlines are returned at the end of the
770
+ # returned data).
753
771
  #
754
772
  # In any case, the end of the stream terminates the current line.
755
773
  #
756
774
  # Raises EOFError when there is no more data in the stream. Raises IOError
757
- # if #closed_read? returns +true+. Raises IOError unless #readable? returns
775
+ # if #closed? returns +true+. Raises IOError unless #readable? returns
758
776
  # +true+.
759
777
  #
760
- # NOTE: When _sep_string_ is not +nil+, this method ignores Errno::EAGAIN
761
- # and Errno::EINTR raised by #unbuffered_read. Therefore, this method
762
- # always blocks. Aside from that exception, this method will also raise the
763
- # same errors and block at the same times as #unbuffered_read.
778
+ # <b>NOTE:</b> When _sep_string_ is not +nil+, this method ignores
779
+ # Errno::EAGAIN and Errno::EINTR raised by #unbuffered_read. Therefore,
780
+ # this method always blocks. Aside from that exception, this method will
781
+ # also raise the same errors and block at the same times as
782
+ # #unbuffered_read.
764
783
  def readline(sep_string = $/)
765
- raise IOError, 'closed stream' if closed_read?
784
+ # Ensure that sep_string is either nil or a String.
785
+ unless sep_string.nil? || sep_string.kind_of?(String) then
786
+ sep_string = sep_string.to_str
787
+ end
766
788
 
767
789
  buffer = ''
768
790
  begin
@@ -777,14 +799,14 @@ class IO # :nodoc:
777
799
  # Record if the user requested paragraphs rather than lines.
778
800
  paragraph_requested = sep_string.empty?
779
801
  # An empty line separator string indicates that the user wants to
780
- # return paragraphs. A pair of newlines in the stream is used to mark
781
- # this.
802
+ # return paragraphs. A pair of newlines in the stream is used to
803
+ # mark this.
782
804
  sep_string = "\n\n" if paragraph_requested
783
805
 
784
806
  # Add each character from the input to the buffer until either the
785
807
  # buffer has the right ending or the end of the input is reached.
786
808
  while buffer.index(sep_string, -sep_string.length).nil? &&
787
- (char = readchar) do
809
+ (char = buffered_read(1)) do
788
810
  buffer << char
789
811
  end
790
812
 
@@ -792,7 +814,9 @@ class IO # :nodoc:
792
814
  # If the user requested paragraphs instead of lines, we need to
793
815
  # consume and discard all newlines remaining at the front of the
794
816
  # input.
795
- while (char = readchar) && char == "\n" do; end
817
+ while char == "\n" && (char = buffered_read(1)) do
818
+ nil
819
+ end
796
820
  # Put back the last character.
797
821
  ungetc(char[0])
798
822
  end
@@ -814,20 +838,23 @@ class IO # :nodoc:
814
838
  # Returns an Array containing the lines in the stream using #each_line.
815
839
  #
816
840
  # If _sep_string_ is +nil+, a line is defined as the remaining contents of
817
- # the stream. If _sep_string_ is empty, a paragraph is returned, where a
818
- # paragraph is defined as data followed by 2 or more successive newline
819
- # characters (only 2 newlines are returned at the end of the returned data).
841
+ # the stream. If _sep_string_ is not a String, it is converted to one using
842
+ # its +to_str+ method. If _sep_string_ is empty, a paragraph is returned,
843
+ # where a paragraph is defined as data followed by 2 or more successive
844
+ # newline characters (only 2 newlines are returned at the end of the
845
+ # returned data).
820
846
  #
821
847
  # In any case, the end of the stream terminates the current line.
822
848
  #
823
849
  # Raises EOFError when there is no more data in the stream. Raises IOError
824
- # if #closed_read? returns +true+. Raises IOError unless #readable? returns
850
+ # if #closed? returns +true+. Raises IOError unless #readable? returns
825
851
  # +true+.
826
852
  #
827
- # NOTE: When _sep_string_ is not +nil+, this method ignores Errno::EAGAIN
828
- # and Errno::EINTR raised by #unbuffered_read. Therefore, this method
829
- # always blocks. Aside from that exception, this method will also raise the
830
- # same errors and block at the same times as #unbuffered_read.
853
+ # <b>NOTE:</b> When _sep_string_ is not +nil+, this method ignores
854
+ # Errno::EAGAIN and Errno::EINTR raised by #unbuffered_read. Therefore,
855
+ # this method always blocks. Aside from that exception, this method will
856
+ # also raise the same errors and block at the same times as
857
+ # #unbuffered_read.
831
858
  def readlines(sep_string = $/)
832
859
  lines = []
833
860
  each_line(sep_string) { |line| lines << line }
@@ -844,10 +871,10 @@ class IO # :nodoc:
844
871
  # whether or not the data stream would block.
845
872
  #
846
873
  # Raises EOFError when there is no more data in the stream. Raises IOError
847
- # if #closed_read? returns +true+. Raises IOError unless #readable? returns
874
+ # if #closed? returns +true+. Raises IOError unless #readable? returns
848
875
  # +true+.
849
876
  #
850
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
877
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
851
878
  # #unbuffered_read. Therefore, this method always blocks if unable to
852
879
  # immediately return _length_ bytes. Aside from that exception, this method
853
880
  # will also raise the same errors and block at the same times as
@@ -861,9 +888,6 @@ class IO # :nodoc:
861
888
  # Flush the buffer.
862
889
  buffer.slice!(0..-1)
863
890
 
864
- raise IOError, 'closed stream' if closed_read?
865
- raise IOError, 'not opened for reading' unless readable?
866
-
867
891
  # Read and return up to length bytes.
868
892
  if internal_read_buffer.empty? then
869
893
  begin
@@ -872,6 +896,9 @@ class IO # :nodoc:
872
896
  retry if read_ready?
873
897
  end
874
898
  else
899
+ raise IOError, 'closed stream' if closed?
900
+ raise IOError, 'not opened for reading' unless readable?
901
+
875
902
  buffer << internal_read_buffer.slice!(0, length)
876
903
  end
877
904
  buffer
@@ -889,9 +916,9 @@ class IO # :nodoc:
889
916
  # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
890
917
  # #seekable? returns +true+.
891
918
  #
892
- # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
893
- # (when the internal write buffer is not empty), it will also raise the same
894
- # errors and block at the same times as those functions.
919
+ # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
920
+ # #unbuffered_write (when the internal write buffer is not empty), it will
921
+ # also raise the same errors and block at the same times as those functions.
895
922
  def rewind
896
923
  seek(0, IO::SEEK_SET)
897
924
  self.lineno = 0
@@ -906,17 +933,17 @@ class IO # :nodoc:
906
933
  # counts from the end of the data (_offset_ should be negative here). If
907
934
  # _whence_ is IO::SEEK_CUR, _offset_ is relative to the current position.
908
935
  #
909
- # As a side effect, the internal read and write buffers are flushed.
936
+ # As a side effect, the internal read and write buffers are flushed except
937
+ # when seeking relative to the current position (whence is IO::SEEK_CUR) to
938
+ # a location within the internal read buffer.
910
939
  #
911
940
  # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
912
941
  # #seekable? returns +true+.
913
942
  #
914
- # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
915
- # (when the internal write buffer is not empty), it will also raise the same
916
- # errors and block at the same times as those functions.
943
+ # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
944
+ # #unbuffered_write (when the internal write buffer is not empty), it will
945
+ # also raise the same errors and block at the same times as those functions.
917
946
  def seek(offset, whence = IO::SEEK_SET)
918
- raise IOError, 'closed stream' if closed?
919
-
920
947
  buffered_seek(offset, whence)
921
948
  0
922
949
  end
@@ -924,14 +951,14 @@ class IO # :nodoc:
924
951
  # call-seq:
925
952
  # ios.seekable? -> true or false
926
953
  #
927
- # Returns +true+ if the stream is both open and seekable, +false+ otherwise.
954
+ # Returns +true+ if the stream is seekable, +false+ otherwise.
928
955
  #
929
- # This implementation calls #closed? and checks to see if #unbuffered_seek
930
- # is defined in order to make its determination. Override this if the
931
- # implementing class always provides the #unbuffered_seek method but may not
932
- # always be seekable.
956
+ # This implementation always returns +false+ for duplexed objects and
957
+ # checks to see if #unbuffered_seek is defined in order to make its
958
+ # determination otherwise. Override this if the implementing class always
959
+ # provides the #unbuffered_seek method but may not always be seekable.
933
960
  def seekable?
934
- ! closed? && respond_to?(:unbuffered_seek, true)
961
+ ! duplexed? && respond_to?(:unbuffered_seek, true)
935
962
  end
936
963
 
937
964
  # call-seq:
@@ -940,9 +967,9 @@ class IO # :nodoc:
940
967
  # Returns true if the internal write buffer is currently being bypassed,
941
968
  # false otherwise.
942
969
  #
943
- # Raises IOError if #closed_write? returns +true+.
970
+ # Raises IOError if #closed? returns +true+.
944
971
  def sync
945
- raise IOError, 'closed stream' if closed_write?
972
+ raise IOError, 'closed stream' if closed?
946
973
  @__io_like__sync ||= false
947
974
  end
948
975
 
@@ -954,10 +981,10 @@ class IO # :nodoc:
954
981
  # operation. When set to +false+, the internal write buffer will be
955
982
  # enabled.
956
983
  #
957
- # Raises IOError if #closed_write? returns +true+.
984
+ # Raises IOError if #closed? returns +true+.
958
985
  def sync=(sync)
959
- raise IOError, 'closed stream' if closed_write?
960
- @__io_like__sync = sync
986
+ raise IOError, 'closed stream' if closed?
987
+ @__io_like__sync = sync ? true : false
961
988
  end
962
989
 
963
990
  # call-seq:
@@ -971,38 +998,42 @@ class IO # :nodoc:
971
998
  #
972
999
  # Raises EOFError if reading begins at the end of the stream. Raises
973
1000
  # IOError if the internal read buffer is not empty. Raises IOError if
974
- # #closed_read? returns +true+.
1001
+ # #closed? returns +true+.
975
1002
  #
976
- # NOTE: Because this method relies on #unbuffered_read, it will also raise
977
- # the same errors and block at the same times as that function.
1003
+ # <b>NOTE:</b> Because this method relies on #unbuffered_read, it will also
1004
+ # raise the same errors and block at the same times as that function.
978
1005
  def sysread(length, buffer = nil)
979
- buffer = '' if buffer.nil?
980
- buffer.slice!(0..-1)
1006
+ buffer = buffer.nil? ? '' : buffer.to_str
1007
+ buffer.slice!(0..-1) unless buffer.empty?
981
1008
  return buffer if length == 0
982
1009
 
983
- raise IOError, 'closed stream' if closed_read?
1010
+ raise IOError, 'closed stream' if closed?
984
1011
  raise IOError, 'not opened for reading' unless readable?
985
1012
  unless internal_read_buffer.empty? then
986
1013
  raise IOError, 'sysread on buffered IO'
987
1014
  end
988
1015
 
1016
+ # Flush the internal write buffer for writable, non-duplexed objects.
1017
+ buffered_flush if writable? && ! duplexed?
1018
+
989
1019
  buffer << unbuffered_read(length)
990
1020
  end
991
1021
 
992
1022
  # call-seq:
993
- # ios.sysseek(offset, whence) -> integer
1023
+ # ios.sysseek(offset[, whence]) -> integer
994
1024
  #
995
- # Sets the data pointer of the data stream to the position requested by
996
- # _offset_ and _whence_ and returns the new position.
1025
+ # Sets the current data position to _offset_ based on the setting of
1026
+ # _whence_. If _whence_ is unspecified or IO::SEEK_SET, _offset_ counts
1027
+ # from the beginning of the data. If _whence_ is IO::SEEK_END, _offset_
1028
+ # counts from the end of the data (_offset_ should be negative here). If
1029
+ # _whence_ is IO::SEEK_CUR, _offset_ is relative to the current position.
997
1030
  #
998
1031
  # Raises IOError if the internal read buffer is not empty. Raises IOError
999
- # if #closed? returns +true+.
1032
+ # if #closed? returns +true+. Raises Errno::ESPIPE unless #seekable?
1033
+ # returns +true+.
1000
1034
  #
1001
- # See the description of the operation of #unbuffered_seek for information
1002
- # concerning how to interpret _offset_ and _whence_.
1003
- #
1004
- # NOTE: Because this method relies on #unbuffered_seek, it will also raise
1005
- # the same errors and block at the same times as that function.
1035
+ # <b>NOTE:</b> Because this method relies on #unbuffered_seek, it will also
1036
+ # raise the same errors and block at the same times as that function.
1006
1037
  def sysseek(offset, whence = IO::SEEK_SET)
1007
1038
  raise IOError, 'closed stream' if closed?
1008
1039
  raise Errno::ESPIPE, 'Illegal seek' unless seekable?
@@ -1010,6 +1041,7 @@ class IO # :nodoc:
1010
1041
  unless internal_write_buffer.empty? then
1011
1042
  warn('warning: sysseek on buffered IO')
1012
1043
  end
1044
+
1013
1045
  unbuffered_seek(offset, whence)
1014
1046
  end
1015
1047
 
@@ -1022,46 +1054,27 @@ class IO # :nodoc:
1022
1054
  # As a side effect for non-duplex objects, the internal read buffer is
1023
1055
  # flushed.
1024
1056
  #
1025
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
1057
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
1026
1058
  # #writable? returns +true+.
1027
1059
  #
1028
- # NOTE: Because this method relies on #unbuffered_write, it will also raise
1029
- # the same errors and block at the same times as that function.
1060
+ # <b>NOTE:</b> Because this method relies on #unbuffered_write, it will also
1061
+ # raise the same errors and block at the same times as that function.
1030
1062
  def syswrite(string)
1031
- raise IOError, 'closed stream' if closed_write?
1063
+ raise IOError, 'closed stream' if closed?
1032
1064
  raise IOError, 'not opened for writing' unless writable?
1033
- unless duplexed? || internal_read_buffer.empty? then
1034
- internal_read_buffer.slice(0..-1)
1035
- end
1036
1065
  unless internal_write_buffer.empty? then
1037
1066
  warn('warning: syswrite on buffered IO')
1038
1067
  end
1039
1068
 
1040
- unbuffered_write(string)
1041
- end
1042
-
1043
- # call-seq:
1044
- # ios.tell -> integer
1045
- #
1046
- # Returns the current offest of ios.
1047
- #
1048
- # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
1049
- # #seekable? returns +true+.
1050
- #
1051
- # As a side effect, the internal write buffer is flushed unless this is
1052
- # a duplexed object. This is for compatibility with the behavior of
1053
- # IO#tell.
1054
- #
1055
- # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
1056
- # (when the internal write buffer is not empty), it will also raise the same
1057
- # errors and block at the same times as those functions.
1058
- def tell
1059
- raise IOError, 'closed stream' if closed?
1069
+ # Flush the internal read buffer and set the unbuffered position to the
1070
+ # buffered position when dealing with non-duplexed objects.
1071
+ unless duplexed? || internal_read_buffer.empty? then
1072
+ unbuffered_seek(-internal_read_buffer.length, IO::SEEK_CUR)
1073
+ internal_read_buffer.slice!(0..-1)
1074
+ end
1060
1075
 
1061
- buffered_flush unless internal_write_buffer.empty?
1062
- buffered_tell
1076
+ unbuffered_write(string)
1063
1077
  end
1064
- alias :pos :tell
1065
1078
 
1066
1079
  # call-seq:
1067
1080
  # ios.to_io -> ios
@@ -1076,7 +1089,7 @@ class IO # :nodoc:
1076
1089
  #
1077
1090
  # Calls #unread with <tt>integer.chr</tt> as an argument.
1078
1091
  #
1079
- # Raises IOError if #closed_read? returns +true+. Raises IOError unless
1092
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
1080
1093
  # #readable? returns +true+.
1081
1094
  def ungetc(integer)
1082
1095
  unread(integer.chr)
@@ -1089,12 +1102,12 @@ class IO # :nodoc:
1089
1102
  # returns +nil+. If _string_ is not a String, it is converted to one using
1090
1103
  # its +to_s+ method.
1091
1104
  #
1092
- # Raises IOError if #closed_read? returns +true+. Raises IOError unless
1105
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
1093
1106
  # #readable? returns +true+.
1094
1107
  def unread(string)
1095
- raise IOError, 'closed stream' if closed_read?
1108
+ raise IOError, 'closed stream' if closed?
1096
1109
  raise IOError, 'not opened for reading' unless readable?
1097
- internal_read_buffer.insert(0, data.to_s)
1110
+ internal_read_buffer.insert(0, string.to_s)
1098
1111
  nil
1099
1112
  end
1100
1113
 
@@ -1107,7 +1120,7 @@ class IO # :nodoc:
1107
1120
  # This default implementation of #write_ready? is a hack which should be
1108
1121
  # able to work for both real IO objects and IO-like objects; however, it is
1109
1122
  # inefficient since it merely sleeps for 1 second and then returns +true+ as
1110
- # long as #closed_write? returns +false+. IO.select should be used for real
1123
+ # long as #closed? returns +false+. IO.select should be used for real
1111
1124
  # IO objects to wait for a writeable condition on platforms with support for
1112
1125
  # IO.select. Other solutions should be found as necessary to improve this
1113
1126
  # implementation on a case by case basis.
@@ -1124,12 +1137,12 @@ class IO # :nodoc:
1124
1137
  #
1125
1138
  # Returns +true+ if the stream is both open and writable, +false+ otherwise.
1126
1139
  #
1127
- # This implementation calls #closed_write? and checks to see if
1128
- # #unbuffered_write is defined in order to make its determination. Override
1129
- # this if the implementing class always provides the #unbuffered_write
1130
- # method but may not always be open in a writable mode.
1140
+ # This implementation checks to see if #unbuffered_write is defined in order
1141
+ # to make its determination. Override this if the implementing class always
1142
+ # provides the #unbuffered_write method but may not always be open in a
1143
+ # writable mode.
1131
1144
  def writable?
1132
- ! closed_write? && respond_to?(:unbuffered_write, true)
1145
+ ! @__io_like__closed_write && respond_to?(:unbuffered_write, true)
1133
1146
  end
1134
1147
 
1135
1148
  # call-seq:
@@ -1140,18 +1153,18 @@ class IO # :nodoc:
1140
1153
  # convert it into one. The entire contents of _string_ are written,
1141
1154
  # blocking as necessary even if the data stream does not block.
1142
1155
  #
1143
- # Raises IOError if #closed_write? returns +true+. Raises IOError unless
1156
+ # Raises IOError if #closed? returns +true+. Raises IOError unless
1144
1157
  # #writable? returns +true+.
1145
1158
  #
1146
- # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
1159
+ # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
1147
1160
  # #unbuffered_write. Therefore, this method always blocks if unable to
1148
1161
  # immediately write _string_ completely. Aside from that exception, this
1149
1162
  # method will also raise the same errors and block at the same times as
1150
1163
  # #unbuffered_write.
1151
1164
  def write(string)
1152
- raise IOError, 'closed stream' if closed_write?
1153
-
1154
1165
  string = string.to_s
1166
+ return 0 if string.empty?
1167
+
1155
1168
  bytes_written = 0
1156
1169
  while bytes_written < string.length do
1157
1170
  begin
@@ -1172,10 +1185,11 @@ class IO # :nodoc:
1172
1185
  #
1173
1186
  # Raises IOError unless #writable? returns +true+.
1174
1187
  #
1175
- # NOTE: Because this method relies on #unbuffered_write, it raises all
1176
- # errors raised by #unbuffered_write and blocks when #unbuffered_write
1188
+ # <b>NOTE:</b> Because this method relies on #unbuffered_write, it raises
1189
+ # all errors raised by #unbuffered_write and blocks when #unbuffered_write
1177
1190
  # blocks.
1178
- def buffered_flush # :nodoc:
1191
+ def buffered_flush
1192
+ raise IOError, 'closed stream' if closed?
1179
1193
  raise IOError, 'not opened for writing' unless writable?
1180
1194
 
1181
1195
  until internal_write_buffer.empty? do
@@ -1193,17 +1207,18 @@ class IO # :nodoc:
1193
1207
  # Raises EOFError if the internal read buffer is empty and reading begins at
1194
1208
  # the end of the stream. Raises IOError unless #readable? returns +true+.
1195
1209
  #
1196
- # NOTE: Because this method relies on #unbuffered_read, it raises all errors
1197
- # raised by #unbuffered_read and blocks when #unbuffered_read blocks
1210
+ # <b>NOTE:</b> Because this method relies on #unbuffered_read, it raises all
1211
+ # errors raised by #unbuffered_read and blocks when #unbuffered_read blocks
1198
1212
  # whenever the internal read buffer is unable to fulfill the request.
1199
- def buffered_read(length) # :nodoc:
1213
+ def buffered_read(length)
1200
1214
  # Check the validity of the method arguments.
1201
1215
  raise ArgumentError, "non-positive length #{length} given" if length < 0
1202
1216
 
1217
+ raise IOError, 'closed stream' if closed?
1203
1218
  raise IOError, 'not opened for reading' unless readable?
1204
1219
 
1205
- # Flush the internal write buffer for non-duplexed objects.
1206
- buffered_flush unless internal_write_buffer.empty? || duplexed?
1220
+ # Flush the internal write buffer for writable, non-duplexed objects.
1221
+ buffered_flush if writable? && ! duplexed?
1207
1222
 
1208
1223
  # Ensure that the internal read buffer has at least enough data to satisfy
1209
1224
  # the request.
@@ -1228,41 +1243,57 @@ class IO # :nodoc:
1228
1243
  # call-seq:
1229
1244
  # ios.buffered_seek(offset[, whence]) -> integer
1230
1245
  #
1231
- # Sets the new position for read or write operations using _offset_ and
1232
- # _whence_ to computer the position. Returns the new position.
1246
+ # Sets the current data position to _offset_ based on the setting of
1247
+ # _whence_. If _whence_ is unspecified or IO::SEEK_SET, _offset_ counts
1248
+ # from the beginning of the data. If _whence_ is IO::SEEK_END, _offset_
1249
+ # counts from the end of the data (_offset_ should be negative here). If
1250
+ # _whence_ is IO::SEEK_CUR, _offset_ is relative to the current position.
1233
1251
  #
1234
- # As a side effect, the internal read and write buffers are flushed.
1252
+ # As a side effect, the internal read and write buffers are flushed except
1253
+ # when seeking relative to the current position (whence is IO::SEEK_CUR) to
1254
+ # a location within the internal read buffer.
1235
1255
  #
1236
1256
  # Raises Errno::ESPIPE unless #seekable? returns +true+.
1237
1257
  #
1238
1258
  # See #seek for the usage of _offset_ and _whence_.
1239
1259
  #
1240
- # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
1241
- # (when the internal write buffer is not empty), it will raise the same
1242
- # errors and block at the same times as those functions.
1243
- def buffered_seek(offset, whence = IO::SEEK_SET) # :nodoc:
1244
- raise Errno::ESPIPE, 'Illegal seek' unless seekable?
1245
-
1246
- # Flush the internal buffers.
1247
- internal_read_buffer.slice!(0..-1)
1248
- buffered_flush unless internal_write_buffer.empty?
1249
- # Move the data stream's position as requested.
1250
- unbuffered_seek(offset, whence)
1251
- end
1252
-
1253
- # call-seq:
1254
- # ios.buffered_tell
1255
- #
1256
- # Returns the current position in the stream.
1257
- #
1258
- # Raises Errno::ESPIPE unless #seekable? returns +true+.
1259
- def buffered_tell # :nodoc:
1260
+ # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
1261
+ # #unbuffered_write (when the internal write buffer is not empty), it will
1262
+ # raise the same errors and block at the same times as those functions.
1263
+ def buffered_seek(offset, whence = IO::SEEK_SET)
1264
+ raise IOError, 'closed stream' if closed?
1260
1265
  raise Errno::ESPIPE, 'Illegal seek' unless seekable?
1261
1266
 
1262
- unless internal_read_buffer.empty? then
1267
+ if whence == IO::SEEK_CUR && offset == 0 then
1268
+ # The seek is only determining the current position, so return the
1269
+ # buffered position based on the read buffer if it's not empty and the
1270
+ # write buffer otherwise.
1271
+ internal_read_buffer.empty? ?
1272
+ unbuffered_seek(0, IO::SEEK_CUR) + internal_write_buffer.length :
1273
+ unbuffered_seek(0, IO::SEEK_CUR) - internal_read_buffer.length
1274
+ elsif whence == IO::SEEK_CUR && offset > 0 &&
1275
+ internal_write_buffer.empty? &&
1276
+ offset <= internal_read_buffer.length then
1277
+ # The seek is within the read buffer, so just discard a sufficient
1278
+ # amount of the buffer and report the new buffered position.
1279
+ internal_read_buffer.slice!(0, offset)
1263
1280
  unbuffered_seek(0, IO::SEEK_CUR) - internal_read_buffer.length
1264
1281
  else
1265
- unbuffered_seek(0, IO::SEEK_CUR) + internal_write_buffer.length
1282
+ # The seek target is outside of the buffers, so flush the buffers and
1283
+ # jump to the new position.
1284
+ if whence == IO::SEEK_CUR then
1285
+ # Adjust relative offsets based on the current buffered offset.
1286
+ offset += internal_read_buffer.empty? ?
1287
+ internal_write_buffer.length :
1288
+ -internal_read_buffer.length
1289
+ end
1290
+
1291
+ # Flush the internal buffers.
1292
+ internal_read_buffer.slice!(0..-1)
1293
+ buffered_flush if writable?
1294
+
1295
+ # Move the data stream's position as requested.
1296
+ unbuffered_seek(offset, whence)
1266
1297
  end
1267
1298
  end
1268
1299
 
@@ -1276,16 +1307,17 @@ class IO # :nodoc:
1276
1307
  # the internal write buffer cannot be immediately flushed due to the
1277
1308
  # underlying stream not blocking when unable to accept more data.
1278
1309
  #
1279
- # NOTE: Because this method relies on #unbuffered_write, it raises all
1280
- # errors raised by #unbuffered_write and blocks when #unbuffered_write
1310
+ # <b>NOTE:</b> Because this method relies on #unbuffered_write, it raises
1311
+ # all errors raised by #unbuffered_write and blocks when #unbuffered_write
1281
1312
  # blocks whenever the internal write buffer is unable to fulfill the
1282
1313
  # request.
1283
- def buffered_write(string) # :nodoc:
1314
+ def buffered_write(string)
1315
+ raise IOError, 'closed stream' if closed?
1284
1316
  raise IOError, 'not opened for writing' unless writable?
1285
1317
 
1286
1318
  # Flush the internal read buffer and set the unbuffered position to the
1287
1319
  # buffered position when dealing with non-duplexed objects.
1288
- if ! (duplexed? || internal_read_buffer.empty?) then
1320
+ unless duplexed? || internal_read_buffer.empty? then
1289
1321
  unbuffered_seek(-internal_read_buffer.length, IO::SEEK_CUR)
1290
1322
  internal_read_buffer.slice!(0..-1)
1291
1323
  end
@@ -1315,14 +1347,58 @@ class IO # :nodoc:
1315
1347
  end
1316
1348
 
1317
1349
  # Returns a reference to the internal read buffer.
1318
- def internal_read_buffer # :nodoc:
1350
+ def internal_read_buffer
1319
1351
  @__io_like__read_buffer ||= ''
1320
1352
  end
1321
1353
 
1322
1354
  # Returns a reference to the internal write buffer.
1323
- def internal_write_buffer # :nodoc:
1355
+ def internal_write_buffer
1324
1356
  @__io_like__write_buffer ||= ''
1325
1357
  end
1358
+
1359
+ # This method joins the elements of _array_ together with _separator_
1360
+ # between each element and returns the result. _seen_ is a list of object
1361
+ # IDs representing arrays which have already started processing.
1362
+ #
1363
+ # This method exists only because Array#join apparently behaves in an
1364
+ # implementation dependent manner when joining recursive arrays and so does
1365
+ # not always produce the expected results. Specifically, MRI 1.8.6 and
1366
+ # 1.8.7 behave as follows:
1367
+ #
1368
+ # x = []
1369
+ # x << 1 << x << 2
1370
+ # x.join(', ') => "1, 1, [...], 2, 2"
1371
+ #
1372
+ # The expected and necessary result for use with #puts is:
1373
+ #
1374
+ # "1, [...], 2"
1375
+ #
1376
+ # Things get progressively worse as the nesting and recursion become more
1377
+ # convoluted.
1378
+ def array_join(array, separator, seen = [])
1379
+ first = true
1380
+ seen.push(array.object_id)
1381
+ result = array.inject('') do |memo, item|
1382
+ if first then
1383
+ first = false
1384
+ else
1385
+ memo << separator
1386
+ end
1387
+
1388
+ memo << if item.kind_of?(Array) then
1389
+ if seen.include?(item.object_id) then
1390
+ '[...]'
1391
+ else
1392
+ array_join(item, separator, seen)
1393
+ end
1394
+ else
1395
+ item.to_s
1396
+ end
1397
+ end
1398
+ seen.pop
1399
+
1400
+ result
1401
+ end
1326
1402
  end
1327
1403
  end
1328
1404