namespace::autocleanについて調べた
読んでます。
- 作者: 冨田尚樹,タナカユカリ
- 出版社/メーカー: ワークスコーポレーション
- 発売日: 2011/04/08
- メディア: 単行本(ソフトカバー)
- 購入: 20人 クリック: 2,028回
- この商品を含むブログ (20件) を見る
その中でずっとなんとなく使っていたnamespace::autocleanについても言及されていたので、せっかくなのでちゃんと調べてみたメモ。
namespace::autoclean - search.cpan.org
主要機能
そもそもこのモジュールどこでよく見たかって、Catalystの5.8系統でスケルトンを作成するとアプリケーションクラスとコントロールクラスにデフォルトで書かれているからだった。
(どのバージョンからこうなったんだろ?)
$ catalyst.pl Ctest
Ctest/lib/Ctest.pm
package Ctest; use Moose; use namespace::autoclean; …
Ctest/lib/Ctest/Controller/Root.pm
package Ctest::Controller::Root; use Moose; use namespace::autoclean; …
で、namespace::autocleanの主要な機能は、上記本やPODの最初に書かれているとおり、exportされてくるサブルーチンの使用可能スコープをそのパッケージ(クラス)に留めること。
何もしなければ、Catalystスケルトンの例でMooseが寄越してくるhas()とか、extends()とか、with()とかが、Ctest->has()として使用可能である。
そんな使い方もどうかと思うが、「可能である限りどこかで使用されてしまうリスクは消えない」。
だから使えないようにしてリスクは消す。
package Foo; use namespace::autoclean; use Some::Package qw/imported_function/; sub bar { imported_function('stuff') } # later on: Foo->bar; # works Foo->imported_function; # will fail. imported_function got cleaned after compilation
ただ「Foo内で」通常の「::」でコールすることは、できてしまう。
Foo::imported_function(); # works!
(だからbar()の中で使える訳か?)
Foo.pmをコールする側では、見えない。
use Foo; Foo->bar; # ok Foo::imported_function(); # abort! # Undefined subroutine &Foo::imported_function
どうやって実現させている?
ソース見るとB::Hooks::EndOfScopeのon_scope_end()で「コンパイル終了後に実行」を実現しているみたい。なるほど。
その中で、Class::MOP::Classとnamespace::cleanを使って、パッケージ内のサブルーチンシンボルを消していく、という感じか。
ほかには?
cleanee
-cleaneeスイッチを使って、自作ライブラリの中に同様の機能を組み込める。
Hoge.pm
package Hoge; use namespace::autoclean (); # no cleanup, just load sub import { my $caller = caller; # package name namespace::autoclean->import( -cleanee => $caller, ); } 1;
Fuga.pm
package Fuga; use Hoge; use Carp qw/ carp /; Fuga->carp; # will fail! 1;
also
-alsoによって追加でcleanするサブルーチンを指定できる。
also.tより
{ package Foo; use namespace::autoclean -also => ['bar']; use namespace::autoclean -also => 'moo'; sub bar {} sub moo {} sub baz {} } ok(!Foo->can('bar'), '-also works'); ok(!Foo->can('moo'), '-also works with string argument'); ok( Foo->can('baz'), 'method not specified in -also remains');
どちらかというと、以下のような使い方だと思う。
cleanee.tより
{ package My::Cleaner; use namespace::autoclean (); sub import { namespace::autoclean->import( -cleanee => scalar(caller), -also => 'blast', ); *{Foo::boom} = sub { 'boom' }; } } { package Foo; BEGIN { My::Cleaner->import } # use My::Cleaner tries to load it from disk sub explode { 'explode' } sub blast { 'blast' } } ok( Foo->can('explode'), 'locally defined methods still work'); ok(!Foo->can('boom'), 'imported functions removed'); ok(!Foo->can('blast'), '-also methods removed');
Fooの中でimportされずalsoの対象でもないblast()だけが使えると。すごい。
まとめ
もしパッケージ内でexport「される」サブルーチンがあるなら、素直にnamespace::autocleanしておけばよい。
もしパッケージ内でexport「する」サブルーチンがあるなら、importでnamaspace::autoclean->importして消してあげてもよい。
あとClass::MOP::Classは魔法だと思った。
参考:
はてなブログ