场景
假设您有一个模块X,其功能可以通过命令行接口提供给用户。这样的模块本身做的并不多,但它允许其他人创建类似插件的模块,他们可以连接到模块X。理想情况下,这些插件可以通过X的CLI来使用。
因此,我的问题是:
要将插件提供给X**'s CLI的任何功能连接起来,您需要做什么?**
这意味着插件需要提供一些结构来描述命令,需要调用什么,希望是插件的使用消息。因此,当您运行X的CLI时,插件的命令和帮助消息将显示在常规的X的CLI帮助消息中。
示例
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.p6中的两个新的MAIN子体中。
这个是可能的吗?如果是这样的话,我怎样才能做到呢?
发布于 2019-07-16 08:56:49
对于一个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中获得,所以实际上调用它们(或者提供额外的消息或其他什么)是作为练习留下的。
更新:您可能希望签出this other StackOveflow answer作为模块中签名的替代机制。
https://stackoverflow.com/questions/57041053
复制相似问题