new(superclass = Object) -> Class
Class.news
new(superclass = Object) {|klass| ... } -> Class
新しく名前の付いていない superclass のサブクラスを生成します。
名前のないクラスは、最初に名前を求める際に代入されている定数名を検索し、見つかった定数名をクラス名とします。
sequel のまねっこをしてこんなことをしてみた。テーブル名を指定してクラスを定義し、そのクラスがアクセスするテーブルを固定する。同時に接続先のデータソースも保持。
module MOD def self.Base(table_name) c = Class.new(Base) sig = class << c ; self end sig.send :define_method , :table_name , Proc.new{table_name} # # ごにょごにょして接続先のデータソースも保持しておく # c end class Base def find # ActiveRecord ちっくなメソッドを色々定義 end end end class A < MOD::Base('DUMMY_TABLE') ; end puts A.table_name #=> DUMMY_TABLE
実際には、データソースの数だけ each で回して切り替えながら各環境のテーブルを検索するようにしていたのだけど、class A を再度定義するとエラーになる。
class A < MOD::Base('DUMMY_TABLE') ; end class A < MOD::Base('DUMMY_TABLE') ; end #=> superclass mismatch for class A (TypeError)
もっと簡単な例。
class B < Array ; end class B < Hash ; end #=> superclass mismatch for class B (TypeError)
これは ruby-list:39567 で提案されて 1.8.2 で追加された変更点。
新たなクラスを同名で定義しようとしたときに、
ruby-list:39567
「already initialized constant Foo」という有り触れた警告ではなく、もっと
強い警告を出すか、またはできれば例外を生成してくれないでしょうか?
でも、最初に書いたようにデータソースを切り替えてつなぎ直すけど、クラス名は使い回したい。なんとかしたい。
考える ・・・・ module で名前空間分ければいいんじゃね?
Module#module_eval あたりを使えばいいなじゃないかと思ったけどうまく行かず。結局 eval 。
def define_class(mod_name , class_name , table_name) eval <<-EOF module #{mod_name} class #{class_name} < MOD::Base('#{table_name}') end end #{mod_name}::#{class_name} EOF end module MOD def self.Base(table_name) c = Class.new(Base) sig = class << c ; self end sig.send :define_method , :table_name , Proc.new{table_name} c end class Base def find "hi !!!" end end end a1 = define_class("M1" , "A" , "DUMMY_TABLE") a2 = define_class("M2" , "A" , "DUMMY_TABLE") puts a1 #=> M1::A puts a2 #=> M2::A puts a1.table_name #=> DUMMY_TABLE puts a1.new.find #=> hi !!!
クラスを変数に突っ込めば再代入もできるので特に問題なし。