【问题标题】:why use include module when class_eval by itself would suffice为什么当 class_eval 本身就足够时使用包含模块
【发布时间】:2010-08-09 22:16:17
【问题描述】:

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

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
        }

【问题讨论】:

  • 也许 ping Rails 核心人员?

标签: ruby-on-rails ruby


【解决方案1】:

首先让我们澄清一件事。当他们在class_eval 中调用super 时——这与他们使用include Module.new {} 的原因完全无关。实际上,在 destroy 方法中调用的 super 与回答您的问题完全无关。该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"

整洁,嗯?

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

我希望这能说清楚。

【讨论】:

    【解决方案2】:

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

    所以 - 通过将它作为一个模块包含在内,他们可以调用“super”,这将调用原始销毁 重载版本(由最终用户编写)。

    【讨论】:

      【解决方案3】:

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

      确实有点棘手。

      【讨论】:

        猜你喜欢
        • 2020-05-19
        • 2011-03-08
        • 2018-02-05
        • 2023-04-06
        • 1970-01-01
        • 2017-05-26
        • 2016-07-28
        • 1970-01-01
        • 2012-05-23
        相关资源
        最近更新 更多