# Rubyの実用的かもしれないメタプログラミングのテクニック
_published: 2009/12/15_ 
4歳の長女とオセロで遊ぶようになりました。挟んでひっくり返すのはできるのですが、ナナメに挟むというのが難しいようです。そこ、ナナメもひっくり返せるよ。と思うのですが、長女は気づきません。私もあえて口に出さず、常に圧勝しています。白黒はっきりしないグレーなオセロを楽しんでいます。
さて、Rubyの特徴のひとつに、メタプログラミングがやりやすいことが挙げられます。あ、オセロの話は一切関係ないので、忘れてください。
- [[Rubyのリフレクション解説(eval族のはなし)]]
メタプログラミングをすると、肩こりや便秘の解消などいろいろな効果がありますね。今回は、そのうちのひとつを紹介したいと思います。思い浮かべてください。既存のライブラリのある処理だけ差し替えたい。下の例では、 `target` メソッドの中で使われている `Fixnum#to_s` メソッドを差し替えたいのです。そう、差し替えたいのです。
```ruby
class Sample
def target(val)
val += 1
val.to_s
end
end
p Sample.new.target(123)
#=> "124"
```
Rubyは拡張クラスなので、既存のクラスに勝手にメソッドを追加することができます。はい、 `Sample` クラスに `target_alt` メソッドを追加しました。
```ruby
class Sample
def target_alt(val)
end
end
```
`class_eval` を使ってクラスにメソッドを追加することもできます。はい、 `Fixnum` クラスに `to_s_alt` メソッドを追加しました。
```ruby
Fixnum.class_eval do
def to_s_alt
"I'm a Rubyist."
end
end
```
さらにですね。さらにですよ。追加した `to_s_alt` メソッドを、元からある `to_s` メソッドの代わりに使いたい。つまり、 `Fixnum#to_s` メソッドが呼ばれたときに、 `Fixnum#to_s_alt` メソッドを使いたいのです。そう、使いたいのです。 `alias_method` が使えます。 `to_s` が入れ替わってますね。
```ruby
Fixnum.class_eval do
def to_s_alt
"I'm a Rubyist."
end
alias_method :to_s_org, :to_s
alias_method :to_s, :to_s_alt
end
```
今回やることは、これだけです。やり尽くしました。本題に戻りましょう。 `Sample#target` メソッド内で使われている `Fixnum#to_s` の中身を差し替えたい。論よりコード。全コードを載せておきます。
```ruby
class Sample
def target(val)
val += 1
val.to_s
end
end
p Sample.new.target(123)
#=> "124"
class Sample
def target_alt(val)
Fixnum.class_eval do
def to_s_alt
"I'm a Rubyist."
end
alias_method :to_s_org, :to_s
alias_method :to_s, :to_s_alt
end
return_value = target_org(val)
Fixnum.class_eval do
alias_method :to_s, :to_s_org
end
return_value
end
alias_method :target_org, :target
alias_method :target, :target_alt
end
p Sample.new.target(123)
#=> "I'm a Rubyist."
p 123.to_s
#=> "123"
```
`Fixnum#to_s` を差し替えたままだと、 `Fixnum#to_s` を使っているすべてのコードが影響を受けてしまいます。そこで、 `target_org` を呼び出したあとで元に戻しています。マルチスレッドなプログラムだと、ヒドイことになることが予想されますので注意してください。
なんでこんな事をするハメになったのか。先日リリースした、Radiant CMSから日本語メールを送れるようにするjp_mailerエクステンション。これは、メール送信のためのmailerエクステンションを改造して作っています。その改造なのですが、ファイルを書き換えるとカンタンに改造できます。ですが、それでは何かと不具合もあろうかと思い、mailerに一切手を加えずに改造するためにメタプログラミングすることにしました。
- [Radiant CMSから日本語メールを送信するjp_mailerエクステンションを作った。 - このブログは証明できない。](http://d.hatena.ne.jp/shunsuk/20091203/1259840376)
jp_mailerでは他にもいくつかのテクニックを使っていますが、コード自体はカンタンなので中身をチェックしてみてください。あとは、こちらの記事がメタっぽいです。
- [[Rubyのリフレクション解説(eval族のはなし)]]
こんな初歩的な内容じゃ物足りない人は、Railsのコードを読むといいらしいですよ。むしろ、Railsはやりすぎという話も。。。