影武者(kagemusha)というスタブフレームワークはテスト時以外にも使えるかも…

Time.nowのテスト? それMochaでできるよ - http://rubikitch.com/に移転しましたを書いてるとid:ujihisaより「kagemusha」というgemを教えてもらった。

require 'rubygems'
require 'kagemusha'

birthday = Time.local(1993, 2, 24)
Time.now     # => Tue Jun 03 01:59:14 +0900 2008
Kagemusha.new(Time).defs(:now) { birthday }.swap do
  Time.now   # => Wed Feb 24 00:00:00 +0900 1993
end

Kagemusha#defはインスタンスメソッドの定義をすりかえ、Kagemusha#defsはクラスメソッドの定義をすりかえる。これらはselfを返す。
で、Kagemusha#swapの間だけすりかえを実行する。まさしく影武者だ。
こんなのだからテスト時以外にも使えそうだ。

「Kagemusha - a library of testing mock-objects」とは書いているけど、引数等のチェック機能がないからスタブフレームワークだと思う…

ザ・ワールドもこの通り。

require 'rubygems'
require 'kagemusha'

now = Time.now
Kagemusha.new(Time).defs(:now) { now }.swap do
  # ザ・ワールド!
  Time.now   # => Tue Jun 03 02:08:06 +0900 2008
  sleep 2
  Time.now   # => Tue Jun 03 02:08:06 +0900 2008
end
# そして時は動きだす
Time.now     # => Tue Jun 03 02:08:08 +0900 2008


影武者のソースコードを読んで初めてModule#define_methodの第2引数を知った(^^; 遅すぎ…
第2引数にUnboundMethodも受け付けるから、Module#instance_methodと組み合わせれば自由自在にメソッドを書き換えられる。aliasで退避するのはかっこ悪くて嫌。

class X
  def hoge() :X end
end

class Y < X
  def hoge() :Y end
end

class Z < Y
  define_method(:hoge, X.instance_method(:hoge))
end

X.new.hoge   # => :X
Y.new.hoge   # => :Y
Z.new.hoge   # => :X

こういうことができるから、やっぱりメソッドもオブジェクトなんだなぁーと感じる。