Как использовать Perl Moose с плагинами для основного объекта?
Я новичок в Музе. Я должен создать объект, который должен загрузить несколько плагинов. Структура выглядит так:
- Главный объект -> некоторые общие функции
- Плагины -> расширения для основного объекта
Плагины находятся в отдельной папке на сервере. Главный объект должен загрузить плагины, инициализировать их и сохранить объект в себе. Возвращаемое значение каждого плагина должно пройти через Основной объект. Потому что главный объект должен преобразовывать каждое возвращаемое значение в структуру JSON для вызывающей стороны.
Я бы назвал что-то вроде этого:
my $main_obj = Main->new();
$main_obj->plugin('MainExtention')->get_title();
Вот мой пример кода:
Главный объект:
package Main;
use Moose;
has 'plugins' => (
is => 'rw',
);
has 'title' => (
is => 'rw',
isa => 'Str',
reader => '_get_title',
);
# load plugins
sub _load_modules {
my $self = shift;
my $path = "/usr/local/apache/sites/.../Plugins";
push(@INC, $path);
my @modules = _find_modules_to_load($path);
eval {
my $plugins = {};
foreach my $module ( sort @modules) {
(my $file = $module) =~ s|::|/|g;
(my $modname = $module) =~ s/.*?(\w+$)/$1/;
require $file . '.pm';
my $obj = $module->new();
$plugins->{$modname} = $obj;
1;
}
$self->plugins($plugins);
} or do {
my $error = $@;
warn "Error loading modules: $error" if $error;
};
}
# read plugins
sub _find_modules_to_load {
my ($dir) = @_;
my @files = glob("$dir/*.pm");
my $namespace = $dir;
$namespace =~ s/\//::/g;
# Get the leaf name and add the System::Module namespace to it
my @modules = map { s/.*\/(.*).pm//g; "${namespace}::$1"; } @files;
return @modules;
}
sub BUILD {
my $self = shift;
$self->_load_modules();
}
sub get_title {
return 'main title'
}
1;
__PACKAGE__->meta->make_immutable;
Плагин MainExtention в каталоге "/usr/local/apache/sites/.../Plugins":
package MainExtention;
use Moose;
has 'title' => (
is => 'rw',
isa => 'Str',
default => 'Default',
);
sub get_title {
return 'MainExtention Title';
}
1;
Это работает так:
my $main = Main->new();
my $plugins = $main->plugins();
print $plugins->{'MainExtention'}->get_title();
Но это не то, что у меня будет:) Я получу возврат плагина не напрямую от плагина, а от Главного объекта. У кого-нибудь есть идея? Второй вопрос: есть ли более простой способ загрузить плагины? Как?
1 ответ
Для загрузки плагинов я бы рекомендовал использовать Module:: Pluggable, который может загружать несколько пакетов из каталога и создавать их экземпляры (или нет) по мере необходимости.
Если вам нужно, чтобы основной объект обернул плагин, просто определите метод для основного объекта, чтобы он делал все, что вам нужно:
package Main;
use Moose;
# Adds a plugins() method to Main that returns a list of all loaded plugin packages
use Module::Pluggable search_dirs => [ "/usr/local/apache/sites/.../Plugins" ];
# Used to store the plugins after ->new is called on each package
has loaded_plugins => (
is => 'rw',
isa => 'HashRef[Object]',
lazy_build => 1,
traits => [ 'Hash' ],
handles => { _plugin => 'get' },
);
# Constructor for loaded_plugins, implied by lazy_build => 1
sub _build_loaded_plugins {
my ($self) = @_;
my %loaded_plugins;
for my $plugin ($self->plugins) {
$loaded_plugins{$plugin} = $plugin->new;
}
return \%loaded_plugins;
}
# Method for getting the processed title from any plugin
sub get_plugin_title {
my ($self, $name) = @_;
my $plugin = $self->_plugin($name);
my $title = $plugin->get_title;
# process the title according to whatever Main needs to do...
...
return $title;
}
Затем ваш код:
my $main = Main->new();
print $main->get_plugin_title('MainExtension');
Если вы используете Moose, я мог бы также предложить сделать так, чтобы все ваши плагины выполняли роль, которая поможет вам определить, правильно ли реализован плагин.