blog.tmp.tokyo

No Promises Await at Journey's End

Ruby で Java のリフレクション的な

RubyJava のリフレクション的なことをしたくなった。リフレクションがどういうものかは置いとくとして,要するに,

Class clazz = Class.forName("Sample");
Method method = clazz.getMethod("sample");
method.invoke(clazz.newInstance());

ということをやりたい。何に使うかは聞かないで欲しい。Ruby も最近やり始めたので最適解なのかどうかはわからないが,

Dir[File.expand_path('./temp') << '/*.rb'].each do |file|
    require file
end

として自分のいるところの temp フォルダ配下に *.rb のファイルがあれば全部 require するようにしてみた。その後,

dir = "./temp"
@class_name_list = Array.new
Dir::glob(dir << "/*.rb").each { |entry|
    file_name = File::basename(entry)
    class_name = file_name[0, file_name.length - 3].capitalize
    @class_name_list.push(class_name)
}

という感じ。./temp/*.rb を走査して取得したパスを File::basename でファイル名だけ取り出す。それの拡張子を取りつつ,先頭の文字を*1大文字に変更。できあがったクラス名を配列へ格納する。

これで実行したいクラス名は require されて全部配列に持つことができたので,いざ実行。

temp = Object.const_get(clazz).new

とすればクラス名の文字列からクラスをインスタンス化することができる。RubyJava みたいに面倒じゃない*2ので,このあと temp オブジェクトから自由にメソッドが呼べる。Interface とかはなくてよい*3

以上。

*1:Ruby の厳密なルールはわからないが,Java と同じくクラス名とファイル名を同じにしておくルールとした。

*2:型の扱いについて。良し悪しはあると思うけど。

*3:というか Ruby にそんなものはない。