文法2

前回の方法でinclude先のクラスで定義されたメソッドを上書きする方法が判った。
あるクラスの親クラスで既に定義されているメソッドを上書きするには普通にモジュールをincludeするだけで良い。
さて、上書きされるメソッドをこの二つの事例ともに同じ名前で呼ぶにはどうしたら良いのだろう。

module Mod
  def foo()
    return "Mod"+super()
    //super()ではなくfoo_old()と呼びたい。
  end
  def bar()
    return "Mod"+bar_old()
    //またはbar_oldではなくsuper()と呼びたい。
  end
  def included(klass)
    klass.__send__(:alias_method, :bar_old, :bar)
    klass.__send__(:remove_method, :bar)
  end
end

判りやすい解は、includeする前にalias_methodして必要ならばremove_methodもする、というものだろう。

module Mod
  def foo()
    return "Mod"+foo_old()
  end
  def self.before_include(klass)
    klass.__send__(:alias_method, :foo_old, :foo)
    if (klass.instance_methods(false).include?("foo")) then
      klass.__send__(:remove_method, :foo)
    end
  end
end

しかし、必ずbefore_include()を呼ばなければならないというのが面倒だ。
黒魔術的にはModule#include()を改造すると良さそう。

class Class
  alias :include_old :include
  def include(mod)
    mod.before_include(self) if (mod.singleton_methods().include?(:before_include))
    include_old(mod)
  end
end

今度は黒魔術なライブラリ同士でinclude_oldという名前が衝突しないかどうかという心配がある。
rubyには、lisp系のgensymのような、絶対に再使用されないシンボルを生成する方法がないので絶対安全な回避法は存在しないと思われる。
ここではfoo_oldやinclude_old、before_includeと書いたようなシンボルをファイル名などから生成することで運用レベルで回避するのが良いのではないか。

      • -

今気付いたのだが、数字で始まるなど、文字列.to_sym()でしか生成できないシンボルを使うと比較的安全に出来そうだ。
具体的には

class Gensym
  def initialize()
    @index = 0
  end
  def []()
    @index += 1
    return (@index.to_s()+self.to_s()).to_sym()
  end
  @@instance = self.new()
  def self.[]()
    return @@instance[]
  end
end

p Gensym[]
p Gensym[]
p Gensym[]

といった感じか。