Ruby 2.0.0リリース! – prependを使ってみよう

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クラスのメソッドが呼び出せることがわかると思います。

prependの場合

 

includeの場合

ここまで書いて、Personクラス意味ないことに気がついたorz

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

baba

ゆとりプログラマー。

高校時代から趣味でプログラミングを初め、そのままコードを書き続けて現在に至る。慶應義塾大学環境情報学部(SFC)卒業。BPS設立初期に在学中から参加している最古参メンバーの一人。Ruby on Rails、PHP、Androidアプリ、Windows/Macアプリ、超縦書の開発などを気まぐれにやる。軽度の資格マニアで、情報処理技術者試験(15区分 + 情報処理安全確保支援士試験)、技術士(情報工学部門)、CITP、Ruby Programmer Goldなどを保有。

babaの書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ

インフラ

BigBinary記事より

ActiveSupport探訪シリーズ