attr_accessorの仕組み

おまじない的に使っていたattr_accessor。
てっきり、予約語かなんかの類だと思っていたけど、Rubyの理解が進むにつれて、attr_accessorもメソッドなのだろうかと思ってきた。

ネットで検索しても、誰も触れていないって事はもはや暗黙の了解?と思ったけど、気になるものはしょうがないので、メソッドとして実現できるか調べてみる。

最初に

attr_accessorを使うソース

class Hello
  attr_accessor :msg
end

hello = Hello.new
hello.msg = "Hello world"
p hello.msg #=> "Hello world"

p hello.methods #=>メソッドの一覧

メソッドの一覧の中に

["msg=" ,"msg" ,...]

があるので、これがそのままメソッド名だろうと言うことで、attr_accessorを使わないソースを書いてみた。

class Hello
  @msg

  def msg
    @msg
  end
  
  def msg=(msg)
    @msg = msg
  end
end

hello = Hello.new
hello.msg = "Hello world"
p hello.msg #=> "Hello world"

p hello.methods #=>メソッドの一覧

後はこれをうまいことやれば、いいわけだ。

メソッドでメソッドを定義する

http://www.ruby-lang.org/ja/man/を片っ端から読んで、それっぽいのを探したところ、組み込み関数evalに

eval(expr[, binding[, fname[, lineno=1]]])

文字列 expr を Ruby プログラムとして評価してその結果を返します。第2引数に Proc オブジェクトまたは Binding オブジェクトを与えた場合、そのオブジェクトを生成したコンテキストで文字列を評価します。binding も参照してください。

メソッドの定義まではさすがにできないだろうと思ったものの、他にそれっぽいのが見つからなかったのでとりあえず書き換えてみる。

def who (val)
  eval("def #{val} ;@#{val} end") #getterの定義
  eval("def #{val}=(#{val}) ;@#{val} = #{val} end") #setterの定義
end

class Hello
  who :msg
end

hello = Hello.new
hello.msg = "Hello World"
p hello.msg #=> "Hello world"

p hello.methods #=> メソッドの一覧

意外にも成功。
意外にと思ってる時点で、まだRubyの理解が足りてないってことか?
実際はどうなってるかは、わかんないけどできることがわかったので十分。
まぁ、とりあえず、Rubyが面白くなってきた。