创建和使用模块§

模块通常是一个或多个源文件,它们公开 Raku 结构 [1]

模块通常是包(角色语法)、子例程,有时还有 变量。在 Raku 中,模块也可以指用 module 关键字声明的一种包类型(参见 模块包 和下面的示例),但这里我们主要指的是“模块”作为命名空间中的一组源文件。

查找和安装模块。§

zef 是用于在 Raku 中安装模块的应用程序。模块列在 Raku 生态系统 中,可以在那里搜索,也可以使用 zef search 从命令行搜索。

zef search WWW

将返回一个包含名称中包含 WWW 的模块列表,例如。然后,

zef install WWW

将安装具有该特定名称的模块,如果它尚未安装 [2]

基本结构§

Raku 中的模块分发(在一组相关源文件的意义上)与 Perl 家族语言中的任何分发具有相同的结构:有一个主项目目录,其中包含一个 README 和一个 LICENSE 文件,一个 lib 目录用于源文件,这些源文件可以单独称为模块,也可以使用 module 关键字定义模块 [3] ,一个 t 目录用于测试,以及一个可选的 bin 目录用于可执行程序和脚本。

参见 文件名扩展,了解各种文件类型的当前和历史扩展。

加载和基本导入§

加载模块会使在同一命名空间中声明的包在加载器的文件范围内可用。从模块导入会使导出的符号在导入语句的词法范围内可用。

need§

need 在编译时加载 compunit

need MyModule;

命名空间中定义的任何包也将可用。

# MyModule.rakumod 
unit module MyModule;
 
class Class {}

MyModule::Class 将在加载 MyModule 时定义,您可以直接使用它的完全限定名 (FQN)。这样定义的类和其他类型不会自动导出;如果您想使用它的简短名称,则需要显式导出它。

# MyModule.rakumod 
unit module MyModule;
 
class Class is export {}

然后

use MyModule;
 
my $class = Class.new();
say $class.raku;

use§

use 在编译时加载并从 compunit 导入。它将查找以 .rakumod 结尾的文件。参见 这里,了解运行时将查找模块的位置。

use MyModule;

这等同于

need MyModule;
import MyModule;

另请参见 选择性导入,以限制您导入的内容。

require§

require 在运行时加载 compunit 并导入明确的符号。

say "loading MyModule";
require MyModule;

如果您将 compunit 名称放在间接查找中,则它可以位于运行时变量中。

my $name = 'MyModule';
require ::($name);

加载的模块提供的符号不会导入到当前作用域中。您可以使用 动态查找动态子集 通过提供符号的完全限定名来使用它们,例如

require ::("Test");
my &mmk = ::("Test::EXPORT::DEFAULT::&ok");
mmk('oi‽'); # OUTPUT: «ok 1 - ␤»

ok 的 FQN 是 Test::EXPORT::DEFAULT::&ok。我们将其别名为 mmk,以便我们可以在当前作用域中使用 Test 提供的该符号。

要导入符号,您必须在编译时定义它们。注意:require 的作用域是词法范围的。

sub do-something {
   require MyModule <&something>;
   say ::('MyModule'); # MyModule symbol exists here 
   something() # &something will be defined here 
}
say ::('MyModule'); # This will NOT contain the MyModule symbol 
do-something();
# &something will not be defined here

如果 MyModule 没有导出 &something,那么 require 将失败。

带有编译时符号的 require 将安装一个占位符 package,该占位符将更新为加载的模块、类或包。请注意,即使 require 无法加载模块,占位符也会保留。这意味着检查是否已加载此类模块是错误的。

# *** WRONG: *** 
try require Foo;
if ::('Foo'~~ Failure { say "Failed to load Foo!"}
# *** WRONG: ***

由于编译时安装的包导致 ::('Foo') 永远不会是 Failure。正确的方法是

# Use return value to test whether loading succeeded: 
(try require Foo=== Nil and say "Failed to load Foo!";
 
# Or use a runtime symbol lookup with require, to avoid compile-time 
# package installation: 
try require ::('Foo');
if ::('Foo'~~ Failure {
    say "Failed to load Foo!";
}

在当前(6.d)版本的语言中,require 的符号不再是可传递暴露的,这意味着您需要从最初声明符号的模块导入符号,而不是从导入符号的模块导入符号。

词法模块加载§

Raku 非常小心地避免全局状态,也就是说,无论您在模块中做什么,它都不应该影响其他代码。例如,这就是为什么子例程定义默认情况下是词法范围(my)的原因。如果您希望其他人看到它们,您需要明确地将它们设置为 our 范围或导出它们。

类默认情况下是导出的,假设加载模块在您无法访问其包含的类时没有太大用处。因此,加载的类仅在最初加载它们的范围内注册 [4]。这意味着我们必须在实际使用类的每个范围内 use 它。

use Foo;           # Foo has "use Bar" somewhere. 
use Bar;
my $foo = Foo.new;
my $bar = Bar.new;

导出和选择性导入§

is export§

包、子例程、变量、常量和枚举通过使用 is export 特性进行导出(另请注意用于指示作者和版本的标签)。

unit module MyModule:ver<1.0.3>:auth<John Hancock ([email protected])>;
our $var is export = 3;
sub foo is export { ... };
constant FOO is export = "foobar";
enum FooBar is export <one two three>;
 
# for multi methods, if you declare a proto you 
# only need to mark the proto with is export 
proto quux(Str $x|is export { * };
multi quux(Str $x{ ... };
multi quux(Str $x$y{ ... };
 
# for multi methods, you only need to mark one with is export 
# but the code is most consistent if all are marked 
multi quux(Str $xis export { ... };
multi quux(Str $x$yis export { ... };
 
# Packages like classes can be exported too 
class MyClass is export {};
 
# If a subpackage is in the namespace of the current package 
# it doesn't need to be explicitly exported 
class MyModule::MyClass {};

与所有特性一样,如果应用于例程,is export 应出现在任何参数列表之后。

sub foo(Str $stringis export { ... }

您可以将命名参数传递给 is export 以对要导出的符号进行分组,以便导入者可以选择。有三个预定义的标签:ALLDEFAULTMANDATORY

# lib/MyModule.rakumod 
unit module MyModule;
sub bag        is export             { ... }
# objects with tag ':MANDATORY' are always exported 
sub pants      is export(:MANDATORY{ ... }
sub sunglasses is export(:day)       { ... }
sub torch      is export(:night)     { ... }
sub underpants is export(:ALL)       { ... }
# main.raku 
use lib 'lib';
use MyModule;          # bag, pants 
use MyModule :DEFAULT# the same 
use MyModule :day;     # pants, sunglasses 
use MyModule :night;   # pants, torch 
use MyModule :ALL;     # bag, pants, sunglasses, torch, underpants 

注意:目前,如果模块作者没有为此提供规定,用户无法导入单个对象,并且目前这不是一项简单的任务(请参阅 RT #127305)。作者可以提供此类访问权限的一种方法是为每个 export 特性提供其自己的唯一标签。(并且标签可以是对象名称!)。然后,用户可以(1)导入所有对象

use Foo :ALL;

或(2)选择性地导入一个或多个对象

use Foo :bar:s5;

注意

1. 导出子例程上的 :MANDATORY 标签确保无论使用程序是否添加任何标签,它都将被导出。

2. 所有没有显式标签的导出子例程隐式地为 :DEFAULT

3. 模块名称之后和标签之前的空格是必需的。

4. 可以使用多个导入标签(用逗号分隔)。例如

# main.raku 
use lib 'lib';
use MyModule :day:night# pants, sunglasses, torch 

5. 可以在 export 特性中使用多个标签,但它们必须用逗号或空格分隔,但不能同时使用两者。

sub foo() is export(:foo :s2 :net{}
sub bar() is export(:bar:s3:some{}

UNIT::EXPORT::*§

在表面之下,is export 将符号添加到 EXPORT 命名空间中 UNIT 范围的包中。例如,is export(:FOO) 将将目标添加到 UNIT::EXPORT::FOO 包中。这就是 Raku 用于决定要导入什么内容的实际方法。

unit module MyModule;
 
sub foo is export { ... }
sub bar is export(:other{ ... }

与以下相同

unit module MyModule;
 
my package EXPORT::DEFAULT {
    our sub foo { ... }
}
 
my package EXPORT::other {
    our sub bar { ... }
}

对于大多数目的,is export 就足够了,但当您想要动态生成导出符号时,EXPORT 包很有用。例如

# lib/MyModule.rakumod 
unit module MyModule;
 
my package EXPORT::DEFAULT {
   for <zero one two three four>.kv -> $number$name {
      for <sqrt log> -> $func {
         OUR::{'&' ~ $func ~ '-of-' ~ $name } := sub { $number."$func"() };
      }
   }
}
# main.raku 
use MyModule;
say sqrt-of-four# OUTPUT: «2␤» 
say log-of-zero;  # OUTPUT: «-Inf␤» 

EXPORT§

您可以使用 EXPORT 子例程导出任意符号。EXPORT 必须返回一个 Map,其中键是符号名称,值是所需的值。名称应包含与关联类型相关的符号(如果有)。

# lib/MyModule.rakumod 
 
class MyModule::Class { }
 
sub EXPORT {
    Map.new:
      '$var'      => 'one',
      '@array'    => <one two three>,
      '%hash'     => %one => 'two'three => 'four' ),
      '&doit'     => sub { say 'Greetings from exported sub' },
      'ShortName' => MyModule::Class
}

将从这个主文件使用

# main.raku 
use lib 'lib';
use MyModule;
say $var;          # OUTPUT: «one␤» 
say @array;        # OUTPUT: «(one two three)␤» 
say %hash;         # OUTPUT: «{one => two, three => four}␤» 
doit();            # OUTPUT: «Greetings from exported sub␤» 
say ShortName.new# OUTPUT: «MyModule::Class.new␤» 

请注意,EXPORT 不能在 内声明,因为它属于编译单元而不是包。

UNIT::EXPORT 包处理传递给 use 的命名参数,EXPORT 子例程处理位置参数。如果将位置参数传递给 use,它们将被传递给 EXPORT。如果传递了位置参数,模块将不再导出默认符号。您仍然可以通过将 :DEFAULT 与位置参数一起传递给 use 来显式导入它们。

# lib/MyModule 
 
class MyModule::Class {}
 
sub EXPORT($short_name?{
    Map.new: do $short_name => MyModule::Class if $short_name
}
 
sub always is export(:MANDATORY{ say "works" }
 
#import with :ALL or :DEFAULT to get 
sub shy is export { say "you found me!" }

从这个主程序使用

# main.raku 
use lib 'lib';
use MyModule 'foo';
say foo.new(); # OUTPUT: «MyModule::Class.new␤» 
 
always();      # OK   - is imported 
shy();         # FAIL - «shy used at line 8. Did you mean 'say'?» 

您可以将 EXPORT 与类型捕获结合使用以获得有趣的效果。此示例创建一个 ? 后缀,它只对 Cool 有效;请注意,通过使用 $_ 作为参数,我们不需要在例程主体中使用变量,只需使用 .so,默认情况下对主题变量起作用。

# lib/MakeQuestionable.rakumod 
sub EXPORT(::Questionable{
    my multi postfix:<?>(Questionable $_{ .so };
    Map.new: '&postfix:<?>' => &postfix:<?>,
}

从这里使用

use lib 'lib';
use MakeQuestionable Cool;
say ( 0?1?{}?, %=> "b" )? ).join(' '); # OUTPUT: «False True False True␤» 

自省§

要列出模块的导出符号,首先查询模块支持的导出标签。

use URI::Escape;
say URI::Escape::EXPORT::.keys;
# OUTPUT: «(DEFAULT ALL)␤»

然后使用您喜欢的标签并按名称选择符号。

say URI::Escape::EXPORT::DEFAULT::.keys;
# OUTPUT: «(&uri-escape &uri-unescape &uri_escape &uri_unescape)␤» 
my &escape-uri = URI::Escape::EXPORT::DEFAULT::<&uri_escape>;

小心不要将 sub EXPORT 放在 unit 声明符 之后。如果您这样做,它将成为您包中的一个子例程,而不是特殊的导出子例程。

unit module Bar;
sub EXPORT { Map.new: Foo => &say } # WRONG!!! Sub is scoped wrong 

其定义 中所述,sub EXPORT编译单元的一部分,而不是包的一部分。所以这是正确的方法

sub EXPORT { Map.new: Foo => &say } # RIGHT!!! Sub is outside the module 
unit module Bar;

查找已安装的模块§

由模块安装程序来决定 compunit 预期模块放置的位置。将由 发行版 提供一个位置,以及当前主目录。无论如何,让模块安装程序处理您的模块都是一个安全的选择。

cd your-module-dir
zef --force install .

用户可能拥有一组在正常生态系统中找不到的模块,由模块或包管理器维护,但经常需要。与其使用 use lib pragma,不如使用 RAKULIB 环境变量来指向模块位置。例如

export RAKULIB=/path/to/my-modules,/path/to/more/modules

请注意,逗号 (',') 用作目录分隔符。

当 Raku needuserequire 模块时,将搜索 RAKULIB 路径中的目录。以点开头的目录将被忽略,符号链接将被跟踪。

为了避免在模块加载时出现性能损失,您可能需要确保添加到此路径的目录不要太大;有关更多信息,请参见 此处

测试模块和发行版§

在本节中,重要的是要重复本文开头的那句话,即Raku 发行版(在其他语言中近似于模块)与Raku 模块声明 之间存在差异。原因是单个文件可能包含多个 classmodulerole 等声明,或者这些声明可能分散在不同的文件中。此外,当发布 发行版(参见 分发模块)时,可能需要提供对其他资源的访问权限,例如可调用的实用程序。

Raku 还允许单个发行版提供多个可以 use(或 require 等)的 编译单元。有关发行版的信息包含在发行版根目录中的 META6.json 文件中。有关 META6.json 的更多信息,请参见下文。发行版允许 use(或 require 等)的每个实体(也称为 编译单元)都放置在 META6.jsonprovides 部分(如下所述)。

还应注意,在编写 META6.json 文件的 depends 部分时,列出的是发行版的名称,而不是所需的 编译单元 的名称。尽管对于许多实体来说,发行版的名称和编译单元的名称相同。在提供给生态系统的发行版中,depends 列表的另一个全局影响是,包管理器(例如 zef)可以在所有可用的发行版中查找 编译单元 名称。

鉴于这种安排,rakudo 编译器或包管理器(如 zef)可以从目录中的 META6.json 文件中获取有关每个 编译单元 的所有必要信息。

这也意味着测试程序(如 PerlproveRaku 等效的 prove6)可以在发行版的根目录中运行,例如

prove -e 'raku -I.'

prove6 -I.

在这两种情况下,测试程序都会查找名为 t/ 的目录并在其中运行测试程序(参见 测试)。

上面的示例中使用 -I. 而不是 -Ilib 是很重要的,因为这样编译器会检查发行版根目录中的 META6.json 文件。

如果你刚开始开发一个模块(或一系列模块),还没有决定模块的名称或代码的存放位置(可能将其拆分成多个 *.rakumod 文件),但假设所有模块文件都在 lib/ 目录下,那么可以使用

prove6 -Ilib

这是一种欺骗行为。一个发行版可能通过基于本地文件的测试,也可能通过发布步骤,但发行版(以及它包含的模块/类/角色)可能不会自动安装。举个常见的例子,但这不是这种问题表现出来的唯一方式,如果发行版在另一个发行版的 depends 部分中列出,并且用户试图使用 zef(或使用相同测试机制的包管理器)安装另一个发行版,也许使用 CLI 命令 zef install --deps-only .,那么 zef 会根据 -I. 而不是 -Ilib 来安排依赖模块的测试。这将导致 rakudo 错误,抱怨缺少命名的 compunits,除非 META6.json 文件是正确的。

对于为发行版定义的每个 compunit,应该运行的最基本的测试是

use-ok 'MyModule';

假设在 META6.json 中有一行代码,例如

provides: { "MyModule": "lib/MyModule.rakumod" },

强烈建议使用 Test::METAmeta-ok 测试,它验证 META6.json 文件的有效性。如果你想将一个发行版(模块)添加到 Ecosystem 中,那么这个测试将应用于该发行版。

由于 meta-ok 只需要由模块的开发者/维护者测试,因此它可以放在另一个目录中的扩展测试集中,例如 /xt。这是因为 prove6(例如)默认情况下只运行 t/ 中的测试。然后,要运行扩展测试

prove6 -I. xt/

事实上,Raku 社区开发者越来越普遍地将发行版的扩展测试放在 xt/ 中,并将最小的健全性测试放在 t/ 中。扩展测试对于开发和质量控制至关重要,但它会减慢流行发行版的安装速度。

meta-ok 测试放在扩展测试目录中的另一种方法是确保它只在开发者或维护者想要运行时才运行,方法是使测试依赖于环境变量,例如,在 t/99-author-test.rakutest 中有以下代码

use Test;
 
plan 1;
 
if ?%*ENV<AUTHOR_TESTING> {
    require Test::META <&meta-ok>;
    meta-ok;
    done-testing;
} else {
    skip-rest "Skipping author test";
    exit;
}

然后,开发者在测试模块时,使用

    AUTHOR_TESTING=1 prove6 -I.

分发模块§

如果你编写了一个 Raku 模块,并且想与社区分享,我们很乐意将其列在 Raku 模块目录 中。:)

目前,有三种方法可以分发模块。无论你选择哪种方法,你的模块都将在 raku.landraku.land 网站上被索引。这三种模块分发网络或生态系统是

  • zef 生态系统 使用 fez 模块上传器 工具。使用 fez 命令是最新且可能是最简单的方法来分发你的模块,并且越来越受欢迎。

  • CPAN 这是 Perl 使用的相同生态系统。模块以 .zip.tar.gz 文件的形式上传到 PAUSE 上。

  • p6c 直到最近,它还是唯一的生态系统。它基于直接访问的 Github 仓库。它对版本控制的能力有限。

分享你的模块的过程包括两个步骤:准备模块和将模块上传到上面提到的生态系统之一。下面将详细介绍这两个步骤。

准备模块§

为了使模块在任何生态系统中都能正常工作,它需要遵循一定的结构。以下是实现方法

使用 dist 管理器§

  • fez

  • 运行以下命令

    fez init MyNew::Module
    
    # Will create the following:
    # MyNew--Module/
    # ├── lib
    # │   └── MyNew
    # │       └── Module.rakumod
    # ├── META6.json
    # └── t
    #     └── 00-use.rakutest
    
    
    • 如果你需要添加新的模块、类、资源、构建依赖项或依赖项,可以使用以下命令(这些资源将自动添加到 META6.json 中)

      fez module My::New::Module
      fez module --class My::New::Module
      fez resource xyz
      fez depends --build Build::Dependency
      fez depends Runtime::Dependency
      
      

手动§

  • 创建一个以你的模块命名的项目目录。例如,如果你的模块是 Vortex::TotalPerspective,那么创建一个名为 Vortex-TotalPerspective 的项目目录。

  • 使你的项目目录看起来像这样

    Vortex-TotalPerspective/
    ├── lib
    │   └── Vortex
    │       └── TotalPerspective.rakumod
    ├── LICENSE
    ├── META6.json
    ├── README.md
    └── bin
    │    └── vortex
    └── t
        └── basic.rakutest
    
    

    如果你的项目包含其他帮助主模块完成工作的模块,它们应该放在你的 lib 目录中,如下所示

    lib
    └── Vortex
        ├── TotalPerspective.rakumod
        └── TotalPerspective
            ├── FairyCake.rakumod
            └── Gargravarr.rakumod
    
    
  • 如果你有任何其他文件(例如模板或动态库),你希望安装这些文件以便在运行时访问它们,它们应该放在项目中的 resources 子目录中,例如

    resources
    └── templates
            └── default-template.mustache
    
    

    然后,必须在 META6.json 中引用该文件(有关 META6.json 的更多信息,请参见下文),以便将分发路径提供给程序。

    {
        "name" : "Vortex::TotalPerspective",
        "provides" : {
            "Vortex::TotalPerspective" : "lib/Vortex/TotalPerspective.rakumod"
        },
        "resources": [ "templates/default-template.mustache"]
    }
    
    

    然后,可以在模块代码内部访问附加文件。请注意,此处的示例返回一个 Distribution::Resource 对象。它 **不应** 被视为文件系统中对象的路径。

    my $template-text = %?RESOURCES<templates/default-template.mustache>.slurp;
  • 您需要在 $PATH 中执行的程序和脚本需要包含在 bin 目录中;这些脚本将被复制到已安装的 Rakudo 发行版为可执行文件分配的任何目录中;通常,这将是 /path/to/installation/share/perl6/site/bin/,这是一个应该在 $PATH 中可用的文件夹。注意:perl6 路径组件早于语言名称更改。

  • README.md 文件是一个 markdown 格式 的文本文件,它将在以后由 GitHub/GitLab 自动渲染为 HTML,用于保存在这些生态系统中的模块,或者由 raku.land 网站用于保存在 CPAN 上的模块。

  • 关于 LICENSE 文件,如果您没有其他偏好,您可能只需使用 Rakudo Raku 使用的相同许可证。只需将 其许可证 的原始形式复制/粘贴到您自己的 LICENSE 文件中。

  • META6.json 中的许可证字段应为此处列出的标准化名称之一:https://spdx.org/licenses/。对于 Artistic 2.0 许可证(这是我们生态系统中许多模块使用的许可证),其标识符为 Artistic-2.0。拥有标准化的标识符使人和计算机都能够轻松地通过查看元数据来了解实际使用了哪个许可证!

  • 如果您在 spdx.org 上找不到您的许可证,或者您使用自己的许可证,您应该将许可证的名称放在许可证字段中。有关更多详细信息,请参见 https://design.raku.org/S22.html#license

  • 如果您还没有任何测试,您可以暂时省略 t 目录和 basic.rakutest 文件。有关如何编写测试的更多信息(暂时),您可以查看其他模块如何使用 Test

  • 要记录您的模块,请在其中使用 Raku Pod 标记。模块文档最受欢迎,并且一旦 Raku 模块目录(或其他一些站点)开始将 Pod 文档渲染为 HTML 以便于浏览,它将变得尤为重要。如果您有额外的文档(除了模块中的 Pod 文档之外),请为它们创建一个 doc 目录。按照与 lib 目录相同的文件夹结构,如下所示

    doc
    └── Vortex
        └── TotalPerspective.rakudoc
    
    

    [5]

    如果您的模块在安装过程中需要额外的处理才能完全集成并使用非 Raku 操作系统资源,您可能需要在顶级目录中添加一个 Build.rakumod 文件(“构建钩子”)。它将被 zef 安装程序用作安装过程的第一步。有关简要示例,请参见 zef 的自述文件。还可以查看现有生态系统模块(例如 zef 本身)中的各种使用场景。

  • 使您的 META6.json 文件看起来像这样

    {
        "raku" : "6.c",
        "name" : "Vortex::TotalPerspective",
        "api"  : "1",
        "auth" : "github:SomeAuthor",
        "version" : "0.0.1",
        "description" : "Wonderful simulation to get some perspective.",
        "authors" : [ "ÿÿ<var>ÿÿYour Nameÿÿ</var>ÿÿ" ],
        "license" : "Artistic-2.0",
        "provides" : {
            "Vortex::TotalPerspective" : "lib/Vortex/TotalPerspective.rakumod"
        },
        "depends" : [ ],
        "build-depends" : [ ],
        "test-depends" : [ ],
        "resources" : [ ],
        "tags": [
          "Vortex", "Total", "Perspective"
        ],
        "source-url" : "git://github.com/ÿÿ<var>ÿÿyouÿÿ</var>ÿÿ/Vortex-TotalPerspective.git"
    }
    
    

    此文件中的属性由 META6 类分析。它们分为可选、强制和惯例。强制性的是您需要插入到文件中的内容,惯例性的是当前 Raku 生态系统使用的内容,如果发布,可能会在模块页面上显示,但您没有义务使用它。

    对于选择版本编号方案,请尝试使用“major.minor.patch”(有关更多详细信息,请参见 版本控制规范)。这将进入 META6.jsonversion 键中。此字段是可选的,但安装程序会使用它来匹配已安装的版本(如果存在)。description 字段也是强制性的,它包含模块的简短描述。

    name 键是强制性的,如果您不包含它,zef 将失败。即使您创建了一个 META6.json 文件只是为了表达一系列脚本的依赖关系,也必须包含此部分。

    可选地,您可以设置一个api字段。递增此字段表示您的模块提供的接口与先前版本不向后兼容。如果您想遵循语义版本控制,可以使用它。最佳实践是将api字段的值保持与您的主版本号相同。然后,依赖项可以通过包含:api部分来依赖您的模块,这将确保不会引入不向后兼容的版本。

    auth部分标识 GitHub 或其他代码库托管网站(如 Bitbucket 或 GitLab)中的作者。此字段是惯例,因为它用于在生态系统中识别作者,并为具有相同名称和不同作者的模块提供可能性。

    authors部分包含所有模块作者的列表。如果只有一个作者,则必须提供一个单元素列表。此字段是可选的。

    provides部分中,包含您的发行版提供的所有命名空间,并且您希望安装这些命名空间;只有在此处明确包含的模块文件才会被安装,并且可以在其他程序中使用userequire使用。此字段是必需的。

    raku版本设置为您的模块可使用的最低 Raku 版本。此字段是必需的。如果您的模块适用于圣诞节版本及更新版本,请使用6.c;如果您的模块至少需要排灯节版本,请使用6.d

    resources部分是可选的,但如果存在,则应包含您希望安装的resources目录中的文件列表。这些文件将与您的库文件一起安装,并具有哈希名称。注意:通过%?RESOURCES访问这些文件不会获取其安装位置或IO类:您正在访问Distribution::Resource。按提供的名称索引的对象。

    tags部分也是可选的。它用于在 Raku 生态系统中描述模块。

    dependsbuild-dependstest-depends部分包含在安装的这些阶段中使用的不同模块。所有这些都是可选的,但如果存在,则必须包含这些阶段所需的模块。这些依赖项可以选择使用Version规范字符串;zef将检查这些模块的存在和版本,并在需要时安装或升级它们。

    //...
    "depends": [
           "URI",
           "File::Temp",
           "JSON::Fast",
           "Pod::To::BigPage:ver<0.5.0+>",
           "Pod::To::HTML:ver<0.6.1+>",
           "OO::Monitors",
           "File::Find",
           "Test::META"
    ],
    //...
    
    

    此外,depends可以是如上所示的数组,也可以是使用两个键的哈希,runtimebuild,其功能应该是自描述的,并且例如Inline::Python中使用

    //...
    "depends" : {
            "build": {
                "requires": [
                    "Distribution::Builder::MakeFromJSON",
                    {
                        "from" : "bin",
                        "name" : {
                            "by-distro.name" : {
                                "macosx" : "python2.7-config",
                                "debian" : "python2.7-config",
                                "" : "python2-config"
                            }
                        }
                    }
                ]
            },
            "runtime": {
                "requires": [
                    "python2.7:from<native>"
                ]
            }
    }, // ...
    
    

    通常,对于大多数情况,数组形式将绰绰有余。

    最后,source-url指示开发模块的代码库的 URL;如果您要将其发布到模块生态系统中,这是惯例模块之一。当前的模块生态系统将从项目描述中链接此 URL。 [6]

    Test::META 模块可以帮助您检查 META6.json 文件的正确性;此模块将检查所有必需字段以及用于所有字段的类型是否正确。

    META 设计文档中描述了更多字段,但并非所有这些字段都由现有的包管理器实现。因此,您应该坚持使用上述示例块中描述的字段,以确保与现有包管理器(如zef)的兼容性。您还可以查看Moritz Lenz 的所有模块存储库以获取示例,但请记住,其中一些模块可能使用当前忽略的字段(如上面的source-type)。

  • 如果您想测试您的模块,可以使用以下命令直接从您刚刚创建的模块文件夹中安装模块。

    zef install ./your-module-folder
    
    

    请注意,这样做会预编译并安装您的模块。如果您对源代码进行了更改,则需要重新安装模块。(请参阅use lib pragma-I 命令行开关或 RAKULIB 环境变量,以在开发模块时包含模块源代码的路径,这样您就不必安装它)。

将您的模块上传到 CPAN§

使用 CPAN 的前提是拥有 PAUSE 用户帐户。如果您还没有帐户,可以 在这里 创建一个。这个过程大约需要 5 分钟,并需要一些电子邮件往返。

  • 创建您的模块的包

    cd your-module-folder
    tar czf Vortex-TotalPerspective-0.0.1.tar.gz \
      --transform s/^\./Vortex-TotalPerspective-0.0.1/ --exclude-vcs\
      --exclude=.[^/]*
    
    

    如果您使用 git,您也可以使用以下命令直接为给定提交创建包。

    git archive --prefix=Vortex-TotalPerspective-0.0.1/ \
      -o ../Vortex-TotalPerspective-0.0.1.tar.gz HEAD
    
    
  • 转到 PAUSE,登录并导航到“上传文件到 CPAN”。

  • 确保您选择“Perl6”作为“目标目录”。对于您的第一次上传,您必须在文本字段中输入字符串“Perl6”。在后续上传中,您可以从输入字段下方选择框中选择“Perl6”目录。

  • 选择您的文件并点击“上传”!如果您的 dist 没有任何问题,您应该在您的“Perl6”目录中看到您的模块 tar 文件,以及一个名为模块文件名的 METAREADME 文件。

    确保您的 dist 中有一个 META6.json 文件,并且您要上传的 dist 版本高于当前上传的版本。这些是最常见的上传错误。

将您的模块上传到 zef 生态系统§

如果您想使用 zef 生态系统,则需要使用 fez 注册您的用户名。

  • 如果您还没有安装 fez,请安装它

zef install fez

  • 使用 zef 的生态系统注册您的用户

fez register
>>= Email: [email protected]
>>= Username: username
>>= Password:
>>= registration successful, requesting auth key
>>= login successful, you can now upload dists

  • 现在您可以上传您的模块了!

在执行以下操作之前,请确保您的 META6.json 文件的 authzef:<username> 相匹配,然后

fez upload
>>= Vortex::TotalPerspective:ver<0.1.2>:auth<zef:username> looks OK
>>= Hey! You did it! Your dist will be indexed shortly

版本控制和 fez§

  • 为了防止对版本化的发行版进行更改(请记住,一个发行版可能包含多个模块),每个版本只允许上传一个发行版。如果开发人员对存储库中的代码进行更改,这些更改将不会反映在 fez 系统中。因此,对代码的更改必须伴随着版本号的提升,并将新版本上传到 fez

将您的模块上传到 p6c§

Raku 社区正在迁移到 fez 生态系统(见上文)。对于在 p6c 生态系统中拥有模块并将其迁移到 fez 的开发人员,请将其从 p6c 生态系统中删除。如果您没有提交权限,请打开一个拉取请求或在 IRC 上 ping 一些人(libera.chat 上的 #raku)以获得帮助。

如果您想使用 p6c 生态系统,您需要使用 git 来进行模块的版本控制。本文档中的说明假设您拥有 GitHub 帐户,以便可以从其 GitHub 存储库共享您的模块,但是其他提供商(如 GitLab)应该也可以工作,只要它们的工作方式类似。

  • 如果您还没有将项目置于 git 版本控制之下,请这样做。

  • 当您对项目感到满意后,在 GitHub 上为它创建一个存储库。如有必要,请参阅 GitHub 的帮助文档。您的 GitHub 存储库应该与您的项目目录同名。在创建 GitHub 存储库后,GitHub 会立即向您展示如何配置本地存储库以了解您的 GitHub 存储库。

  • 将您的项目推送到 GitHub。

  • 考虑设置自动测试。一个选项是使用 github 工作流功能,Mi6-Helper App 中有一个示例。

  • ecosystem 上创建一个 PR(拉取请求),将您的模块添加到 META.list,或者在 IRC 上 ping 一些人(libera.chat 上的 #raku)以获得帮助将其添加。

  • 拉取请求被接受后,请等待一个小时。如果您的模块没有出现在 https://raku.land/ 上,请查看日志文件,看看它是否识别出您的模块或 meta 文件的错误。

就是这样!感谢您为 Raku 社区做出贡献!

如果您想尝试安装您的模块,请使用 zef 模块安装工具,它包含在 Rakudo Star Raku 中

zef install Vortex::TotalPerspective

这将把您的模块下载到它自己的工作目录(~/.zef),在那里构建它,并将模块安装到您的本地 Raku 安装目录中。

要在您的脚本中使用 Vortex::TotalPerspective,只需编写 use Vortex::TotalPerspective,您的 Raku 实现就会知道在哪里查找模块文件。

与模块编写相关的模块和工具§

您可以在 Modules Extra 中找到旨在改善编写/测试模块体验的模块和工具列表。

联系方式§

要讨论一般的模块开发,或者您的模块是否可以满足生态系统中的需求、命名等,您可以使用 #raku on irc.libera.chat IRC 频道。

一个用于讨论工具问题 的仓库位于 https://github.com/Raku/toolchain-bikeshed

1 [↑] 从技术上讲,模块是一组compunit,它们通常是文件,但只要存在可以提供它们的compunit 仓库,它们就可以来自任何地方。参见 S11
2 [↑] 如果已安装,它将仅在模块的版本比已安装的版本更新时重新安装。
3 [↑] 正如 概要 S11 所说:令人困惑?是的,确实如此。
4 [↑] 此更改是在 2016 年后期引入的。如果您使用的是比这更早的版本,行为将有所不同。
5 [↑] 请注意,上面描述的是一个最小的项目目录。如果您的项目包含您希望与模块一起分发的脚本,请将它们放在 bin 目录中。如果您希望在模块目录中,模块旁边出现一个图形徽标,请创建一个 logotype 目录,并将 logo_32x32.png 文件放入其中。在某些时候,您可能还需要考虑添加 CONTRIBUTORSNEWSTODO 或其他文件。
6 [↑] 一些旧模块还提供 source-type 字段,该字段用于指示源代码控制系统的类型,通常是 git,它可以用于下载模块。但是,此字段现在被 zef 和其他工具忽略。