blog.waterlow.work

Ruby, Rails, js, etc...

【ruby】モジュールをextendした時の動き

タイトルそのまんまです。 疑問だったのでいろいろ調べてみたのですが、メタプログラミングRubyの特異クラスのところを読んでいたらわかりました。 オブジェクトからモジュールをextendすると、そのメソッドはオブジェクトのクラスではなく特異クラスに入る。

class Object
  def eigenclass
    class << self; self; end
  end
end

class C; end

module M
  def my_singleton_method
    'obj#my_singleton_method()'
  end
end

obj = C.new
obj.extend M

obj.eigenclass.instance_methods.grep(/my_/) #=>[:my_singleton_method]
obj.class.instance_methods.grep(/my_/) #=>[]

だから、もしオブジェクトに対してextendする予定のモジュールに定数とか名前空間を更に定義している場合、外からは非常にアクセスしづらい。

class C
  C_VAL = :c
end

module M
  M_VAL = :m
end

obj = C.new
obj.extend M
obj.class::C_VAL #=> :c
obj.singleton_class::M_VAL #=> :m
obj.class::M_VAL #=> uninitialized constant C::M_VAL

もし2つモジュールをインクルードするとどうなるかというと…

class C; end
module M; end
module N; end

obj = C.new
obj.extend M
obj.extend N
obj.singleton_class.ancestors
# => [M, C, Object, Kernel, BasicObject]

特異クラスで継承が行われる。