Ruby元编程(二)

参考书籍《Ruby元编程(第二版)》 Ruby版本: 书上使用的是2.x,自己使用的3.1.2

关于self和私有方法调用之间的问题

Ruby2.7之前版本

在Ruby 2.7之前,可以使用self调用一个私有写入/赋值方法,但是使用self调用任何其他私有方法将抛出NoMethodError。

示例如下:

class Counter
  def reset
    self.count = 0 # 允许调用
    puts self.count # 抛出异常
  end

  private
  attr_accessor :count
end

Counter.new.reset

上面的示例执行self.count = 0语句时,不会出现问题,但是执行到puts self.count语句时就会抛出NoMethodError错误。这种前后不一致的行为让人感到十分困惑。

Ruby2.7及以后版本

在Ruby 2.7版本,Ruby修复这种让人感到困惑的不一致行为。换而言之,从Ruby 2.7开始,可以使用self调用任何私有方法,就像其他语言那样。

class Counter
  def reset
    self.count = 0 # 允许调用
    puts self.count # 允许调用
  end
  private
  attr_accessor :count
end
Counter.new.reset

细化(Refine)

使用打开类的方式扩展代码,有一个很明显的问题:修改时全局性的,很有可能无意中影响其他代码的正常使用。但通过细化的方式可以使修改只作用于局部,很好的解决了之前的问题。

细化的定义方式

使用细化,首先需要先定一个模块,然后在这个模块的定义中调用refine方法。

module StringExtentions
  refine String do
    def something
      "something..."
    end
  end
end

通过using让细化生效

在上述代码中,为String类添加了一个something方法,但细化在默认情况下并不生效,需要使用using让修改生效。

module StringExtentions
  refine String do
    def something
      "something..."
    end
  end
end

# 在当前文件内生效
using StringExtentions
"My Something".something #=> something...

从Ruby 2.1开始,可以在一个模块内调用using方法,此时细化的作用范围仅限于当前模块。

module StringExtentions
  refine String do
    # 为String类修改了reverse方法的定义
    def reverse 
      "My reverse method..."
    end
  end
end

module StringStuff
  # 在StringStuff模块内生效
  using StringExtentions
  "test".reverse #=> "My reverse method..."
end

# 出StringStuff模块失效
"test".reverse #=> "tset"