gdata-1.1.1 を読んでいたら gdata-1.1.1/lib/gdata/client/base.rb に
41 self.send("#{key}=", value)
とあった。
それで
クラスを初期化する際 hash をもちいてインスタンス変数を一気に設定したい場合について考えてみた。
1) 最初にgdataでも使われている self.send() を使った場合
3 class My_1
4 attr_writer :id, :name
5 attr_reader :name
6 def initialize(h={})
7 h.each{|k,v| self.send("#{k}=", v)}
8 end
9 end
10
11 h = {:id=>'123', :name=>'abc'}
12 p My_1.new(h)
実行結果
#<My_1:0xxxxxxxx @id="123", @name="abc">
ハッシュのキーと値がinstance 変数に設定されていることがわかる。
また attr_writer によって :id と :name 以外のkey を受け付けないよう設定されている。
これを確認する為、
h = {:id=>'123', :name=>'abc', :title=>'ddd'}
に変更し実行してみると
...(NoMethodError)
:title は attr_writer に定義されていないので instance 変数として定義できないことが確認できた。
2) 次に self[]= をつかってinstance 変数を設定するクラスの場合
15 class My_2
16 attr_writer :id, :name
17 attr_reader :name
18 def initialize(h={})
19 h.each{|k,v| self[:"@#{k.downcase}"] = v}
20 end
21 alias []= instance_variable_set
22 alias [] instance_variable_get
23 private :[], :[]=
24 end
25 p My_2.new(h)
実行結果
#<My_2:0xxxxxxxx @id="123", @name="abc">
My_1 の場合と全く同じ結果を得ることができる。
では、My_1 の場合と同様に h を変更し
h = {:id=>'123', :name=>'abc', :title=>'ddd'}
として実行してみるとどうなるか?
実行結果
#<My_2:0xxxxxxxx @id="123", @name="abc", @title="ddd">
attr_writer とは無関係になんなく :title が定義されてしまった。
15行目のattr_writer :id, :name は class My_2 においては無意味のようだ。
では、あたかも attr_writer を設定したかのようにさせるにはどうすればいいのだろうか?
14 class My_2
15 @@ins_keys = [:id, :name]
16 @@ins_keys.each{|x| attr_reader x}
17 def initialize(h={})
18 h.each{|k,v|
19 key = k.downcase.to_sym
20 self[:"@#{key}"] = v if @@ins_keys.index(key)}
21 end
22 alias []= instance_variable_set
23 alias [] instance_variable_get
24 private :[], :[]=
25 end
26 h = {:id=>'123', :name=>'abc', :title=>'ddd'}
27 p m = My_2.new(h)
28 p m.name
実行結果
#<My_2:0xxxxxxxx @id="123", @name="abc">
"abc"
@@ins_keys を定義し19行目に if @@ins_keys.has_key?(k) を追加することで
attr_writer に「似た」役目をはたしてもらうことにした。
attr_reader は16行目にまとめた。
配列の要素を全て attr_reader にした。
配列の要素をチョイスして attr_reader したい場合はさらに2行加える必要あり。
@@ins_keys に登録されていない :title は定義されなくなった。
つまり定義したい instance 以外 が定義されるようなことはおこらないという意味で My_1 と同じ結果を得られるようになった。
ただし、My_1 ではerrorが返ってくるが My_2 では error にはならない。
error を返す必要があれば20行目直前に raise unless @@ins_keys.index(key) などすればいいのかも。
+++ メモ +++
0) self[] を initialize で使用しつつどの key を instance 変数として定義するかをちゃんと制御したい時
attr_writer で設定したつもりになっていてもそれは意味をなさないので、別途 式を用意するなどする必要がある。
あるいは最初から My_1 のような方法を採用した方がいいかも。
あるいは通常どおりに def initialize で @id = nil などと定義しておいて instance_variable_defined? を使う。
1) (派手に間違えているかもしれない)書きながら浮かんだイメージ
self.send()
は、一旦廊下に出て attr_writer ドアをノックしてから self 部屋に入るが、
self[]=
は、既にself 部屋の内側に入っていて attr_writer ドアの存在を意識していない。
といったようなイメージ。
2) git にあげてある 2010-amazon-api-jp に、上記の内容を反映する予定は未定。
続き
0 件のコメント:
コメントを投稿