【问题标题】:Ruby refinements gotchasRuby 改进的陷阱
【发布时间】:2017-10-07 18:30:19
【问题描述】:

在第 2 章的“细化”部分的Metaprogramming Ruby 2 中,我发现了以下一段 Ruby 代码:

class MyClass 
  def my_method
    "original my_method()"
  end

  def another_method 
    my_method
  end

end

module MyClassRefinement 
  refine MyClass do
    def my_method
      "refined my_method()"
    end 
  end
end

using MyClassRefinement
MyClass.new.my_method # => "refined my_method()"
MyClass.new.another_method # => "original my_method()" - How is this possible?

据作者介绍:

但是,拨打another_method 可能会让你措手不及:即使你在using 之后调用another_method,对my_method 的调用本身也会发生在using 之前——因此它调用了该方法的原始、未改进的版本。

这完全把我绊倒了。

为什么MyClass.new.another_method 打印“original my_method()”,因为它在using MyClassRefinement 之后使用,作者在这里想说什么?

谁能提供更直观/更好的解释?

谢谢。

【问题讨论】:

  • 这可能是因为细化引入的重新映射仅适用于特定范围,并且在原始定义中该范围不受影响。

标签: ruby metaprogramming refinements


【解决方案1】:

我能找到的最好的解释来自the docs

细化在范围上是词法的。细化仅在调用using 后的范围内有效。 using 语句之前的任何代码都不会激活优化。

这意味着您的优化方法必须在调用using 之后的某个地方调用。重要的是方法调用的实际位置,而不是方法被调用的方式或方法被调用的位置。


这就是发生的事情。

  1. usingusing MyClassRefinement 激活my_method 细化。
  2. MyClass.new.my_method 被执行。
  3. method lookup 从调用的确切点开始:

在为class C Ruby 检查的实例查找方法时:

  • 如果为C 激活了优化,则以相反的顺序激活它们
    • C 改进后的前置模块
    • C 的细化
    • C 改进中包含的模块
  • C 的前置模块
  • C
  • C包含的模块
  1. 细化处于活动状态my_method 从细化 "refined my_method()" 返回代码
  2. MyClass.new.another_method 被执行。
  3. method lookup 从调用的确切点开始。​​
  4. 细化在这个调用点处于活动状态,但 another_method 不是细化,因此 Ruby 在 MyClass 类中查找 another_method 并找到它。
  5. 在类方法another_method内部,找到并调用了方法my_method
  6. method lookup 从调用的确切点开始。​​
  7. 在调用点没有激活细化,因为没有调用using 行上(即物理上之前)my_method 被调用。 Ruby 继续在 MyClass 类中查找 my_method 并找到它。
  8. my_method 从类方法 "original my_method()" 返回代码。

我们可以做一个简单的比较。假设我有一个独立的file.rb,代码如下:

puts puppy
puppy = 'waggle'

puppy 在定义之前不能使用。该变量是词法范围的,其使用取决于其定义在隔离的file.rb 中的位置。

类似地,refinement 只有在通过using 激活前一行(或源代码文件中物理上之前的某个位置)后才能调用。细化为lexically scoped

From Wikipedia

在具有词法范围(也称为静态范围)的语言中,名称解析取决于源代码中的位置和词法上下文 , 由定义命名变量或函数的位置定义...

词法解析可以在编译时确定,也称为早期绑定,而动态解析一般只能在运行时确定,因此称为后期绑定


this article 的最后一部分讨论了您的具体问题。作者还解释了using 语句在文件中的物理位置如何确定细化是否处于活动状态。

【讨论】:

  • 谢谢!现在完全有道理!词法作用域是真正的罪魁祸首。
  • ...您的参考资料帮助很大!
  • 没问题 :) 我很高兴它有帮助
  • @kstratis:请记住,改进的全部意义是它们提供了猴子补丁而不影响其他代码。这正是您在这里看到的。你是猴子修补my_method 而不影响another_method
猜你喜欢
  • 2010-09-22
  • 2012-05-10
  • 2011-03-27
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多