発表の内容

自己紹介

リフレクション機能の基本的考え方

利用法

リフレクション機能概要

実例

自己紹介

大林一平

京大マイコンクラブ http://www.kmc.gr.jp/

京都大学理学研究科数学数理解析専攻D2

力学系分野

自己紹介

Ruby/SDL

RRSE

Ruby Refactoring Browser(RRB)

今回の発表について

Ruby 1.8.6 を想定

1.9.xでは変わっているかも

参考文献

RubyリファレンスのClass/Module/Objectの項

RHG http://i.loveruby.net/ja/rhg/

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

クラス階層と継承と特異クラス

a1.png

クラス階層と継承と特異クラス

 class <<a
 end
 def a.x
 end

a2.png a3.png a5.png a7.png

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

補足

スレッド関連に注意

上の例はそのあたりは無視している