昨日紹介したROXMLを
Railsで製作中のシステムに入れてみようとして、派手にコケる。エラーメッセージをたどっていくと、どうもroxml.rbの
412 def assert_accessor(name) 413 @tag_accessors = [] unless @tag_accessors 414 raise "Accessor #{name} is already defined as XML accessor in class #{self}" if @tag_accessors.include?(name) 415 @tag_accessors << name 416 end
これが原因らしい。1回目のアクセス時は良くても、2回目以降にアクセスしたときに同じ定義をすることになるので、「ちょっと待て、お前はもうこれを定義してるぞ」ということらしい。とにかく、この@tag_accessorsの中身がユニーク(重複ナシ)であればいいっぽいので、
412 def assert_accessor(name) 413 @tag_accessors = [] unless @tag_accessors 414 @tag_accessors << name unless @tag_accessors.include?(name) 415 end
に変更。
とりあえずアドホック的に解決したけど、恐らくこの作者さん、RailsみたいなWebアプリで動かすことを想定してないんだろうなぁ…。
(追記)
上で挙げた方法だと、万が一定義が重複してた時のエラーが出せなくなる。つーことで、その辺の修正も施したものとオリジナルのdiffをとってみた。アドホックさはまだ抜けないけど、モジュールが呼び出されたときにインスタンス変数(ここでは@tag_accessors)が初期化されればいいので、とりあえずの動作に問題はないだろう。
--- roxml.rb +++ roxml.rb @@ -401,7 +401,13 @@ def tag_refs @xml_refs || [] end - + + #initialize this flag when ROXML module is included + def clear + @@flag = 0 + end + module_function :clear + private def add_ref(xml_ref) @@ -410,7 +416,8 @@ end def assert_accessor(name) - @tag_accessors = [] unless @tag_accessors + @tag_accessors = [] unless @tag_accessors && @@flag == 1 + @@flag = 1 raise "Accessor #{name} is already defined as XML accessor in class #{self}" if @tag_accessors.include?(name) @tag_accessors << name end @@ -444,6 +451,7 @@ # def included(klass) super + ROXML_Class.clear#initialize flag klass.__send__(:extend, ROXML_Class) end end