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

Nic*_*ilt 7 ruby ruby-on-rails

在下面的代码中使用了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
        }
Run Code Online (Sandbox Code Playgroud)

Max*_*yak 12

首先让我们说清楚一件事.当他们super在里面打电话时class_eval- 它与他们使用include Module.new {}东西的原因完全无关.实际上,superdestroy方法中调用的内容与回答问题完全无关.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>
Run Code Online (Sandbox Code Playgroud)

这是有道理的,因为第一个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"
Run Code Online (Sandbox Code Playgroud)

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

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

module A
  def foo
    'foo'
  end
end

class Foo
  include A

  def foo
    super + 'bar'
  end
end

Foo.new.foo # => "foobar"
Run Code Online (Sandbox Code Playgroud)

那他们为什么不这样做呢?答案是 - 致电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"
Run Code Online (Sandbox Code Playgroud)

整洁,对吧?

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

我希望这说清楚.