脳汁portal

アメリカ在住(だった)新米エンジニアがその日学んだIT知識を書き綴るブログ

[Ruby] メタプログラミングRuby 第2章 まとめ (動的メソッド)

2章のキーワード

  • 動的ディスパッチ
  • パターンディスパッチ
  • define_method()
  • Method Missing
  • ゴーストメソッド
  • ブランクスレート

はじめに

メソッドを動的に呼び出す

普通に呼び出す例

obj = TestClass.new
obj.test_method(arg)

動的に呼び出す例

obj = TestClass.new
obj.send(:test_method, arg)
  • 第一引数はメソッド名で、文字列とシンボルを入れられるが、シンボルの方が適当だとされている
  • 呼び出すメソッド名は通常の引数になるので、コード実行時に呼び出すメソッドを直前に決められる

  (この技術を「動的ディスパッチ」という)

動的ディスパッチ


  • プライベートメソッドも呼び出せる
    (public_send()はレシーバのプライバシー範囲を遵守する)
  • Test::Unitではこの動的ディスパッチが使われている
  • この技術を「パターンディスパッチ」という

define_method()

動的にメソッドを定義する方法

普通に定義する場合

class TestClass
  def test_method(arg)
    arg * 2
  end
end

動的に定義する場合

define_method ${つくりたいメソッド名} do |${つくったメソッドに与えたい引数}|
  ${メソッドの内容} #==>ここは同じ
endclass TestClass
  define_method :test_method do |arg|
    arg * 2
  end
end

Method Missing

obj.send :method_missing, :test_method

# => NoMethodError: undefined method 'test_method' for # *********
  • method_missing()をオーバーライドすると、実際に存在しないメソッドを呼び出すことができる

  • 上でもカバーしきれないメソッド呼び出しが合った場合は、superキーワードで元々の処理を呼び出してやればいい
  • ゴーストメソッドは継承チェーンを最後まで探索しなければならないため、通常のメソッドよりもややパフォーマンスが悪い
  • respond_to?メソッド:レシーバのオブジェクトに対してメソッドを呼び出せるか調べるメソッド

ブランクスレート

  • メタプロで動的にメソッドを定義する際に、既存のメソッドとの衝突を防ぐために、必要最小限のメソッド以外の全てのメソッドを削除した状態
  • Module#undef_method() : すべてのメソッドを削除する(注意が必要)
  • Module#remove_method() : レシーバのメソッドは削除するが、継承したメソッドはそのままにする
  • Ruby1.9からは、Objectのsuperclassに必要最低限のメソッドしかないBasicObjectが加わった。これを直接継承したクラスは自動的にブランクスレートになる

Builderライブラリ

  • XML生成ライブラリ
require 'builder'
xml = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)

xml.coder{
  xml.name 'のうじる', :nickname => 'brain'
  xml.langage 'Ruby'
}

<coder>
  <name nickname= 'brain'>のうじる</name>
  <language>Ruby</language>
</coder>

動的プロキシ

  • オブジェクトがゴーストメソッドを受け取り、なんらかのロジックを適用してから他のオブジェクトに転送すること