2007年 10月 05日

ruby, クラスのインスタンス変数と、クラス変数

なにがちがうんだって思ったらリファレンスマニュアルに書いてあった……

  • サブクラスから参照/代入が可能
  • インスタンスメソッドから参照/代入が可能
http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=%CA%D1%BF%F4%A4%C8%C4%EA%BF%F4

サブクラスからの参照/代入が大きな違いかな。クラスのインスタンス変数はあくまでそのクラスにしか属さないから、サブクラスからは頑張って superclass 探すしかアクセスする術がない。

無駄だけど以下のようにすればだいたいクラス変数と同じことがクラスのインスタンス変数でできる。

class Hoge
	@@test = :CV_test
	@test = :IV_test

	def hoge
		[
			@@test,
			self.class.ancestors.find {|c|
				c.instance_variables.include?("@test")
			}.instance_variable_get(:@test)
		]
	end

end

p Hoge.new.hoge #=> [:CV_test, :IV_test]
p Hoge.new.hoge #=> [:CV_test, :IV_test]

class Fuga < Hoge
end

p Fuga.new.hoge #=> [:CV_test, :IV_test]
p Fuga.new.hoge #=> [:CV_test, :IV_test]

サブクラスからはアクセスされたくない場合だけクラスのインスタンス変数をつかうべきかな (具体的にはどういうケースだろう)

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 するはめになってだめか。おれあたまわるい……もうやだ

特異クラス

なんかひさしぶりに特異クラスを意識して、あぁ Ruby はクラスベースなんだなぁってなんだか実感した。普段使う分にはインスタンスにメソッドが直接定義されてるように見えて特異クラスを意識しないもんなぁ。
特異メソッドは全て特異クラスに定義されているんだから、特異クラスも間接的に使いまくってるんだよなぁ。Class.new も特異クラスのメソッドだもんなぁ。Class さえ Object のインスタンスであるから特異クラスを持ってる。特異クラスもクラスのインスタンスなのだから、特異クラスを持ってる。

なんとなく思いついて特異クラスもクラスなんだからインスタンスつくれんのかなぁって思ったけど作れなかった。(clone からデータコピーをのぞいた感じになるのかなぁと漠然と想像した)

in `new': can't create instance of virtual class (TypeError)

SQLite3::SQLException: SQL logic error or missing database

CGI で AR つかってたらこれがでてすすまなくなった。一時間ぐらいハマった……

and that the folder that the DB file is in is also readable and writeable by the Apache user

http://www.kryogenix.org/days/2006/09/17/operationalerror-sql-logic-error-or-missing-database

オーナーシップと該当ファイルのパーミッションだけじゃなくて、ディレクトリのパーミッションもだった……

gems

別に gems じゃなくてもそうだけど、ああいうパッケージシステムのウェブのほうは、ドキュメントが表になってるべきで、rubyforge みたいにプロジェクトがいきなりでてくるようになってるべきじゃないと思う。だってプロジェクトの情報なんてそのモジュールを使うためには全く必要ない。
gem のサーバってどうなってんのかな。おれおれサーバつくったほうがいい気がしてきた。

http://jp.rubyist.net/magazine/?0010-PackageManagement
に書いてあった。