tags:

views:

139

answers:

2

How do I make my own class that can be substituted for IO in order to e.g. redirect/capture input/output for some code that accepts an IO-like parameter? IO itself seems to be coupled to OS file descriptors and the only class I know of that mimics it without subclassing it is StringIO, which seems to just reimplement the entire interface.

I would think that the language would provide a straightforward way to do this but I can't find any information on the subject. Is there a mixin that implements the interface on top of a few primitives, as Enumerable does?

+4  A: 

I suspect I'm missing something, but supposing that's not the case...

Why would subclassing not be an option? Could you not just override the parts of IO that need to behave differently?

class MyIO < IO
  # your modified methods go here. e.g.
  def some_io_method(args)
    do_some_preprocessing
    super(args)
    do_some_post_processing
  end
end

If you can't substitute your home-brew subclass, what about modifying IO itself ("monkey-patching") something like:

class IO
  alias_method :original_some_io_method, :some_io_method
  def some_io_method(args)
    do_some_preprocessing
    original_some_io_method(args)
    do_some_post_processing
  end
end
Mike Woodhouse
The problem is that there are a hojillion methods that read/write data and reimplementing them all is tedious.Enumerable explicitly implements everything in terms of #each, thus by overriding #each, you get everything else "for free". I don't understand why IO doesn't work the same way or why the mixins I am now writing to accomplish this don't already exist.The other strange thing is that IO is so specialized, with methods like sys* that don't belong in other implementations. This can be worked around but is not ideal.
jedediah
A: 

I have successfully used the following hack to make StringIO a "real" IO:

    # make mechanize believe our address xml actually is an IO so it will be uploaded as a file:
    address_io = StringIO.new(address_xml)
    class << address_io
      alias is_a_old is_a?
      def is_a?(stuff)
        stuff == IO || is_a_old(stuff)
      end
      def path
        'address.xml'
      end
    end
I tried this with the library I'm working on and it caused some native code in the interpreter to go crazy, in some cases. Use with caution. Also, since <<address_io is a subclass of StringIO, the aliasing is not necessary. Just override the method and call super.
jedediah