まさし君の家はお金持ちでした
まさし君はちょっと意地悪だったけど
クラスでビデオゲームを持っているのが彼だけだったので
授業が終わるとみんなまさし君の家に行ってゲームをしました
週末になるとまさし君は家族で湖畔の別荘にいって
釣りをしたりバーベキューをしたりしていて
僕らをたまにその別荘に誘ってくれました
そんなときには決まってまさし君のお父さんが
魚の釣り方とかボートの漕ぎ方とかを
優しく僕らに教えてくれました
別荘からの帰り道でいつも僕らはこうつぶやいていたのでした
「まさし君のパパが僕のパパだったらいいのに..」
幸か不幸か現実世界ではそんなことは起きませんでした。
果たして、Rubyの世界ではどうでしょうか?早々試してみましょう。
まずは僕の現実から。
class PoorDad
private
def money
10000
end
end
class Me < PoorDad
def give_me_money
money * 0.01
end
end
Me.new.give_me_money # => 100.0
次に、まさし君の現実を。
class RichDad
private
def money
10000 * FaceBook.stock_value
end
end
class FaceBook
def self.stock_value
38
end
end
class Masashi < RichDad
def give_me_money
money * 0.01
end
end
Masashi.new.give_me_money # => 3800.0
さすが、まさし君のパパです。
さあ、本題です。まさし君のパパを僕のパパに!
class Me < RichDad
def give_me_money
money * 0.01
end
end
Me.new.give_me_money # =>
# ~> -:38:in `<main>': superclass mismatch for class Me (TypeError)
嗚呼、Rubyにもそれはできないのですね..
それではあまりに僕らが可哀想じゃありませんか.. 少しの間だけでも金持ち父さんの子供になってみたい..
...
そんなわけで..
そんな子供たちの夢を叶えるべく、親クラスを一時的に差し替えるSurrogateDad(代理パパ)モジュールを書いてみましたよ!
module SurrogateDad
def surrogate(klass, new_dad)
class_name = :"#{klass}"
temp_class_name = :"Temp#{klass}"
self.class.class_eval do
const_set(temp_class_name, klass)
remove_const(class_name)
c = Class.new(new_dad)
const_set(class_name, c)
end
yield
ensure
self.class.class_eval do
remove_const(class_name)
const_set(class_name, const_get(temp_class_name))
remove_const(temp_class_name)
end
end
end
次のように使います。
include SurrogateDad
surrogate(Me, RichDad) do
Me.superclass # => RichDad
Me.class_eval do
def give_me_money
money * 0.01
end
end
Me.new.give_me_money # => 3800.0
end
Me.new.give_me_money # => 100.0
surrogateメソッドの第1引数に子クラスを、第2引数に差し替える親クラスを渡します。そうすると、そのブロック内では一時的に親クラスが差し替えられます。子クラスのメソッドはここで定義し直す必要があります。残念ながら1 。ブロックを出ると元の親クラスに戻ります。
つかの間、Rubyの世界で子供たちの夢が叶いました。
replace superclass temporally in Ruby — Gist
なお、SurrogateDadモジュールはfakefsモジュールを参考にしました。
金持ち父さん貧乏父さん by ロバート キヨサキ and シャロン・レクター(公認会計士)