Ruby20周年おめでとうございます。そして、待望の2.0.0がリリースされましたね!
1.8から1.9になったときと違い、互換性がなくなる変更は少ないので、比較的移行は楽そうです。
有名かつ大きな新機能は
- キーワード引数
- Module#prepend
- Enumerable#lazy
- Refinements
ですが、ここではprependについて見てみます。
prependはメソッド探索チェーンの手前にmoduleを差し込む機能であって、何がうれしいかといえばalias_method_chainと同じことがシンプル・高速に実現できるということです。
たとえば以下のようなクラスがあったとします。
class Person
def hello
puts "Hello! I am a person."
end
end
class Student < Person
def hello
puts "Hello! I am a student."
end
end
ここで、挨拶の前後に処理を入れたい場合、Module#prependを使えばこのように簡単に実現できます。
module Polite
def hello
puts "これから挨拶します"
super
puts "よろしくお願いします"
end
end
class Student < Person
prepend Polite
end
Student.new.hello
実行結果
これから挨拶します
Hello, I am a student.
よろしくお願いいたします
同じことをRuby 1.9で実現したい場合、以下のようなアプローチが必要になります。
- StudentのサブクラスPoliteStudentを用意して、helloを定義し、その中でsuperを呼び出す
- この方法では、Studentを利用しているすべての箇所をPoliteStudentに書き直す必要があります。
- Student#polite_helloを定義し、その中でhelloを呼び出す
- この方法では、Student#helloを利用しているすべての箇所をStudent#polite_helloに書き直す必要があります。
- Student#helloをStudent#original_helloとして別名定義する(alias_method)。Student#helloを上書きし、その中でoriginal_helloを呼び出す
- alias_method_chainの仕組みです。呼び出し側に影響なく実現できますが、original_helloのようなつまらないメソッドが増えてしまいます。
すぐに思いつく使い方としては、
- 特定メソッドの前後で時刻を測定し、簡易的なプロファイリングを実施
- 例外が発生するメソッドがあるけどそれを握りつぶしたい・独自処理したい
- 特定メソッドの実行のみ、I18n.localeやシステム環境変数をいじりたい
などに使えそうですね。
このようなちょっとしたパッチを、少ないコード量で気軽に実現できるのが重要なポイントだと思います。
なお、メソッドの探索順を図で表すと、こんな感じです。
prependの場合、superでStudentクラスのメソッドが呼び出せることがわかると思います。
ここまで書いて、Personクラス意味ないことに気がついたorz