Ruby、モジュール、クラス、またはオブジェクトにメソッドを追加する
概要
グローバルに利用できるようにするためにカーネルにメソッドを追加しているときに、奇妙なことに気づきました。興味深いので、ドキュメントやわかりやすい説明を探しています。
コードを見てみましょう:
ファイル: ./demo.rb
# example 1
module Kernel
def foo
puts "I'm defined inside the module!"
end
end
# example 2
module Bar
def bar
puts "I'm included! (bar)"
end
end
Kernel.send :include, Bar
# example 3
module Baz
def baz
puts "I'm included! (baz)"
end
end
module Kernel
include Baz
end
次に、bash と IRB で
$ irb -r ./demo.rb
> foo
# I'm defined inside the module!
> bar
# NameError: undefined local variable or method `bar' for main:Object
> baz
# NameError: undefined local variable or method `baz' for main:Object
>
> self.class.ancestors
# => [Object, Kernel, BasicObject]
>
> include Kernel
>
> self.class.ancestors
# => [Object, Kernel, Baz, Bar, BasicObject]
>
> foo
# I'm defined inside the module!
> bar
# I'm included! (bar)
> baz
# I'm included! (baz)
foo は期待どおりに機能し、カーネルを含むすべてのオブジェクトで使用できます。一方、bar と baz はすぐには利用できません。 これは、IRB (オブジェクト) の評価コンテキストにすでにカーネルが含まれており、モジュール A をモジュール B 内に含めても、B の以前のすべての組み込みが「再ロード」されないためだと思います。 これは完全に理にかなっています。実際、カーネルを再インクルードすると、他の 2 つのメソッドが追加されます。
次に、私の質問は次のとおりです。
解決策
Ruby で foo.bar を呼び出すとどうなりますか?このようなもの:
foo.class.ancestors.each do |klass|
if klass.public_instance_methods.include? :bar
return klass.instance_method(:bar).bind(foo).call
end
end
raise NameError
つまり、Ruby は祖先を検索して、一致するインスタンス メソッドを見つけます。
Ruby で A.include B を呼び出すとどうなるでしょうか?このようなもの:
B.ancestors.each do |mod|
A.ancestors << mod unless A.ancestors.include? mod
end
B とそのすべての祖先は A の祖先になります。これら 2 つの動作ですべてが説明されます。