2

我正在为我的项目开发一个外壳。我希望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完全像我发现的那样。

4

2 回答 2

5

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

Kernel#puts另一种策略是使用just does的事实,$stdout.puts而不是覆盖puts,设置$stdout为您的一个类,它可以在将值传递给puts前一个 $stdout 之前对其进行处理。

完成后,恢复$stdout到原来的值。

于 2013-07-01T07:02:26.863 回答
0

好吧,我想我解决了这个问题,但我不确定它为什么会起作用。我这些块既在调用它的范围内,也在它们定义的范围内,但前者的优先级更高。这意味着我不能使用self并期望它指向外壳。相反,我这样做了:

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

于 2013-07-01T06:52:43.973 回答