1. ホーム
  2. ruby

[解決済み] Rubyのモジュール/ミキシンからクラスメソッドを継承する

2022-12-22 13:11:27

質問

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 モジュールに継承させたいのです。しかし、もちろん、それはうまくいきません。そこで、この継承を1つのモジュールから動作させる秘密の方法はないでしょうか?

これを2つの異なるモジュールに分割して、一方はインクルード、もう一方はエクステンションにするのは、私には非礼に思えます。もうひとつの可能な解決策は、クラス Common を使用することです。しかし、これは単なる回避策に過ぎません。(共通の機能として2つのセットがある場合はどうでしょう 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"