Ruby 包含任何模块时的回调

Ruby callback when any module is included

我知道我们可以为任何单独的模块定义 included 回调。

有什么方法可以定义每当 任何 模块被包含在另一个模块或 class 中时调用的回调?然后回调最好可以访问包含的模块和包含它的 class/module。

我无法思考或找到 Ruby 中的内置方法来做到这一点。

一种替代方法是直接对 Module class 进行猴子修补以创建回调。为此,我们可以在原始方法 includeextend 周围添加一些包装器方法,以在每次调用 includeextend 方法时强制执行我们定义的回调。

以下几行内容应该有效:

class Module

  def self.before
    m_include = instance_method(:include)
    m_extend = instance_method(:extend)

    define_method(:include) do |*args, &block|
      included_callback(args[0])
      m_include.bind(self).call(*args, &block)
    end

    define_method(:extend) do |*args, &block|
      extend_callback(args[0])
      m_extend.bind(self).call(*args, &block)
    end
  end

  def included_callback(mod_name)
    puts "#{self} now has included Module #{mod_name}"
  end

  def extend_callback(mod_name)
    puts "#{self} now has extended Module #{mod_name}"
  end

  before
end

一个测试它是否有效的例子:

module Awesome

  def bar
    puts "bar"
  end

  def self.baz
    puts "baz"
  end
end

class TestIncludeAwesome
  include Awesome
end

class TestExtendAwesome
  extend Awesome
end

示例代码应输出以下内容:

> TestIncludeAwesome now has included Module Awesome
> TestExtendAwesome now has extended Module Awesome
class D
  def self.callback(mod)
    include mod
  end
end

module M
  def hello
    puts 'hi'
  end

  def self.included(klass)
    D.callback(self) unless klass == D
  end
end

class C
  include M
end

C.new.hello #=> 'hi'
D.new.hello #=> 'hi'

C包含M时,M::includedklass#=>C一起执行。由于 klass == Dfalse,因此执行 D.callback(M)callback在classD中包含M,然后M::includedklass#=>D一起执行。由于 klass == D 现在为真,因此 M 不会再次包含在 D 中。