ここには 2 つの問題があります。
- スクリプトをスタンドアロンとして実行する場合とモジュールとして使用する場合の動作を変更するにはどうすればよいですか?
- コンパイルしたばかりのコードのパッケージ名を見つけるにはどうすればよいですか?
質問 2 に対する一般的な答えは次のとおりです。どのコンパイル ユニットにも任意の数のパッケージが含まれている可能性があるため、必要ありません。
とにかく、ここに3つの可能な解決策があります:
- モジュールに名前を付けて、ロード時に名前がわかるようにします。
- 各モジュールを中央のランデブー ポイントに登録します。
- #1 と同様ですが、プラグインの自動検出を追加します。
最も簡単な解決策は、すべての API を通常のモジュールに配置し、スタンドアロン ロジックを別のスクリプトに配置することです。
/the/location/
Module/
A.pm
B.pm
a-standalone.pl
b-standalone.pl
各スタンドアロンの基本的な外観
use Module::A;
Module::A->run();
別のスクリプトがそのコードを再利用したい場合は、そうします
use lib "/the/location";
use Module::A;
...
ロードが実行時に発生する場合は、Module::Runtime
ここで役立ちます。
use Module::Runtime 'use_module';
use lib "/the/location";
my $mod_a = use_module('Module::A');
$mod_a->run();
a-standalone.pl
と の内容をModule/A.pm
別々のファイルに配置することは厳密には必要ではありませんが、その方が明確です。スクリプトとして使用されている場合にのみ、モジュール内のコードを条件付きで実行したい場合は、unless(caller)
トリックを利用できます。
もちろん、これはすべてトリックです。ここでは、モジュール名からファイル名を決定しますが、その逆ではありません。既に述べたように、これはできません。
できることは、各モジュールを特定の事前定義された場所に登録することです。
Rendezvous::Point->register(__FILE__ => __PACKAGE__);
もちろん、スタンドアロン バージョンは、存在しない可能性から保護する必要があるため、次のRendezvous::Point
ようになります。
if (my $register = Rendezvous::Point->can("register")) {
$register->(__FILE__ => __PACKAGE__);
}
ええ、これはばかげており、DRY に違反しています。Rendezvous::Point
それでは、これを処理するモジュールを作成しましょう。
で/the/location/Rendezvous/Point.pm
:
package Rendezvous::Point;
use strict; use warnings;
my %modules_by_filename;
sub get {
my ($class, $name) = @_;
$modules_by_filename{$name};
}
sub register {
my ($file, $package) = @_;
$modules_by_filename{$file} = $package;
}
sub import {
my ($class) = @_;
$class->register(caller());
}
ここでuse Rendezvous::Point;
、呼び出し元のパッケージを登録し、モジュール名を絶対パスで取得できるようにします。
さまざまなモジュールを使用するスクリプトは、次のようになります。
use "/the/location";
use Rendezvous::Point (); # avoid registering ourself
my $prefix = "/the/location";
for my $filename (map "$prefix/$_", qw(Module/A.pm Module/B.pm)) {
require $filename;
my $module = Rendezvous::Point->get($filename)
// die "$filename didn't register itself at the Rendezvous::Point";
$module->run();
}
次に、のような完全な機能を備えたプラグイン システムがありますModule::Pluggable
。このシステムは、Perl モジュールが存在する可能性のあるすべてのパスを調べることによって機能し、特定のプレフィックスがある場合はそれらをロードします。それを使用したソリューションは次のようになります。
/the/location/
MyClass.pm
MyClass/
Plugin/
A.pm
B.pm
a-standalone.pl
b-standalone.pl
すべてが最初のソリューションと同じです。スタンドアロン スクリプトは次のようになります。
use lib "/the/location/";
use MyClass::Plugin::A;
MyClass::Plugin::A->run;
しかし、MyClass.pm
次のようになります。
package MyClass;
use Module::Pluggable require => 1; # we can now query plugins like MyClass->plugins
sub run {
# Woo, magic! Works with inner packages as well!
for my $plugin (MyClass->plugins) {
$plugin->run();
}
}
もちろん、これには特定の命名スキームが必要ですが、可能なプラグインを自動検出します。