class MyException < Exception
と、Exceptionクラスを継承しがち。けれど、Exceptionクラスの配下には、
fatalとか、NoMemoryErrorなど、致命的なものと、回復可能かもしれないもの(StandardError)が
きちんと分けられているのです。
従って、自分のコード中で処理したい例外は、
class MyException < StandardError
と書くのが良い。その根拠として、rubyのrescue文は、
begin
(例外が発生)
rescue
(StandardError、またはその派生クラスがここで補足される)
end
と、補足する例外クラスを指定しないと、デフォルトでStandardErrorを捕まえるように設計されています。
このせいで、rubyunitで補足できない例外があって長時間苦しみました。
sqlite3-rubyはExceptionから派生したクラスで例外を出すので、もう大変。
rubyでは、rescueで補足されなかった例外があると、
leo@celia:~/../ruby/depot> rake test_units
/usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.1.0/lib/sqlite3/resultset.rb:76:in `check': cannot rollback - no transaction is active (SQLite3::SQLException)
from /usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.1.0/lib/sqlite3/resultset.rb:68:in `commence'
from /usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.1.0/lib/sqlite3/resultset.rb:61:in `initialize'
from /usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.1.0/lib/sqlite3/statement.rb:158:in `execute'
from /usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.1.0/lib/sqlite3/database.rb:211:in `execute'
from /usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.1.0/lib/sqlite3/database.rb:186:in `prepare'
from /usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.1.0/lib/sqlite3/database.rb:210:in `execute'
from /usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.1.0/lib/sqlite3/database.rb:620:in `rollback'
from /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/connection_adapters/sqlite_adapter.rb:179:in `rollback_db_transaction'
... 10 levels...
from /usr/local/lib/ruby/1.8/test/unit/autorunner.rb:200:in `run'
from /usr/local/lib/ruby/1.8/test/unit/autorunner.rb:13:in `run'
from /usr/local/lib/ruby/1.8/test/unit.rb:285
from /usr/local/lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake/rake_test_loader.rb:5
というように、途中が省略されて内容が表示されるのです。さらに、C++やJavaの影響でrescue文ですべての例外が補足できると思いこんでいたために、デバッグが困難を極めました。例外をrescueで捕まえることができれば、Exception.backtraceを実行して完全表示できるのですが。
(思考の過程)
- raise 文を、コードの動作を確認しながら、ruby on railsやsqlite3-rubyのソースコードのあちこちに埋め込んで、backtraceを表示させようと試みるが、なぜかうまくいかない。ここで数時間。
- どんな状況でこの例外表示が省略されるのかを、テストコードを書いて調べてみた
- 普通に例外をrescueしてputs $@とするだけで、full traceが表示される。。。
- どうやら、rescueしないで、スクリプトを抜ける場合に省略表示が入る模様。
- それでは、この省略表示をやめる方法があると考え、小1時間調べる。
- 結局、そんな設定はなく、rubyのソースコードに定数値で書かれていて不変であることが判明
- rubyに手をいれるのは怖いので、rubyunitの動作をもう一度確認。raise文もあるし、補足できないような例外があるとは思えない。
- rubyのバグかと思い調べるが、そんなことはなさそうだった
- 基本に戻って、Programming Ruby 2nd Editionを読んでみる。
- rescueはStandardErrorをデフォルトで捕らえると書いてある。。。
- 自分で作成する例外クラスは、StandardErrorから派生すること、と書いてある…
- sqlite3-rubyのソースコードの例外定義部分を見てみる
- sqlite3-rubyはすべての例外クラスをExceptionから派生していた…
- この部分を、StandardErrorに変更
- RubyUnitが無事に例外を補足。エラーのフルトレースが表示される。
まだ、ruby歴半月なのです。
0 件のコメント:
コメントを投稿