Temporarily override a methode

I'd like to do something like this (see below).

Is that possible?

class Tee
  @@already_redirected = false
  def self.tee(logfile, append = true)
    raise "output already redirected" if @@already_redirected
    @@already_redirected = true
    lf = File.open(logfile, append ? 'a' : 'w+')
    #TODO: store current methods
    #redefine methods
    redirect(lf)
    begin
      yield
    ensure
      lf.close
      #TODO: restore methods
    end
  end

  private
  def self.redirect(file)
    [$stdout, $stderr].each do |io|
      old_write = io.method(:write)
      class << io
        self
      end.module_eval do
        define_method(:write) do |text|
          old_write.call(text)
          file.write(text)
        end
      end
    end
  end
end

puts "start"
Tee::tee("logfile.log", true) {
  puts "Hello"
  printf "Hello again"
  system("ls -l")
}
puts "end"

···

--
Posted via http://www.ruby-forum.com/.

found a way

class Tee
  def self.tee(logfile, append = true, stdout = true, stderr = true)
    file = File.open(logfile, append ? 'a' : 'w+')
    stdout_write = nil
    stderr_write = nil
    stdout_write = decorate($stdout, :write) {|data| file.write(data)}
if stdout
    stderr_write = decorate($stderr, :write) {|data| file.write(data)}
if stderr
    begin
      yield if block_given?
    ensure
      restore($stdout, :write, stdout_write) if not stdout_write.nil?
      restore($stderr, :write, stderr_write) if not stderr_write.nil?
      file.close
    end
  end

  private
  def self.restore(obj, methodname, method)
    class << obj
      self
    end.module_eval do
      define_method(methodname, method)
    end
  end
  def self.decorate(obj, methodname)
    method = obj.method(methodname)
    class << obj
      self
    end.module_eval do
      define_method(methodname) do |data|
        method.call(data)
        yield data
      end
    end
    return method
  end
end

···

--
Posted via http://www.ruby-forum.com/.

However, what you have written doesn't handle your desired case of
redirecting system("ls"). This is because commands run by system()
inherit the ruby process's stdout/stderr file descriptors, and write
directly to them (not touching any Ruby objects)

Here's a possible solution, setting stdout and stderr to point to new
pipes which the ruby process consumes in the background.

class Tee
  def self.tee(logfile, append=true)
    file = File.open(logfile, append ? 'a' : 'w')
    stdout_old = $stdout.dup
    stderr_old = $stderr.dup
    #stdout_old.close_on_exec = true # only in ruby 1.9
    #stderr_old.close_on_exec = true # only in ruby 1.9
    stdout_r, stdout_w = IO.pipe
    stderr_r, stderr_w = IO.pipe
    t1 = Thread.new do
      while data = stdout_r.readpartial(1024) rescue nil
        stdout_old.write(data)
        file.write(data)
      end
      stdout_r.close
    end
    t2 = Thread.new do
      while data = stderr_r.readpartial(1024) rescue nil
        stderr_old.write(data)
        file.write(data)
      end
      stderr_r.close
    end
    begin
      $stdout.reopen(stdout_w)
      $stderr.reopen(stderr_w)
      yield
    ensure
      $stdout.reopen(stdout_old)
      $stderr.reopen(stderr_old)
      [stdout_w,stderr_w].each { |f| f.close }
      t1.join
      t2.join
      [stdout_old,stderr_old,file].each { |f| f.close }
    end
  end
end

puts "start"
Tee::tee("logfile.log", true) {
  puts "Hello"
  printf "Hello again"
  system("ls -l")
}
puts "end"

···

--
Posted via http://www.ruby-forum.com/.

Under OSX my version worked too - but not under windows.

Your version works with both - so I will use this.

Thanks for this.

Al

···

--
Posted via http://www.ruby-forum.com/.

You are right - I'm working with OSX 10.6.8 and develop my scripts in
eclipse with jruby.
With JRuby 1.6.8 my solution works.
With the native Ruby 1.8.7 it doesn't.

Your solution works with ruby, jruby, on windows and on osx.

Al

···

--
Posted via http://www.ruby-forum.com/.

Markus n/a wrote in post #1090973:

Under OSX my version worked too - but not under windows.

I tried yours under OSX (10.7.5) and it didn't work, and I can't see how
it possibly could - unless you are using a version of ruby with a
radically different version of the system() command than mine.

I tried both these rubies:

(system)
ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin11.0]

(homebrew)
ruby 1.9.3p362 (2012-12-25 revision 38607) [x86_64-darwin11.4.2]

···

--
Posted via http://www.ruby-forum.com/\.