読者です 読者をやめる 読者になる 読者になる

Perl日記

PerlとかRubyとかPHPとかPythonとか

読み込んだモジュールを全削除するClass::Unload::Allというのを書いてみた

前回の若干続き。
@さんからClass::Unloadなるものがあることを教えてもらった
http://search.cpan.org/~ilmari/Class-Unload-0.07/lib/Class/Unload.pm
podに

use Class::Unload;
use Class::Inspector;

use Some::Class;

Class::Unload->unload( 'Some::Class' );
Class::Inspector->loaded( 'Some::Class' ); # Returns false

require Some::Class; # Reloads the class

とある通り、指定したpackage名をuseしてからunloadする以外のプログラム全体の空間から削除することができる。
(つまりuse MODULE;からunload('MODULE')までの空間ではuseしたモジュールは使用可能である)


ただ、本当にそのpackageで使用するモジュールを閉じ込めるためには呼んだモジュール名分unloadを呼ばなければならない。
これをファイルの最後に追加する。

for my $module (qw/ Module1 Module2 Module3 … /) {
    Class::Unload->unload($module);
}

useしたモジュールが増えればこのunloadする配列に加えないといけないよなーと思って、もうちょっとなんとかならないかと作ってみた。



<追記>
B::Hooks::EndOfScopeモジュールを使用して、下に書いていたことを解消できた。

package Hoge;
use Class::Unload::All;
use Some::Class1;
sub hoge {…}
1;

</追記>


package Hoge;
use Some::Class1;
use Some::Class2;
use Some::Class3;
use Some::Class4;
use Some::Class5;

sub hoge {…}
sub fuga {…}
sub piyo {…}

use Class::Unload::All;
1;

いろいろ試してみたけど、お尻に書くという制限だけ残ってしまった。
(そうしないと読み込んだモジュールがあいだで動かない。)
だがまあとりあえず動く。
もうちょっときれいに書けないかな。



蛇足的メモ。
本当は普通のuseのようにファイルの頭に書いてもよいようにしたかったのだけれど、
BEGIN、UNITCHECK、CHECK、INITはシンボルテーブル越しには動かないというのがわかった。

package Class::Unload::All;
sub import {
  my $class = caller;
  *{"$class\::INIT"} = sub {
    …
  };
}
1;

importはBEGINで動いてるから、INITみたいなコンパイルフェーズ終了後ならいけるように思ったが実際にはだめだった。
Page not found | The Effective Perler こちらのページの図が分かりやすかった。
また、ちょうどBlack magicという見出しでDevel::Hookが紹介されていて見てみたが
自分のpackageに動的にINITとかCHECKとかを差し込めるけれど他のpackageにはだめみたいだった。残念。

やっぱりこの辺まだちゃんと理解しきってないってことか。。