# Rubyの実用的かもしれないメタプログラミングのテクニック _published: 2009/12/15_ ![alt](http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/shunsuk/20091215/1260875723) 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はやりすぎという話も。。。