TDR-now's Tech Blog

仕事で Ruby を使っているエンジニアのアウトプット用 Blog

【Ruby】Module#includeとは

Module#includeとは

  • Module#includeとは、Rubyの組み込みライブラリに含まれるModuleクラスのインスタンスメソッド
  • 引数で指定したモジュールの定義 (メソッド、定数) を引き継ぐことができる
  • 多重継承の代わりに用いられておりmix-inとも呼ばれている
  • モジュールの機能追加は、クラスの継承関係の間にそのモジュールが挿入されることで実現されている
  • そのため、メソッド探索などはスーパークラスよりもインクルードされたモジュールのほうが先に行われる
module M
  def hoge
    puts "M#hoge"
  end
end

class C1; end

class C2 < C1
  include M
end

# 指定されたモジュールの定義 (メソッド、定数) を引き継ぐことができる
C2.new.hoge
# => M#hoge

# モジュールの機能追加は、クラスの継承関係の間にそのモジュールが挿入されることで実現されている
p C2.ancestors 
# => [C2, M, C1, Object, Kernel, BasicObject]
module M
  def hoge
    puts "M#hoge"
  end
end

class C1
  def hoge
    puts "C1#hoge"
  end
end

class C2 < C1
  include M
end

# メソッド探索などはスーパークラスよりもインクルードされたモジュールのほうが先に行われる
C2.new.hoge
# => M#hoge

引数について

  • 引数は可変長引数なので配列として受け取られるため複数モジュール指定可能

include(*mod) -> self

  • 引数に複数のモジュールを指定した場合、最後の引数から順にインクルードする
  • 同じモジュールを二回以上includeした場合、二回目以降は無視される
class C2 < C1
  include M1, M2
end

p C2.ancestors
# => [C2, M1, M2, C1, Object, Kernel, BasicObject]

Module#includeの実態

  • Module#includeの実態はModuleクラスのappend_featuresインスタンスメソッド
  • includeメソッドはRubyで書くと以下のように定義できる
    • Module#append_features → モジュール(またはクラス)に self の機能を追加する
    • Module#included → self が include されたときに対象のクラスまたはモジュールを引数にしてインタプリタがこのメソッドを呼び出す(フックメソッド)
def include(*modules)
  modules.reverse_each do |mod|
    # append_featuresやincluded はプライベートメソッドなので
    # 直接 mod.append_features(self) とは書けない
    mod.__send__(:append_features, self)
    mod.__send__(:included, self)
  end
end

参考:https://docs.ruby-lang.org/ja/latest/method/Module/i/append_features.html

  • Module#includedメソッドはフックメソッドのため通常はメソッドの中身はなく、オーバーライドして使うもの
  • 一方で、Module#append_featuresはメソッドは、実際に継承チェーンにモジュールを追加する