Tech Racho エンジニアの「?」を「!」に。
  • 開発

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


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。