How can find modules that have been installed locally, and which I can use
in a Raku program?
Assume that we have three distributions: Parent, Brother, Sister. Parent 'provides' Top.rakumod
, while Brother and Sister provide 'Top::Son.rakumod' and 'Top::Daughter.rakumod', respectively. Brother and Sister have a 'depends': 'Top' in their META6.json.
Each distribution is in its own git repo. And each are installed by zef.
Suppose Top is set up as a class with an interface method, perhaps something like: multi method on-starting { ... }
, which each sub-class has to implement, and which when run, provides the caller with information about the sub-class. So both Top::Brother
and Top::Daughter
implement on-starting
. There could also be distributions Top::Aunt
and so on, which are not locally installed. We need to find which are installed.
So, now we run an instance of Top (as defined in Parent). It needs to look for installed modules that match Top::*
. The place to start (I think) is $*REPO
, which is a linked list of repositories containing the modules that are installed. $*REPO also does the CompUnit::Repository role, which in turn has a 'need' method.
What I don't understand is how to manipulate $*REPO to get a list of all the candidate modules that match Top::*
, along the whole of the linked list.
Once I have the list of candidates, I can use ^can
to check it has a on-starting
method, and then call that method.
If this is not the way to get to a result where Top finds out about locally installed modules, I'd appreciate some alternatives to the scheme I just laid out.
CompUnit::Repository
(CUR) has a candidates
method for searching distributions, but it does not allow for searching by name prefix (since it also does fast lookups which require the full name to get its sha1 directory/lookup). For a CompUnit::Repository::FileSystem
(CURFS) you can call .distribution
to get the distribution is provides, and for CompUnit::Repository::Installation
(CURI) you can call .installed
to get all the distributions it provides:
raku -e ' \
say $*REPO.repo-chain \
.grep(CompUnit::Repository::FileSystem | CompUnit::Repository::Installation) \
.map({ $_ ~~ CompUnit::Repository::FileSystem ?? $_.distribution !! $_.installed.Slip }) \
.grep(*.defined) \
;'
If you want to match namespaces you would need to then grep on the distributions name or the name of its modules:
my @matches = @distributions.grep({ $_.meta<provides>.keys.first({.starts-with("Top::")}) });
This way of handling things can be seen in the Pluggable module (which I'd suggest using if you also want to load such code)
Of course you explicitly asked only for installed modules, but ignoring CURFS doesn't make any sense -- as an application developer it isn't supposed to matter where or how a module is loaded. If someone wants to use -I ./foo
instead of installing it there isn't a good reason to ignore it. If you insist on doing this anyway it should be obvious how to change the example above to accommodate.
Once I have the list of candidates, I can use ^can to check it has a on-starting method, and then call that method.
Having a list of candidates doesn't let you do anything other than inspect the meta file or slurp the source code of various files. At the very least you would load whatever module you wanted to call e.g. .^can
first, and there is going to be a few steps involved in that, which the distribution object can't be used directly to load with (you extract the full name from it and use that to load it) -- so again I'd suggest using Pluggable