パーフェクトRubyお勉強フェスティバル 第10章 「Rubyでのリフレクションプログラミング」

oftonkingdom.hatenablog.com

多忙につき,ひさびさの更新です.

メタプログラミング最後の章「Rubyでのリフレクションプログラミング」見ていきます.

リフレクションとは

プログラムの実行中に、そのプログラムの情報を取得したり操作することをリフレクションと呼びます。

とあります.

オブジェクトの情報取得や操作

インスタンス変数の確認

Object#instance_variablesメソッドで,オブジェクトに定義されているインスタンス変数名の配列を得ることができるのはわかったが,そこの例にあったコード(以下のような感じ)

class Ofton
  def initialize
    @material = '羽毛'
    @temperature = 38
  end
end

ofton = Ofton.new

ofton.instance_variables.any? do |instance_val_name|
  instance_val_name =~ /status/
end

Array#any?で絞り込んでいるが,絞り込み条件はRegexp#=~を使った結果を用いている.「=~」ってマッチした位置を返すんじゃなかったか…?と思ったが,マッチしなかった場合「nil」を返すから,偽として扱われてマッチしたかどうかの判定に使われるんでした.(復習した)

getterやsetterがなくても,オブジェクトのインスタンス変数のgetやsetができる.

Object#instance_variable_getとObject#instance_variable_setで可能. どんなところで使われるのか気になって調べましたが,なるほど,テストなどで使われるのですね.(以下のような感じで)

dev.classmethod.jp

オブジェクトに定義されているメソッドを調べる

Object#methodsでは引数なしまたは引数にtrueを渡すと,プライベートメソッド以外のメソッドを返す.引数にfalseを渡すとオブジェクトの特異メソッドのみを返す.

Object#send

任意のメソッドを実行できるメソッド.上に貼ったブログにも登場しております. メソッドの可視性にかかわらず呼び出しができるので,privateメソッドを呼びたいときなどに使える.

Object#__send__,前に書いた記事でなんだ?って思ったものですがここで解決.「send」というメソッド名が汎用的な名前のため,もしオーバーライドされてもよいように「__」をつけた別名のメソッドを用意しているということでした.

クラスの情報取得・操作

クラス変数への値のセット

サンプルコードで,Module#instance_variable_getではインスタンス変数名としてシンボル(例:「:@val」)を渡していたがModule#class_variable_setではクラス変数名として文字列(例:「'@@val'」)を渡している.クラス変数名はシンボルを使って書かないんだろうか?と思ったけど「:@@val」のような指定もするみたい(サンプルコードがたまたま文字列になってただけ)

ref.xaio.jp

インスタンスメソッド 削除と未定義の違い

Module#remove_methodでメソッドを削除,Module#undef_methodでメソッドを未定義にする. これらの違いについて.削除された場合は,同名メソッドが親クラスでも定義されているなら,親クラスのメソッドが呼び出されるようになる.一方,未定義にすると,同名メソッドが親クラスで定義されていたとしても,NameErrorが発生する.

イベントのフック

「||=」という演算子

モジュールがincludeされたら実行されるメソッド「Module#included」「Module#extended」のサンプルコードで出てきた「||=」という演算子がよくわからなかった.けどこれよくよくみたら以下のようなことだな.

#どっちもおなじ!
ary ||= []
ary || ary = []

aryが「nilである」場合にだけaryに空配列が代入されるというわけか…ショートサーキット演算子の性質を上手く用いていてスマートである.

qiita.com

Kernel.#set_trace_funcとTracePoint

これらによってRubyインタプリタのフックができる.細かいことはたくさんあるが,Procオブジェクトかブロックの処理をハンドリングできる.Ruby標準添付のdebugやprofileはKernel.#set_trace_funcによって作られている.

普通にアプリを書くぐらいだったらなかなか使うことはないのかな.