【问题标题】:Is it possible to mix-in a module method?是否可以混合模块方法?
【发布时间】:2020-05-02 19:24:36
【问题描述】:

假设我有一个声明模块方法的模块(不是实例方法):

module M
  def self.foo
    puts 'foo'
  end
end

现在假设我想将M.foo 混合到另一个类C 中,这样C.foo 就被定义了。

最后,我想这样做不改变M.foo 的定义方式 并且不只是在C 中创建一个调用M.foo 的方法。 (即将foo重写为实例方法不算数。使用module_function也不算数。)

这在 Ruby 中是不可能的吗?

【问题讨论】:

  • 没有。如果您的意图是在类C 中混合成为类方法的方法,则必须使模块中的方法成为实例方法并使用C.extend M(或C.singleton_class.include M)。 (见Object#extend)。要使用模块方法,您必须调用定义它的模块上的方法,模块Math 的方法就是一个例子。
  • @CarySwoveland 完美答案!谢谢。
  • 作为学术练习,您可以将M 上的所有类方法复制到实例方法中,但最好只修改M 以首先将定义作为实例方法并可能@ 987654337@如果需要(或使用module_method

标签: ruby module metaprogramming mixins composition


【解决方案1】:

我想这样做不改变M.foo 的定义方式

很遗憾,这是不可能的。 Ruby 只允许包含模块,而不是类。 foo 是在 M 的单例类上定义的,一个类。因此,您不能include 它。同样的限制适用于extend。尝试这样做会导致TypeError

module M
  def self.foo
    puts 'foo'
  end
end

class C
  extend M.singleton_class # TypeError: wrong argument type Class (expected Module)
end

但是,您可以通过将 foo 定义为单独模块中的实例方法来实现您想要的,然后可以通过 extend 将其混合到 MC 中:(该模块没有嵌套在M)

module M
  module SingletonMethods
    def foo
      puts 'foo'
    end
  end

  extend SingletonMethods     # <- this makes foo available as M.foo
end

class C
  extend M::SingletonMethods  # <- this makes foo available as C.foo
end

或者使用 Ruby 的 included 回调使用一些元编程魔法:

module M
  module SingletonMethods
    def foo
      puts 'foo'
    end
  end

  extend SingletonMethods

  def self.included(mod)
    mod.extend(SingletonMethods)
  end
end

class C
  include M
end

这是 ActiveSupport::Concern 在 Rails 中工作方式的简化版本。

【讨论】:

  • 一个不错的简单答案,谢谢。这基本上是我在阅读 MRI 源后得出的结论——很高兴在这里以“官方”形式记录它。
  • @pje 你的问题启发了我问这个问题:stackoverflow.com/q/59766403/477037
  • 多么有趣的问题啊!我以前从没想过。
猜你喜欢
  • 2020-11-06
  • 1970-01-01
  • 2012-08-04
  • 1970-01-01
  • 2020-10-13
  • 2016-02-22
  • 1970-01-01
  • 2014-04-18
  • 2011-06-09
相关资源
最近更新 更多