K.Sasada's Home Page

こめんとのついか

こめんとこめんと!

message

please add long comment :).

_19(Wed)

Ruby VM アドベントカレンダーの 19 日目です.

今日は,Ruby 2.0 で導入された Module#prepend についてご紹介します.

ご存じの通り,Ruby では Module を include することで,メソッド定義を拡張することが出来ます.

module M
  def m; p :M; end
end

class C
  include M
end

このとき,C オブジェクトはメソッド m を持っていることになります.

さてここで,C もメソッド m を持っているとき,どうなるか知っていますか?

module M
  def m; p :M; end
end

class C
  include M
  def m; p :C; end
end

C.new.m #=> :C

C#m と M#m では,C#m が使われていることがわかりました.実は,ancestors を見るとわかります.

# 上のコードの続きだと思って下さい.
p C.ancestors #=> [C, M, Object, Kernel, BasicObject]

Class#ancestors はクラスの階層構造を示しますが,これはそのままメソッドの探索順序にもなっています.C と M では C のほうが先に来るので,C#m が先にヒットするのでこちらが使われる,ということですね.

だめ押しに,C#m で super してみましょう.

module M
  def m; p :M; end
end

class C
  include M
  def m; p :C; super; end
end

C.new.m

#=>
:C
:M

C#m の super から M#m が呼ばれていることがわかります.


さて,C が M を include したとき,C -> M の順にメソッドが検索されることがわかりました.そして,C#m があれば,それが利用されます.

しかし,C#m よりも先に M#m を呼んで欲しい,という場合はどうすればいいでしょうか.実は 1 つ方法があります.Object#extend を使います.

class C0
  def m; p :C0; end
end

module M
  def m; p :M; super; end
end

class C < C0
  def m; p :C; super; end
end

obj = C.new
obj.extend M
obj.m

#=>
:M
:C
:C0

C オブジェクトである obj に extend メソッドによって M を拡張したため,見事に M,C(そして C0)の順にメソッドが呼ばれていることがわかります.よかったよかった.

が,これだと C.new で作成したオブジェクトすべてに対して Object#extend を呼ばねばならず不便です(そして,オブジェクトの生成が遅くなります).


そこで Ruby 2.0 からは Module#prepend という機能が追加されました.Module#include では include するクラスの後ろに加えていたのが,今度は prepend によって 前に追加する,という機能です.

では実際に使ってみましょう.

class C0
  def m; p :C0; end
end

module M
  def m; p :M; super; end
end

class C < C0
  prepend M                # include だったのを prepend にしている
  def m; p :C; super; end
end

obj = C.new
obj.m

#=>
ruby 2.0.0dev (2012-12-21 trunk 38515) [i386-mswin32_100]
:M
:C
:C0

見事,obj.m は M#m,C#m(そして C0#m)の順に探索されるようになりました.一応,ancestors を見ておきましょうか.

# 前述のクラス定義があると思って下さい
p C.ancestors
#=> [M, C, C0, Object, Kernel, BasicObject]

というわけで,C の前に M があることが確認できると思います.

Module#prepend で,これまで出来なかったようなことができるようになります.試してみて下さい.

では,今日はこの辺で.


好きなだけ長いコメントをどうぞ。

お名前


back

tton 記述が使えます。YukiWikiな記述してりゃ問題ありません。

「行頭に#code」 と、「行頭に#end」 で挟むと、その間の行は pre で囲まれます。プログラムのソースを書くときに使ってください。

例:

#code

(なんかプログラム書く)

#end

リンクは

[[なまえ|http://www.example.org]]

とか

[[http://www.example.org]]

で貼れます。

$Date: 2003/04/28 10:27:51 $