From: tnaka@... Date: 2000-04-25T22:06:23+09:00 Subject: [ruby-list:22115] デフォルト付き attr_reader 中島@ブレーンです。 attr_readerの派生バージョンを作ろうとしています。一応、目的の ものはできたのですが、ちょっと見苦しいので、もっと良い方法があ るような気がします。何かよい方法があれば教えてください。 やろうとしていることは、デフォルトを指定できるattr_readerです。 つまり、attr_readerはその変数が定義されてないとnilを返すので、 nilのかわりに、指定した値を返すようにしたいのです。そして、こ れにいくつか機能をつけ加えて、自作のフレームワークに組込もうと しています。 一応、目的を満たしたバージョンは次のとおりです。 ----------------------------------------------------------- module M1 def setup_defaults module_eval "Defaults = {}" end def attr_reader_with_default(var_name, val) var_name = var_name.id2name if var_name.kind_of?(Integer) set_defaults(var_name, val) module_eval <<-END def #{var_name} if @#{var_name} @#{var_name} else instance_eval { Defaults["#{var_name}"] } end end END end def set_defaults(var_name, val) class_eval "Defaults[\"#{var_name}\"] = val" end end ----------------------------------------------------------- 使用例 ----------------------------------------------------------- class A1 extend M1 setup_defaults attr_reader_with_default :v1, "A1.v1 default" attr_reader_with_default :v2, "A1.v2 default" def initialize @v2 = "A1.v2" end def test print "v1=#{v1} v2=#{v2} in A1\n" end end class A2 extend M1 setup_defaults attr_reader_with_default :v1, "A2.v1 default" attr_reader_with_default :v2, "A2.v2 default" def initialize @v2 = "A2.v2" end def test print "v1=#{v1} v2=#{v2} in A2\n" end end A1.new.test A2.new.test ----------------------------------------------------------- 実行結果 ----------------------------------------------------------- [tnaka@ryoma tmp]$ ruby test.rb v1=A1.v1 default v2=A1.v2 in A1 v1=A2.v1 default v2=A2.v2 in A2 ----------------------------------------------------------- つまり、 attr_reader_with_default :var_name 既定値 と定義しておくと、var_nameというメソッドが、@var_nameというイン スタンス変数のアクセッサになります。そして、@var_nameが定義さ れてない場合は、既定値を返却します。 問題はこの既定値を覚えておく場所です。既定値はクラスごとに違い ますから、クラスごとに定義されます。そこで、ruby本のクラス定数 もどきの技を使って、 Defaults = {} とすればいいと思ったのですが、これを単純にM1の中に書いてもうま くいかず、試行錯誤しているうちに上記のコードとなりました。直し たいのは、以下の3点です。 1) setup_defaultsの呼びだしを不要にしたい attr_reader_...だけ書けば使えるようにしたいのですが、クラス変 数もどき(Defaults)をモジュール内で定義する方法がわかりませんで した。 2) extend M1 でなく include M1 にしたい つまり、M1の中で普通のメソッドも定義したいのです。 module M2 extend M1 def ..... end end とか、その逆とかやったのですが、だめでした。 3) evalが多すぎるが、できれば削りたい なんとなく、無駄な遠回りをしているような気がするのですが、どう もよくわかりません。 クラス変数もどきを使うと仮定して問題を単純にすると次のようにな ります。 ----------------------------------------------------------- module M2 def make_classval class_eval "Classval = {}" class_eval <<-END def get_classval instance_eval { Classval[0] } end END end def set_classval(val) class_eval { Classval[0] = val} end end class B1 extend M2 make_classval set_classval("B1") def initialize print "B1::Classval=",get_classval,"\n" end end class B2 extend M2 make_classval set_classval("B2") def initialize print "B2::Classval=",get_classval,"\n" end end B1.new B2.new ----------------------------------------------------------- つまり、def ... endの外側でクラス変数に値を代入して、メソッド の中で取り出せればいいわけです。 これに対して 1) make_classvalをなくしたい 2) extendでなくimportですむようにしたい または、get_classvalをevalでなく直接定義したい 3) xxx_evalは全部必要か? ということです。あるいは、全く違う実装方法でももちろんかまいません。 ------------------------------------------------------------- 中島 拓 (株)ブレーン パッケージ開発部 (p95@brain-tokyo.co.jp) http://www03.u-page.so-net.ne.jp/dc4/tnaka/