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

时间:2013-07-05 22:46:45

标签: ruby

我一直试图弄清楚如何从模块中扩展initialize的行为。我想这样做,而不是在混合的类的initialize中调用super。我想支持调用include的正常模式我无法理解。我已经阅读了我能在这件事上找到的所有内容,虽然有人提出建议,但实际上似乎没有任何建议(至少在我手中)。

以下是我认为的事情:

  • 如果可以完成,则必须使用include上的挂钩(即Module.included(base))来完成。
  • include挂钩将在包含类定义initialize之前执行,因此无需简单地尝试使用initialize定义base.instance_eval,因为它会被覆盖。< / LI>
  • 建议使用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,是的,我认为我有充分的理由想要这样做。

4 个答案:

答案 0 :(得分:28)

如果您使用的是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

答案 1 :(得分: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

返回......

Hello from Mo
Hello from Foo

答案 2 :(得分: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

答案 3 :(得分: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

如果有兴趣,您也可以阅读this文章