Ruby

【Ruby】モジュール動作メモ

今回はメタプログラミングを読んでいて、試しに自分のローカルで動かしてみたので、それをメモしていこうと思います。

includeとprepend

module M1
  def my_method
    'M1#my_method()'
  end
end

module M2
  def my_method
    'M2#my_method()'
  end
end

class C
  prepend M1
  include M1 # 無視される
  include M2

  def my_method
    'C#my_method'
  end
end

class D < C; end

p '-------------------'
p D.ancestors => [D, M1, C, M2, Object, Kernel, BasicObject]
p C.ancestors => [M1, C, M2, Object, Kernel, BasicObject]
p C.new.my_method => "M1#my_method()"

モジュールM1とM2をそれぞれprepend、includeしたクラスCのインスタンスからmy_methodを実行するとprependしたモジュールM1のmy_methodが呼ばれた。継承チェーンの中でprependされたメソッドはCクラスのメソッドより下にくるため、([M1, C, M2, Object, Kernel, BasicObject]) Cクラスのメソッドより先に見つかるため、M1のメソッドが呼ばれたのだ。

当然prepend M1をコメントアウトすると、Cクラスのmy_methodが呼ばれる。

# 略
class C
  prepend M1
  include M1 # 無視される
  include M2
end
# 略

p C.new.my_method => "C#my_method()"

また、同じモジュールを2回 prependまたは includeしても2回目の記述は無視される。

refine

Refinementはオープンクラスの問題点であったグローバルに適用されて、気づかぬうちにメソッドを上書きしてしまうなどの問題が発生しない。

module StringExtensions
  refine String do
    def reverse
      "esrever"
    end
  end
end

module StringStuff
  p 'my_string'.reverse
  using StringExtensions
  p 'my_string'.reverse
end

=> "gnirts_ym"
=> "esrever"

Refinementsが有効になるのはrefineブロックとusingを呼び出したところからモジュールの終わりまでである。トップレベルでusingがいる場合はファイルの終わりまで有効である。

Refinementsが有効になっている限定されたスコープの中では、オープンクラスと同じでメソッドの再定義や追加ができる。

オーバーライドしているモジュール内ではrefineは無効である。

# 先程の続き
mudule StringStuff
  p 'my_string'.reverse
end

=> "gnirts_ym"

また、少々謎ではあるが、

class MyClass
  def my_method
    p 'original my_method'
  end

  def another_method
    my_method
  end
end

module MyClassRefinements
  refine MyClass do
    def my_method
      p 'refined my_method'
    end
  end
end 

using MyClassRefinements
MyClass.new.my_method => "refined my_method"
MyClass.new.another_method => "original my_method"

using MyClassRefinementsの後にanother_methodを呼び出しているのに結果はoriginal my_methodが呼ばれている。個々の部分はメタプログラミングの説明を呼んでもよく分からなかった。

さいごに

Rubyというかプログラミングっておもしろい。コツコツやっていきます。

ABOUT ME
sakai
東京在住の30歳。元々は車部品メーカーで働いていてましたが、プログラミングに興味を持ちスクールに通ってエンジニアになりました。 そこからベンチャー → メガベンチャー → 個人事業主になりました。 最近は生成 AI 関連の業務を中心にやっています。 ヒカルチャンネル(Youtube)とワンピースが大好きです!