如何暂时覆盖打印和放入课程?

时间:2013-07-01 06:32:27

标签: ruby ruby-2.0

我正在为我的项目开发一个shell。我希望Shell类在shell运行时覆盖所有打印函数,所以我已经这样做了:

# WARNING: Blocks until the user exits.
def start
  # Override Kernel print functions.
  master_print = Kernel.method :print
  master_puts = Kernel.method :puts

  Kernel.module_exec {
    define_method(:print) { |text = ""|
      self.send(:print_override, master_print, text)
    }
    define_method(:puts) { |text = ""|
      self.send(:puts_override, master_puts, text)
    }
    define_method(:puts_padded) { |text = ""|
      self.send(:puts_override, master_puts, "")
      self.send(:puts_override, master_puts, text)
      self.send(:puts_override, master_puts, "")
    }
  }

  # Readline loop and command parsing here...
end

只要Shell类只输出任何文本,但只要命令类尝试puts我就会得到这个:

NoMethodError: undefined method `puts_override' for #<AddressKit::CLI::Interactiv
e::Commands::LoadTable:0x000000027735b0>

我认为我上面写的print和puts块将保留在这个范围内,而不是执行它们碰巧调用的范围。有可能解决这个问题吗?


奖金问题:如何恢复原始打印功能?我曾计划过这个:

Kernel.module_exec {
  define_method(:print, &master_print)
  define_method(:puts, &master_puts)
  undef_method(:puts_padded)
}

但是我还没有尝试过,我不知道是否会像我发现的那样完全离开Kernel

2 个答案:

答案 0 :(得分:5)

虽然块确实保留了范围,但self是特殊的并且是指运行该方法的实例(我假设因为这更有用)。

另一种策略是使用Kernel#puts只做$stdout.puts的事实,因此,不要覆盖puts,而是将$stdout设置为您的某个类,在将值传递给前$ stdout上的puts之前按下这些值。

完成后,将$stdout恢复为原始值。

答案 1 :(得分:0)

嗯,我想我已经解决了这个问题,但我不确定为什么会这样。我猜测这些块既在调用它的范围内,也在它们定义的范围内,但在前者上具有更高的优先级。这意味着我不能使用self并期望它指向shell。相反,我这样做了:

shell = self

Kernel.module_exec {
  define_method(:print) { |text = ""|
    shell.send(:print_override, master_print, text)
  }
  define_method(:puts) { |text = ""|
    shell.send(:puts_override, master_puts, text)
  }
  define_method(:puts_padded) { |text = ""|
    shell.send(:puts_override, master_puts, "")
    shell.send(:puts_override, master_puts, text)
    shell.send(:puts_override, master_puts, "")
  }
}

如果变量shell存在于与printputs相同的范围内,那么它会完美但但(如果我是对的话)会中断。但它没有破坏,我测试了它!我真的不知道发生了什么事情,如果有人能解释一下我会非常感激: - )