Preparing the module§
For a module to work in any of the ecosystems, it needs to follow a certain structure.
We strongly suggest that you use some of the Module builder and authoring tools that are available.
Quick Overview using fez§
To create a skeleton, run the commands:
fez init MyNew::Module # Will create the following: # MyNew--Module/ # ├── lib # │ └── MyNew # │ └── Module.rakumod # ├── META6.json # └── t # └── 00-use.rakutest
If you need to add new modules, classes, resources, build-depends, or depends you may use the following (respectively, these resources will automatically be added to 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
Full Explanation§
Even if you're intending to use fez or an equivalent (and you should), the following can be useful reading before releasing a module, so that, as you develop your module, you will be aware of the requirements and customs regarding how a module is structured.
The META6.json file specifies the various metadata of your project and their uses; this has various sections that align with the filesystem of your module, so the following sections will step through the filesystem and the META6.json file in parallel; some sections belong only to one or the other, but many belong to both. The filesystem items will be referred to as file or directory, and the META6.json sections will be referred to as a key or a section.
The module root directory and META6.json file§
The attributes in this file are analyzed by the META6 class. They are divided into optional, mandatory and customary. Mandatory are the ones you need to insert into your file, and customary are those used by the current Raku ecosystem and possibly displayed on the module page if it's published, but you have no obligation to use it.
Create a project directory named after your module. For example, if your module is Vortex::TotalPerspective, then create a project directory named Vortex-TotalPerspective.
Make your project directory look like this:
Vortex-TotalPerspective/
├── META6.json
├── LICENSE
├── README.md
├── lib
│ └── Vortex
│ └── TotalPerspective.rakumod
└── bin
│ └── vortex
└── t
└── basic.rakutest
Make your META6.json file look something like this:
{ "name" : "Vortex::TotalPerspective", "description" : "Wonderful simulation to get some perspective.", "source-url" : "git://github.com/ÿÿ<var>ÿÿyouÿÿ</var>ÿÿ/Vortex-TotalPerspective.git" "auth" : "github:SomeAuthor", "authors" : [ "ÿÿ<var>ÿÿYour Nameÿÿ</var>ÿÿ" ], "tags": [ "Vortex", "Total", "Perspective" ], "depends" : [ ], "build-depends" : [ ], "test-depends" : [ ], "license" : "Artistic-2.0", "version" : "0.0.1", "api" : "1", "raku" : "6.c", "provides" : { "Vortex::TotalPerspective" : "lib/Vortex/TotalPerspective.rakumod" }, "resources" : [ ], }
There are more fields described in the META design documents, but not all of these are implemented by existing package managers. Hence you should stick to the fields described in the above example block to ensure compatibility with existing package managers such as zef. You can also check Moritz Lenz's repository of all modules for examples, but bear in mind that some of them might use fields, such as source-type above, which are currently ignored.
The name key§
The name key is compulsory, and zef will fail if you do not include it. Even if you have created a META6.json file just to express the dependencies for a series of scripts, this section must be included.
The description key§
The description field is also mandatory, and includes a short description of the module.
The source-url key§
source-url indicates the URL of the repository where the module is developed; this is one of the customary modules if you are going to publish it in the module ecosystem. The current module ecosystem will link this URL from the project description. [1]
See also the auth section, below.
The auth and authors sections§
The authors section includes a list of all the module authors. In the case there is only one author, a single element list must be supplied. This field is optional.
The auth section identifies the author in GitHub or other repository hosting site, such as Bitbucket or GitLab. This field is customary, since it's used to identify the author in the ecosystem, and opens the possibility of having modules with the same name and different authors.
The tags section§
The tags section is also optional. It is used to describe the module in the Raku ecosystem.
The depends, build-depends, and test-depends sections§
The depends, build-depends, and test-depends sections include different modules that are used in those phases of the of installation. All are optional, but if present must contain the required modules for those phases. These dependencies might optionally use Version specification strings; zef will check for the presence and versions of these modules and install or upgrade them if needed.
//... "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" ], //...
Additionally, depends can be either an array as above or a hash that uses two keys, runtime and build, whose function should be self-descriptive, and which are used, for instance, in 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>" ] } }, // ...
In general, the array form will be more than enough for most cases.
The README.md file§
The README.md file is a markdown-formatted text file, which will later be automatically rendered as HTML by GitHub/GitLab for modules kept in those places, or by raku.land website for modules accessible with zef.
The LICENSE file and key§
Regarding the
LICENSEfile, if you have no other preference, you might just use the same one that Rakudo Raku uses. Just copy/paste the raw form of its license into your ownLICENSEfile.The license field in META6.json should be one of the standardized names listed here: https://spdx.org/licenses/. In the case of the Artistic 2.0 license, which is what many of our ecosystem modules use, its identifier is
Artistic-2.0. Having standardized identifiers make it easy for humans and computers alike to know which license was actually used by looking at the metadata!If you can't find your license on
spdx.orgor you use your own license, you should put the license's name in the license field. For more details see https://github.com/Raku/old-design-docs/blob/master/S22-package-format.pod#license.
The versioning keys§
The version key§
For choosing a version numbering scheme, try and use "major.minor.patch" (see the spec on versioning for further details). This will go into the version key of META6.json. This field is optional, but used by installation to match against installed version, if one exists.
The api key§
Optionally, you can set an api field. Incrementing this indicates that the interface provided by your module is not backwards compatible with a previous version. You can use it if you want to adhere to Semantic Versioning. A best practice is to simply keep the api field to the same value as your major version number. A dependency can then depend on your module by including an :api part, which will ensure backwards incompatible releases will not be pulled in.
The raku key§
Set raku version to the minimum Raku version your module works with. This field is mandatory. Use 6.c if your module is valid for Christmas release and newer ones, use 6.d if it requires, at least the Diwali version.
The provides section and the lib directory§
If your project contains other modules that help the main module do its job, they should go in your lib directory like so:
lib
└── Vortex
├── TotalPerspective.rakumod
└── TotalPerspective
├── FairyCake.rakumod
└── Gargravarr.rakumod
In the provides section, include all the namespaces provided by your distribution and that you wish to be installed; only module files that are explicitly included here will be installed and available with use or require in other programs. This field is mandatory.
In the provides object (object in the JSON sense), the key is the module name, the value is the path from the module root to the file in the lib/ directory.
The bin directory§
Programs and scripts that you need in the $PATH for execution need to be included in the bin directory; these will be copied to whatever directory your installed Rakudo distribution has allotted for executables; typically this will be /path/to/installation/share/perl6/site/bin/, a folder that should be available in $PATH. Note: the perl6 path component predates the language name change.
The resources directory and section§
The resources section is optional, but if present, should contain a list of the files in your resources directory that you wish to be installed. These will be installed with hashed names alongside your library files.
If you have any additional files (such as templates or a dynamic library) that you wish to have installed so you can access them at runtime, they should be placed in a resources subdirectory of your project, e.g.:
resources
└── templates
└── default-template.mustache
The file must then be referenced in META6.json (see below for more on META6.json) so that the distribution path can be provided to the program.
{ "name" : "Vortex::TotalPerspective", "provides" : { "Vortex::TotalPerspective" : "lib/Vortex/TotalPerspective.rakumod" }, "resources": [ "templates/default-template.mustache"] }
The additional file can then be accessed inside module code; see the section on the %?RESOURCES variable, below.
The $?DISTRIBUTION variable§
The L<$?DISTRIBUTION|syntax/$?DISTRIBUTION> variable is only populated within files listed in the provides section (usually those in the lib directory). If you want to use it outside that (i.e. in your tests), you'll need to provide a Routine that returns it.
The %?RESOURCES variable§
Note the example here is returning a Distribution::Resource object. It should not be considered as a path to an object in the filesystem.
my $template-text = %?RESOURCES<templates/default-template.mustache>.slurp;
Note: Accessing these files via %?RESOURCES is not getting their installed locations or an IO class: You are accessing a Distribution::Resource. object indexed on the name provided. It's important to be careful in how you use these to avoid embedding the filename at compile-time which might be different than the location the file is in at runtime.
The L<$?RESOURCES|syntax/$?RESOURCES> variable is only populated within files listed in the provides section (usually those in the lib directory). If you want to use it outside that (i.e. in your tests), you'll need to provide a Routine that returns it.
An example of how to use this might be:
sub MyModule-resources(*@resources) is export(:tests) { for @resources -> $resource-filename { my $resource = %?RESOURCES{$resource-filename}; # Just some error-checking code if $resource !~~ Distribution::Resource { note "Could not find resource '$resource-filename' in distribution"; say qx{ls -laF ; ls -laF resources ; cat META6.json}; say "Resource is: " ~ $resource.raku; say "Resource keys are: " ~ %?RESOURCES.keys.raku; } } return %?RESOURCES; }
...and then to use it in your code:
use TOP :tests :DEFAULT; # Note that the :tests here matches the "is export" above my $resource-name = 'templates/default-template.mustache'; my $resources = MyModule-resources($resource-name); my $resource = $resources{$resource-name}; my $file_handle = $resource.open();
The above will produce quite a bit of debugging output if the resource isn't found (but will continue regardless). This can be handy when running in a remote location, such as a GitHub action.
The t directory, and testing your module§
If you don't yet have any tests, you can leave out the t directory and basic.rakutest file for now. For more information on how to write tests (for now), you might have a look at how other modules use Test.
The Test::META module can help you check the correctness of the META6.json file; this module will check for all the mandatory fields and that the type used for all of them is correct.
If you want to test your module you can use the following command to install the module directly from the module folder you just created.
zef install ./your-module-folder
Note that doing so precompiles and installs your module. If you make changes to the source, you'll need to re-install the module. (See use lib pragma, -I command line switch, or RAKULIB environment variable, to include a path to your module source while developing it, so you don't have to install it at all).
See also the test-depends section, above.
Documenting your modules§
To document your modules, use Raku Pod markup inside them. Module documentation is most appreciated and will be especially important once the Raku module directory (or some other site) begins rendering Pod docs as HTML for easy browsing. If you have extra docs (in addition to the Pod docs in your module(s)), create a doc directory for them. Follow the same folder structure as the lib directory like so:
doc
└── Vortex
└── TotalPerspective.rakudoc
Build hooks§
If your module requires extra processing during installation to fully integrate with and use non-Raku operating system resources, you may need to add a Build.rakumod file (a "build hook") to the top-level directory. It will be used by the zef installer as the first step in the installation process. See the README for zef for a brief example. Also see various usage scenarios in existing ecosystem modules such as zef itself.