11

我一直在试图弄清楚如何扩展initialize模块的行为。我想在不调用initialize被混入的类的 super 的情况下做到这一点。我想支持include我无法弄清楚的正常调用模式。我已经阅读了我能找到的关于这件事的所有内容,虽然人们有建议,但它们似乎都没有真正起作用(至少在我手中)。

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

  • 如果它可以完成,则必须使用include(ie 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
    

这将产生以下输出:

    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

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

如果有人真的知道如何做到这一点并让它发挥作用,请赐教。

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

4

4 回答 4

30

如果您使用的是 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
于 2013-07-05T23:22:28.800 回答
7

好的,在 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

那返回...

Hello from Mo
Hello from Foo
于 2013-07-07T03:28:04.457 回答
0

在 Foo 的初始化中进行条件调用是否满足,如果它存在则只调用包含的方法?

module Mo
  def initialize_extra
    puts "Hello from Mo"
  end
end

class Foo
  include Mo
  def initialize
    if defined? initialize_extra
      initialize_extra
    else
      puts "Hello from Foo"
    end
  end
end

x = Foo.new
于 2013-07-05T23:08:34.720 回答
0

如果使用 Rails,你也可以ActiveSupport::Concern这样做:

module M
  extend ActiveSupport::Concern

  included do
    attr_reader :some_reader
  end

  def initialize
    puts 'module initialize'
  end
end

class Foo
  include M

  def initialize
    super
    puts 'class initialize'
  end
end

调用Foo.new会输出

module initialize
class initialize

有兴趣的也可以看看这篇文章

于 2020-05-09T10:20:43.330 回答