On Wed, Aug 07, 2002 at 08:22:48AM +0900, alex f wrote:
> In particular, I'm interested in doing things similar to what I did in Perl
> with select and tie-ing STD filehandles to classes temporarily then
> returning to program default values. I've seen an interesting example of a
> string-based class implementing IO-like behaviour in 'The Ruby Way'
> (223-225), but I'm not sure how to manipulate Ruby's STDOUT and $stdout to
> do useful things with these sort of classes (using .reopen()?), so I'd
> appreciate pointers to threads or example code.

There's been some discussion
(http://www.rubygarden.org/article.php?sid=179) about changing IO to
make this task (and other tasks) easier.  At the moment, reopen() can
only be using with real IO objects (and won't work with a StringIO
object).

The following will work, but the solution is not ideal.  I'd like to
make it buffered (for better performance), and I'm not sure what should
happen if stop() is called when there is still data to be read.  And if
you want to be able to make this work during a system call, then you'll
need to use a separate process instead of a thread.

Hope this helps,

Paul

# A *very* basic StringIO implementation
class StringIO
  attr_reader :str

  def initialize
    @str = ''
  end

  def write(str)
    @str << str
  end
end

# A class to redirect $stdout (or other IO object) to a StringIO object
# (or other object with a write() method)
class Redirector
  def initialize(from, to)
    tmp = from.dup
    r, w = IO.pipe
    from.reopen(w)
    @t = Thread.new do
      begin
        loop do
          s = r.read(1) # TODO: can I make this buffered?
          to.write(s)
        end
      ensure
        from.reopen(tmp)
      end
    end
  end

  def stop
    @t.kill
  end

  def self.redirect(from, to)
    s = self.new(from, to)
    begin
      yield
    ensure
      s.stop
    end
  end
end

if __FILE__ == $0 then
  Thread.abort_on_exception = true
  s = StringIO.new
  r = Redirector.redirect($stdout, s) do
    $stdout.puts "this is a test"
    $stdout.puts "of the StringIO redirection system"
  end
  puts "Done redirecting."
  puts "Result:\n#{s.str}"
end