首页 > 解决方案 > 有没有办法重新定义块内的方法?

问题描述

假设我想要puts大量的大写字符串,但为简洁起见,我想写puts(string)而不是puts(string.upcase)每次都写。

有没有办法让我puts 只在一个块内重新定义,像这样?

# This will puts as normal
puts "abc"
# => abc

# This will puts an upcased string, but it's too verbose
puts "abc".upcase
# => ABC

# I want to do something like this, which will override the puts method for code run within the block
def always_upcase_strings(&block)
  def puts(string)
    super(string.upcase)
  end
end

always_upcase_strings do 
  puts "abc"
  puts "def"
  puts "ghi"
end
puts "xyz"
# => ABC
# => DEF
# => GHI
# => xyz

我上面的例子是简化的——我不是puts在我的例子中使用,而是我自己写的一个方法。

标签: ruby

解决方案


想一想,这里有一个更好的答案:

def always_upcase_strings(&block)
  anon_class = Class.new do
    def puts(str)
      super(str.upcase)
    end
  end
  anon_class.new.instance_eval(&block)
end

always_upcase_strings do
  puts "abc" #=> "ABC"
  puts "def" #=> "DEF"
  puts "ghi" #=> "GHI"
end

puts "xyz"   #=> "xyz"

这将创建一个具有所需方法覆盖的临时类(匿名,因为它不需要名称)。

然后在此类实例的上下文中调用产生的块。

除了比我的“redefine-undefine-redefine the method”解决方案更容易混淆之外,这种方法还具有线程安全的额外优势。因此,例如,如果在调用方法时运行并行测试,您将不会出现奇怪的行为。

...但我仍然坚持我最初的说法,即在一个块中重新定义方法是非常令人惊讶的行为。同事可能不喜欢您选择该设计模式(除非它以有限的方式完成,并且有充分的理由!)


推荐阅读