为什么在class_eval本身就足够的时候使用include模块

时间:2010-08-09 22:16:18

标签: ruby-on-rails ruby

在下面的代码中使用include模块。如果删除了包含模块的方式,那么也会创建一个实例方法。那么为什么用户包含模块?

http://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations.rb#L1416

  include Module.new {
          class_eval <<-RUBY, __FILE__, __LINE__ + 1
            def destroy                     # def destroy
              super                         #   super
              #{reflection.name}.clear      #   posts.clear
            end                             # end
          RUBY
        }

3 个答案:

答案 0 :(得分:12)

首先让我们说清楚一件事。当他们在super内拨打class_eval时,它与他们使用include Module.new {}事物的原因完全无关。事实上,在super方法中调用的destroy与回答您的问题完全无关。在destroy方法中可能有任意代码。

现在我们已经解决了问题,这就是正在发生的事情。

在ruby中,如果您只是定义一个类方法,然后在同一个类中再次定义它,那么您将无法调用super来访问上一个方法。

例如:

class Foo
  def foo
    'foo'
  end

  def foo
    super + 'bar'
  end
end

Foo.new.foo # => NoMethodError: super: no superclass method `foo' for #<Foo:0x101358098>

这是有道理的,因为第一个foo没有在某个超类中定义,或者在查找链的任何地方定义(super指向的位置)。但是,您可以定义第一个foo,以便以后覆盖它时 - 通过调用super可以使用它。这正是他们想要用模块包含的目的。

class Foo
  include Module.new { class_eval "def foo; 'foo' end" }

  def foo
    super + 'bar'
  end
end

Foo.new.foo # => "foobar"

这很有效,因为当你包含一个模块时,ruby会将它插入到查找链中。这样,您可以随后在第二种方法中调用super,并期望调用包含的方法。大。

然而,您可能想知道,为什么不简单地包含一个没有所有技巧的模块?他们为什么使用块语法?我们知道上面的示例完全等同于以下内容:

module A
  def foo
    'foo'
  end
end

class Foo
  include A

  def foo
    super + 'bar'
  end
end

Foo.new.foo # => "foobar"

那他们为什么不这样做呢?答案是 - 致电reflection。他们需要捕获当前上下文中可用的变量(或方法),即reflection

由于他们使用块语法定义新模块,因此块内的所有变量都可用于块内。方便。

只是为了说明。

class Foo
  def self.add_foo_to_lookup_chain_which_returns(something)
    # notice how I can use variable something in the class_eval string
    include Module.new { class_eval "def foo; '#{something}' end" }
  end
end

# so somewhere else I can do

class Foo
  add_foo_to_lookup_chain_which_returns("hello")

  def foo
    super + " world"
  end
end

Foo.new.foo # => "hello world"

干净,呵呵?

现在让我再强调一下。在您的示例中,super方法内的destroy调用与上述任何内容无关。他们出于自己的原因调用它,因为可能发生这种情况的类正在继承另一个已定义destroy的类。

我希望这说清楚。

答案 1 :(得分:0)

我猜测但是......他们不想覆盖“破坏”方法,并希望让某些最终用户(你或我)重载它,而不会删除这个“反射” .clear“功能。

所以 - 通过将它包含为模块,他们可以调用“super”来调用原始的destroy 重载的版本(由最终用户编写)。

答案 2 :(得分:0)

感谢includedestroy方法未被覆盖。它落在实际类派生的幽灵类中。这样,当一个人在AR对象上调用destroy时,将调用原始的一个,super将从匿名模块调用一个(稍后将调用原始 {{1}从它派生的类开始。)

确实有点棘手。