4

In the Ruby-Docs it gives the example of:

f1 = File.new("testfile")
f2 = File.new("testfile")
f2.readlines[0]   #=> "This is line one\n"
f2.reopen(f1)     #=> #<File:testfile>
f2.readlines[0]   #=> "This is line one\n"

My question is why reopen f2 when you could just f2.close and f1.readlines[0]? Are there any advantages to reopening with a new stream vs. just using the new stream?

webdesserts
  • 1,003
  • 8
  • 22

2 Answers2

1

I talked to some devs on IRB a while back and the response I got was that it was mostly used for changing the $std variables to modify where methods such as puts and print output to...

$stdout.reopen(File.open('log'))
puts 'hello world'

The reason for using this rather than...

$stdout = File.open('log')

...was kinda up in the air though. I had one dev that said that direct assignment didn't play well with some of ruby's C functions. I don't know much about C and can't say much about this, but he pointed me to some minitest source to see an example of it in use. However, apparently even the source has switched over to direct assignment vs. reopening since the dev last looked at it.

In conclusion... from the looks of it IO#reopen might be useless, but I would love to hear an argument against this.

Update

Ok, so I reread over the documentation and saw that there was a second set of opts for reopen:

reopen(path, mode_str) → ios

This actually seems somewhat useful as opposed to the reopen(other_IO) → ios option.

webdesserts
  • 1,003
  • 8
  • 22
0

I suspect that the main difference is that with reopen, the new stream would apply not only to subsequent uses of the $std... variable, but also to variables that previously were assigned the value of the $std... variable. This can be good or bad, depending on your situation.

This irb session shows that with reopen, a variable that was assigned previous to the stream change will acquire the newly changed stream. Note that the fileno does not change for either variable, and both variables produce no output:

> $stderr.fileno
 => 2
> stderr_copy = $stderr
 => #<IO:<STDERR>>
> stderr_copy.fileno
 => 2
> $stderr.reopen(File.open('/dev/null', 'w'))
 => #<File:/dev/null>
> stderr_copy.fileno
 => 2
> $stderr.fileno
 => 2
> $stderr.puts 'foo'
 => nil
> stderr_copy.puts 'foo'
 => nil

In contrast, when instead of using reopen, the newly opened /dev/null File object is assigned to $stderr, stderr_copy will retain its original output stream. Only $stderr gets the new fileno, and stderr_copy still produces output:

> $stderr.fileno
 => 2
> stderr_copy = $stderr
 => #<IO:<STDERR>>
> stderr_copy.fileno
 => 2
> $stderr = File.open('/dev/null', 'w')
 => #<File:/dev/null>
> $stderr.fileno
 => 10
> stderr_copy.fileno
 => 2
> $stderr.puts 'foo'
 => nil
> stderr_copy.puts 'foo'
foo
 => nil

If you want to use reopen, but want to save a copy of the original output stream, you can use dup:

> stderr_dup = $stderr.dup
 => #<IO:<STDERR>>
> stderr_dup.fileno
 => 10
> $stderr.reopen(File.open('/dev/null', 'w'))
 => #<File:/dev/null>
> $stderr.fileno
 => 2
> stderr_dup.puts 'foo'
foo
 => nil
> $stderr.puts 'foo'
 => nil
Keith Bennett
  • 4,722
  • 1
  • 25
  • 35