From: SASADA Koichi Date: 2004-10-13T14:35:39+09:00 Subject: [ruby-list:40085] require 中に例外が発生したときの対処について  ささだです。  あるファイルを require 中に、たとえばその先で require する ものがインストールされていなかった場合など、例外が発生します。 そのとき、$" には、最初に require したファイルが記録されます ので、次に require しても、読み込みを行いません。たとえば、 例外発生要因を取り除いた後にもう一度 require するような場合、 これに気づかずにはまるかもしれません(というか、はまった)。  このような可能性がある場合の対処について、定石などあれば教 えてください。  例をもうちょっと具体的に書きます。 ・N というアプリケーションがありました ・N はプラグイン機能を有しています ・N はサーバアプリケーションで、基本的にずーっと動いています (終了させたくありません) ・N は P というプラグインをロードしようとしました ・P は ライブラリ L1 を require するのですが、L1 は L2 を必要としました 生憎その環境には L2 がインストールされていませんでした ・require L2 で例外が発生しますが、N はその例外をフックし、 P のロード失敗と、その原因をユーザに知らせ、動き続けました ・そこで、ユーザは L2 をインストールしてから、再度 P をロード しようとしますが、P が L1 を require しようとしたところ、 すでに L1 が $" に入っていたため、L1 がきちんと require できませんでした なので、P の実行は失敗してしまいました(不完全な L1 を利用 するため)  通常(?)のアプリケーションの場合、require L2 の段階で例 外が発生し、アプリケーション自体が終了するためこのような問題 はあまり気にならないと思うのですが、サーバ系アプリケーション などでは問題になるかと思います。  さて、この例では、アプリケーション N はどんな対策を取れば いいんでしょうか。何か定石でもありましたらご教示ください。 ・考えてみた解決案1  プラグイン(P)中で require する場合、特別な関数 require_in_plugin(libname) を用意する。この関数では、require に失敗すると、その libname を $" から取ってしまう。 def require_in_plugin libname begin require libname rescue Exception $".delete libname raise end end この解決案の問題点:  L1 中で例外が発生した場合、なんとなくそれでいいような気が するのだけれど、たとえば L1 が L2、L2 が L3, ... LN-1 が LN を require していたとき、LN 中で例外が発生した場合、L1 のみ が $" から除かれ、再度 L1 を require しても、L2 は require されず、問題の LN は require されない。 ・考えてみた解決案2  ということで、require_in_plugin は $" を保存し、require を 行う。もし require 中で例外が発生した場合、$".replace(old_libs) のように、取り替えちゃう。 def require_in_plugin libname begin old_libs = $" require libname rescue Exception $".replace old_libs raise end end (マルチスレッドによる並行実行はあんまり考えたくない) ・考えてみた解決案3  そんなケースはレアなので、あきらめる。  なにか、スマートな方法があれば教えてください。 -- // SASADA Koichi at atdot dot net // N: nadoka P: rss_check.nb L1: rss_check.rb L2: zlib  別に困らないんで、Kernel.require を上記関数で書き換えてしま うってのもいいのかなぁ