How should I bundle a library of text files with my module?

Viewed 93

I have the following structure in the resources directory in a module I'm building:

resources
|-- examples
    |-- Arrays
    |   |-- file  
    |-- Lists
        |-- file1
        |-- file2

I have the following code to collect and process these files:

use v6.d;
unit module Doc::Examples::Resources;

class Resource {
    has Str $.name;
    has Resource @.resources;
    has Resource %.resource-index;

    method resource-names() {
        @.resources>>.name.sort
    }

    method list-resources() {
        self.resource-names>>.say;
    }

    method is-resource(Str:D $lesson) {
        $lesson ~~ any self.resource-names;
    }

    method get-resource(Str:D $lesson) {
        if !self.is-resource($lesson) {
            say "Sorry, that lesson does not exist.";
            return;
        }
        return %.resource-index{$lesson};
    }

}

class Lesson is Resource {
    use Doc::Parser;
    use Doc::Subroutines;
    has IO $.file;

    method new(IO:D :$file) {
        my $name = $file.basename;
        self.bless(:$name, :$file)
    }

    method parse() {
        my @parsed = parse-file $.file.path;
        die "Failed parse examples from $.file" if @parsed.^name eq 'Any';
        for @parsed -> $section {
            my $heading = $section<meta>[0] || '';
            my $intro = $section<meta>[1] || '';
            say $heading.uc ~ "\n" if $heading && !$intro;
            say $heading.uc if $heading && $intro;
            say $intro ~ "\n" if $intro;
            for $section<code>.Array {
                die "Failed parse examples from $.file, check it's syntax." if $_.^name eq 'Any';
                das |$_>>.trim;
            }
        }
    }
}

class Topic is Resource {
    method new(IO:D :$dir) {
        my $files = dir $?DISTRIBUTION.content("$dir");
        my @lessons;
        my $name = $dir.basename;
        my %lesson-index;
        for $files.Array -> $file {
            my $lesson = Lesson.new(:$file);
            push @lessons, $lesson;
            %lesson-index{$lesson.name} = $lesson;
        }
        self.bless(:$name, resources => @lessons, resource-index => %lesson-index);
    }

}

class LocalResources is Resource is export {
    method new() {
        my $dirs = dir $?DISTRIBUTION.content('resources/examples');
        my @resources;
        my %resource-index;
        for $dirs.Array -> $dir {
            my $t = Topic.new(:$dir);
            push @resources, $t;
            %resource-index{$t.name} = $t;
        }
        self.bless(:@resources, :%resource-index)
    }

    method list-lessons(Str:D $topic) {
        self.get-resource($topic).list-lessons;
    }

    method parse-lesson(Str:D $topic, Str:D $lesson) {
        self.get-resource($topic).get-resource($lesson).parse;
    }
}

It works. However, I'm told that this is not reliable and there there is no guarantee that lines like my $files = dir $?DISTRIBUTION.content("$dir"); will work after the module is installed or will continue to work into the future.

So what are better options for bundling a library of text files with my module that can be accessed and found by the module?

2 Answers

Files under the resources directory will always be available as keys to the %?RESOURCES compile-time variable if you declare them in the META6.json file this way:

"resources": [
    "examples/Array/file",
]

and so on.

I've settled on a solution. As pointed out by jjmerelo, the META6.json file contains a list of resources and, if you use the comma IDE, the list of resources is automatically generated for you.

From within the module's code, the list of resources can be accessed via the $?DISTRIBUTION variable like so:

my @resources = $?DISTRIBUTION.meta<resources>

From here, I can build up my list of resources.

One note on something I discovered: the $?DISTRIBUTION variable is not accessible from a test script. It has to be placed inside a module in the lib directory of the distribution and exported.

Related