我正在为我的项目开发一个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
。
答案 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
存在于与print
或puts
相同的范围内,那么它会完美但但(如果我是对的话)会中断。但它没有破坏,我测试了它!我真的不知道发生了什么事情,如果有人能解释一下我会非常感激: - )