Techioz Blog

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 つの動作ですべてが説明されます。