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

Hul*_*iax 10 ruby

我一直在试图弄清楚如何扩展initialize模块的行为.我想这样做而不会调用initialize正在混入的类中的超级.我想支持正常的呼叫模式,include我无法弄明白.我已经阅读了我能找到的关于这个问题的所有内容,虽然有人提出建议,但实际上它们似乎都没有(至少在我手中).

这是我(我想)我知道的:

  • 如果可以完成,则必须使用挂钩include(即Module.included(base))来完成.
  • include钩子将执行之前,包括类定义initialize,所以没有点无非是想确定initializebase.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
    
    Run Code Online (Sandbox Code Playgroud)

这导致以下输出:

    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
Run Code Online (Sandbox Code Playgroud)

在我看来,initializeFoo类仍然会覆盖模块中的定义.我猜这是因为定义仍然是开放的,这表明不是最后一个块启动的问题,最后是"胜利".

如果有人真的知道如何做到这一点并让它工作,请启发我.

FWIW,是的,我认为我有充分的理由想要这样做.

Chu*_*uck 29

如果您使用的是Ruby 2.0或更高版本,则可以使用prepend.要么需要用户prepend,而不是include,或做:

module Mo
  module Initializer
    def initialize
      puts "Hello from Mo#initialize"
      super
    end
  end

  def self.included(klass)
    klass.send :prepend, Initializer
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 使用“klass.send :prepend”而不是简单的“klass.prepend”时,我是否遗漏了任何明显的东西? (2认同)

Ste*_*zyn 6

好的,在Ruby 1.9中你可以为new类方法添加功能......

module Mo
  def new(*var)
    additional_initialize(*var)
    super(*var)
  end
  def additional_initialize(*var)
    puts "Hello from Mo"
  end
 end

class Foo
  extend Mo
  def initialize
    puts "Hello from Foo"
  end
end

foo = Foo.new
Run Code Online (Sandbox Code Playgroud)

那回来......

Hello from Mo
Hello from Foo
Run Code Online (Sandbox Code Playgroud)