発表の内容
自己紹介
リフレクション機能の基本的考え方
利用法
リフレクション機能概要
実例
自己紹介
大林一平
京都大学理学研究科数学数理解析専攻D2
力学系分野
自己紹介
Ruby/SDL
RRSE
Ruby Refactoring Browser(RRB)
今回の発表について
Ruby 1.8.6 を想定
1.9.xでは変わっているかも
参考文献
RubyリファレンスのClass/Module/Objectの項
Rubyのソースコード
基本
大半の機能が「実行時処理」
実行前の処理に手をだせない
構文木をいじる方法がない
Lispのマクロのようなものがない
以下のクラスが中心的役割を果たす
Module/Class
Object
リファレンスのこれらの項が参考になる
利用法
ライブラリの作成に
Inner DSL
Rubyのリフレクション機能概要
変数,定数関連
メソッド関連
クラス階層関連
eval
hook
変数,定数関連
インスタンス変数(Object#)
instance_variable_{set,get}
instance_variable_defined?
instance_variables
クラス変数
定数
変数,定数関連
ローカル変数
local_variables
グローバル変数
global_variables
trace_var
メソッド関連
存在するメソッドの調査
Module#instance_methods
Object#methods
Object#singleton_methods
Module#method_defined?
Object#respond_to?
メソッド関連
メソッドの定義
Module#define_method
Module#{remove,undef}_method
Module#alias_method
Method, UnboundMethod
Object#method
Module#instance_method
クラス階層
Module#include
Object#extend
Class#superclass
Module#include?
Module#ancestors
Object#is_a?
Module#===
eval関連
Module#module_eval
Object#instance_eval
eval
eval関連
ブロックを取る場合
文字列を取る場合
hook
Object#method_missing
Module#const_missing
Module#method_added
Module#method_{removed,undefined}
Module#included
Module#extended
クラス階層と継承と特異クラス
特異クラスとは
あるオブジェクト固有のクラス
クラス階層と継承と特異クラス
class A < Object
end
a = A.new
クラス階層と継承と特異クラス
クラス階層と継承と特異クラス
class <<a
end
def a.x
end
Module#define_method
今回のテーマ
メソッドを定義するメソッド
メソッドを定義するメソッドを定義できる
例題
こんなメソッド(define)を作る
class A
extend Definable
define :foo, 32
define :bar, "barbar"
end
A.new.foo # -> 32
A.new.bar # -> "barbar"
Module#define_method
そのモジュール/クラスにメソッドを定義
メソッドの中身はブロックで与える
外のローカル変数が見える
例
class A
def initialize(); @y = 2; end
define_method(:foo) {|x| x+@y}
end
A.new.foo(4) # -> 6
例題の答え
module Definable
def define(name, val)
define_method(name){ val }
end
end
例題2
デフォルト値つきattr_readerぽくする
class A
extend Definable
define :foo, "foo"
def baz(); @foo = "baz"; end
end
a = A.new
a.foo # -> "foo"
a.baz
a.foo # -> "baz"
例題2回答
module Definable
def define(name, val)
ivname = "@#{name}"
define_method(name) {
if instance_variable_defined?(ivname)
instance_variable_get(ivname)
else
val
end
}
end
end
クラス変数もどきにしてみる
class A
extend Definable
define :foo, "foo"
end
a = A.new
a.foo # -> "foo"
A.new.foo = "bar"
a.foo # -> "bar"
ローカル変数の共有
module Definable
def define(name, val)
define_method(name){ val }
define_method("#{name}="){|v| val = v}
end
end
さらなる課題
initialize時にインスタンス変数を初期化
実装はかなり変わる
初期値をhashで与えられるようにしてみる
インスタンス変数を使わないように変更
foo=も定義するように変更
こつ
必要な情報はどのクラスが持っているかを意識する
どのクラス/オブジェクトに情報を持たせるか意識する
その他
アクセッサのないインスタンス変数の読み書き
privateメソッドの外部からの呼びだし
Railsみたいにメソッドが生えてくるようなこと
質問
どうぞ
終わり
http://risky-safety.org/zinnia/sdl/works/sps/
おまけ
MethodとUnboundMethod
Object#method
Module#instance_method
Method
メソッドをオブジェクト化する
(1..10).map(&3.method(:+))
UnboundMethod
クラスからメソッドを取りだす
インスタンスが存在しない状態
→つまりそのまま呼びだせない
UnboundMethod#bind
Methodのインスタンスを生成
Time.instance_method(:to_s).bind(Time.now).call
特徴
Procと互換
メソッドの名前でなく実体を持つ
上書きしても元の情報を持つ
例題
class A
extend Obsolete
def a
print "a\n"
end
obsolete :a
end
A.new.a
例題
module Obsolete
def obsolete(name)
old = instance_method(name)
msg = "Warning: #{self.to_s}\##{name} is obsolete"
define_method(name) {|*a|
warn(msg)
old.bind(self).call(*a)
}
end
end
補足
スレッド関連に注意
上の例はそのあたりは無視している