文法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[]
といった感じか。