是否可以在Ruby模块中覆盖#initialize?

问题描述 投票:10回答:4

我一直在尝试找出如何从模块扩展initialize的行为。我想做到这一点而无需在要混入的类的initialize中调用super。我想支持调用include的正常模式,但无法弄清楚。我已经阅读了有关此事的所有内容,尽管人们有建议,但实际上却没有一个建议(至少在我手中)。

这是我(认为)我所知道的:

  • 如果完全可以完成,则必须使用include(即Module.included(base))上的钩子来完成。
  • include钩子将在包含类定义initialize之前执行,因此没有必要尝试简单地尝试用initialize定义base.instance_eval,因为它将被覆盖。
  • 有人建议利用method_added钩子并在那里处理它。这就是我现在正在尝试的方法,但是似乎该钩子在方法定义的开头执行,所以您最终看到下面的内容。

    module Mo
      def self.included(klass)
        klass.instance_eval do
          def method_added(method)
            puts "Starting creation of #{method} for #{self.name}"
            case method
            when :initialize
              alias_method :original_initialize, :initialize
              puts "About to define initialize in Mo"
              def initialize
                original_initialize
                puts "Hello from Mo#initialize"
              end
              puts "Finished defining initialize in Mo"
            end
            puts "Finishing creation of #{method} for #{self.name}"
          end
        end
      end
    end
    
    class Foo
      include Mo
      def initialize
        puts "Hello from Foo#initialize"
      end
    end
    
    foo = Foo.new
    

这将产生以下输出:

    Starting creation of initialize for Foo
    Starting creation of original_initialize for Foo
    Finishing creation of original_initialize for Foo
    About to define initialize in Mo
    Finished defining initialize in Mo
    Finishing creation of initialize for Foo
    Hello from Foo#initialize

在我看来,Foo类中的initialize仍在覆盖模块中的定义。我猜这是因为定义仍然是开放的,这表明“获胜”不是最后开始的块是最后完成的块。

如果外面有人真的知道如何做到这一点并使其起作用,请启发我。

FWIW,是的,我想我有充分的理由要这样做。

我一直在尝试找出如何从模块扩展初始化行为。我想做到这一点而无需在正在混合的类的initialize中调用super。我想支持...

ruby
4个回答
30
投票

[如果您使用的是Ruby 2.0或更高版本,则可以只使用prepend。要求用户使用prepend而不是include,或执行以下操作:


6
投票

好吧,在Ruby 1.9中,您可以向new类方法中添加功能...


1
投票

[如果使用Rails


0
投票

在Foo的Initialize中有条件调用仅在存在的情况下才调用它会满足吗?

© www.soinside.com 2019 - 2024. All rights reserved.