Представьте, что у вас есть модуль X
, функции которого доступны пользователю через интерфейс командной строки. Такой модуль сам по себе мало что делает, но он позволяет другим людям создавать модули, похожие на плагины, которые они могут подключать к модулю X
. В идеале плагины можно было бы использовать через интерфейс командной строки X
.
Таким образом, мой вопрос:
Что вам нужно сделать, чтобы подключить любой функционал
плагин может предоставлять интерфейс командной строки X
?
Это означает, что плагин должен будет предоставить некоторую структуру, описывающую команду, то, что нужно вызвать, и, надеюсь, сообщение об использовании для плагина. Поэтому, когда вы запускаете интерфейс командной строки X
, команда плагина и справочное сообщение отображаются в обычном справочном сообщении интерфейса командной строки X
.
main.p6
:
use Hello;
use Print;
multi MAIN('goodbye') {
put 'goodbye'
}
lib/Hello.pm6
:
unit module Hello;
our %command = %(
command => 'hello',
routine => sub { return "Hello" },
help => 'print hello.'
);
lib/Print.pm6
:
unit module Print;
our %command = %(
command => 'print',
routine => sub { .print for 1..10 },
help => 'print numbers 1 through 10.'
);
Программа main.p6
представляет собой упрощенную версию этого сценария. Я хотел бы интегрировать информацию, предоставленную как Hello.pm6
, так и Print.pm6
через
их соответствующие переменные %command
в две новые подпрограммы MAIN
в main.p6
.
Это возможно? Если да, то как мне добиться этого?
Основная линия @HåkonHægland настраивается только один раз (при первом обнаружении) - если вы импортируете MAIN
из двух разных модулей, будет использоваться только один.
Это выглядит довольно специфично для вопроса StackOverflow, но я все равно попробую. Здесь есть несколько проблем. Первый — зарегистрировать команды как таковые, чтобы MAIN
мог выдать сообщение о том, что «это делает то», а второй — фактически выполнить команду. Если оба могут быть известны во время компиляции, это, вероятно, можно исправить. Но давайте посмотрим, как будет работать реальный код. Я просто сделаю первую часть, а остальное оставлю в качестве упражнения.
Во-первых, %command
нужно как-то экспортировать. Вы не можете сделать это так, как вы делаете это сейчас. Во-первых, потому что он явно не экспортируется; если бы это было так, вы бы получили несколько символов с одним и тем же именем во внешней области. Поэтому нам нужно изменить это на класс, чтобы фактические символы были лексическими для класса и не загрязняли внешнюю область видимости.
unit class Hello;
has %.command = %(
command => 'hello',
routine => sub { return "Hello" },
help => 'print hello.'
);
(То же самое относится и к Print
)
Пока у нас есть это, остальное не так сложно, только мы должны использовать самоанализ, чтобы узнать, что там на самом деле, как небольшой хак:
use Hello;
use Print;
my @packages= MY::.keys.grep( /^^<upper> <lower>/ );
my @commands = do for @packages -> $p {
my $foo = ::($p).new();
$foo.command()<command>
};
multi MAIN( $command where * eq any(@commands) ) {
say "We're doing $command";
}
Мы проверяем таблицу символов в поисках пакетов, начинающихся с заглавной буквы, за которой следует другая не заглавная буква. Так получилось, что нас интересуют только пакеты, но, конечно, если вы хотите использовать это как механизм плагина, вы должны просто использовать любой шаблон, который будет уникальным для них.
Затем мы создаем экземпляры этих новых пакетов и вызываем автоматически сгенерированный метод команды, чтобы получить имя команды. И это именно то, что мы используем, чтобы проверить, правильно ли мы выполняем команду в подпрограмме MAIN
, используя подпись where
, чтобы проверить, действительно ли используемая строка находится в списке известных команд.
Так как функции и все остальное доступно также из @packages
, их фактический вызов (или отправка дополнительного сообщения или что-то еще) остается в качестве упражнения.
Обновлять: вы можете попробовать этот другой ответ StackOveflow как альтернативный механизм для подписей в модулях.
Я попытался экспортировать новые мультифункции MAIN из модулей, но получил ошибку:
An anonymous routine may not take a multi declarator
. Если я не использовалmulti
, я получил ошибку:Redeclaration of routine 'MAIN'
... Так что, может быть, их нельзя добавить динамически?