众所周知,在Ruby中,类方法会被继承:

class P
  def self.mm; puts 'abc' end
end
class Q < P; end
Q.mm # works

然而,令我惊讶的是,它不适用于mixin:

module M
  def self.mm; puts 'mixin' end
end
class N; include M end
M.mm # works
N.mm # does not work!

我知道#extend方法可以做到这一点:

module X; def mm; puts 'extender' end end
Y = Class.new.extend X
X.mm # works

但我正在编写一个包含实例方法和类方法的mixin(或者更确切地说,我想编写):

module Common
  def self.class_method; puts "class method here" end
  def instance_method; puts "instance method here" end
end

现在我想做的是:

class A; include Common
  # custom part for A
end
class B; include Common
  # custom part for B
end

我想要A,B从Common个模块继承实例和类方法.但是,这当然行不通.那么,难道没有一种秘密的方法可以让这个继承从一个模块开始工作吗?

对我来说,把它分成两个不同的模块,一个要包含,另一个要扩展,这似乎不雅观.另一个可能的解决方案是使用Common类而不是模块.但这只是一个解决办法.(如果有两组通用功能Common1Common2,而我们真的需要mixin呢?)类方法继承在mixin中不起作用有什么深层次的原因吗?

推荐答案

一个常见的习惯用法是使用included个钩子并从那里注入类方法.

module Foo
  def self.included base
    base.send :include, InstanceMethods
    base.extend ClassMethods
  end

  module InstanceMethods
    def bar1
      'bar1'
    end
  end

  module ClassMethods
    def bar2
      'bar2'
    end
  end
end

class Test
  include Foo
end

Test.new.bar1 # => "bar1"
Test.bar2 # => "bar2"

Ruby相关问答推荐

为什么整数除法在许多脚本语言中舍入?

从命令行使用Bundle 器将 gem 添加到 gemfile

如何自定义 Jekyll 的 url?

为什么 RuboCop 建议用 Array.new 替换 .times.map?

Ruby数组中的`return`#map

如何在 macOS 上卸载 rbenv?

如何使用 Homebrew 更新 Ruby?

如何编写启动 tmux 会话的 shell 脚本,然后运行 ​​ruby​​ 脚本

找不到Bundle 命令 mac

获取字符串中的最后一个字符

如何声明 RSpec 中示例之间共享的变量?

当我将参数传递给脚本时,使用 gets() 会出现“没有这样的文件或目录”错误

类似查询的安全 ActiveRecord

Ruby总是Round Up

用元素填充数组 N 次

在 Ruby 中捕获异常后重新引发(相同的异常)

Ruby RVM apt-get 更新错误

退出(exit)和中止(abort)有什么区别?

如何在 Ruby 中使用“gets”和“gets.chomp”

如果 Ruby 不存在目录,则创建目录