2007年 10月 05日

instance_eval のスコープ と class_variable_set を使わないクラス変数の代入方法

ただし、ローカル変数だけは instance_eval の外側のスコープと共有します。

http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=Object#instance_variables
class Hoge
	@@hoge = :hoge

	def initialize
		@test = :Nice_boat
	end
end

o = :test
Hoge.new.instance_eval do
	p o #=> :test
	p @test #=> :Nice_boat
	p @@hoge #=> uninitialized class variable @@hoge in Object (NameError)
end

クラス変数も外側のスコープになってるような。module_eval のほうでは : version 1.6.8 以降でブロックが与えられた場合は、定数とクラス変数のスコープも外側のスコープになります。 って書いてあるけど、instance_eval もそうなのかな。
だとすると、ruby 1.8.3 で class_variable_set/get が実装されるまで、クラス変数に直接オブジェクトをセットする方法ってなかったのかな。Mac の ruby は 1.8.2 なのだけど、クラス変数を外から代入するのをどう書いたらいいかわからない……

とここで思いたって、たぶん同じことを ML できいたひとがいるだろうと class_variable_set を検索すると、[ruby-talk:144741] に "Why no class_variable_set?" という投稿があった。ML のアーカイブが全部 not found になっててリンクはれないけどキャッシュでみたところ、アクセサメソッドを定義してた。なるほど……

未定義にするところまで含めると以下のようにすれば一応できる。きもい。

class Hoge
	@@hoge = :hoge

	def t
		@@hoge
	end
end

o = :test
Hoge.class_eval %{
	def self.hoge=(val)
		@@hoge = val
	end
}
Hoge.hoge = o
(class << Hoge; self; end).instance_eval do
	remove_method(:hoge=)
end

p Hoge.new.t #=> :test

Hoge.hoge = :aaa # undefined method `hoge=' for Hoge:Class (NoMethodError)

なんか更めて読みなおしてみるとあたりまえすぎる……べつに定義されていないなら、class_variable_set/get を定義すればいいだけじゃん
あれでも、結局 eval するはめになってだめか。おれあたまわるい……もうやだ