Evented-API-Engine

 view release on metacpan or  search on metacpan

lib/Evented/API/Module.pm  view on Meta::CPAN

# Copyright (c) 2017, Mitchell Cooper
# Module represents an API module and provides an interface for managing one.
package Evented::API::Module;

use warnings;
use strict;
use 5.010;

use Evented::Object;
use parent 'Evented::Object';

use Scalar::Util qw(blessed weaken);
use List::Util qw(first);

our $VERSION = '4.13';

=head1 NAME

B<Evented::API::Module> - represents a module for use with
L<Evented::API::Engine>.

=head1 SYNOPSIS

    # Module metadata
    #
    # @name:        'My::Module'
    # @package:     'M::My::Module'
    # @description:
    #
    # @depends.modules+ 'Some::Other'
    # @depends.modules+ 'Another::Yet'
    #
    # @author.name:     'Mitchell Cooper'
    # @author.website:  'https://github.com/cooper'
    #
    package M::My::Module;
    
    use warnings;
    use strict;
    use 5.010;
    
    # Auto-exported variables
    our ($api, $mod);
    
    # Default initializer
    sub init {
        say 'Loading ', $mod->name;
        
        # indicates load success
        return 1;
    }
    
    # Default deinitializer
    sub void {
        say 'Bye!';
        
        # indicates unload success
        return 1;
    }
    
    # Package must return module object
    $mod;

=head1 DESCRIPTION

=head2 Module directory structure

Modules must be placed within one of the search directories specified in the
L<Evented:API::Engine> C<mod_inc> consturctor option or the C<dirs> option
of C<< ->load_module >> and similar Engine methods.

A module in its simplest form is a directory with the C<.module> extension

lib/Evented/API/Module.pm  view on Meta::CPAN

        $mod->Log("void stopped: ".$void_fire->stop);
        $mod->Log("Can't unload: canceled by '$stopper'");
        return;
    }

    # if this is a submodule, it isn't allowed to refuse to unload.
    elsif ($stopper) {
        $mod->Log(
            "Warning! This submodule has requested to remain ".
            'loaded, but submodules MUST be unloaded with their parent'
        );
    }

    # void was successful
    return 1;

}

##################
### SUBMODULES ###
##################

=head2 $mod->load_submodule($submod_name)

Loads a submodule.

This is generally used within the module initializer.

B<Parameters>

=over

=item *

B<$submod_name> - name of the submodule, without the parent module name prefix.
for instance if the full submodule name is C<My::Module::Sub>, this is simply
C<Sub>.

=back

B<Returns>

Submodule object on success, false otherwise.

=cut

sub load_submodule {
    my ($mod, $mod_name) = @_;
    $mod->Log("Loading submodule $mod_name");

    # call ->load_module with the search dir set to the
    # parent module's main directory.
    $mod->api->{indent}++;
        my $ret = $mod->api->load_module($mod_name, [ $mod->{dir} ], 1);
    $mod->api->{indent}--;

    # add weakly to submodules list. hold weak reference to parent module.
    if ($ret) {
        my $a = $mod->{submodules} ||= [];
        push @$a, $ret;
        weaken($a->[$#$a]);
        weaken($ret->{parent} = $mod);
    }

    return $ret;
}

=head2 $mod->unload_submodule($submod)

Unloads a submodule.

You do not have to call this in the parent module deinitializer. Only use this
method if you want to dynamically unload a submodule for some reason without
unloading the parent module.

B<Parameters>

=over

=item *

B<$submod> - submodule object or name, without the parent module name prefix.
for instance if the full submodule name is C<My::Module::Sub>, this is simply
C<Sub>.

=back

=cut

sub unload_submodule {
    my ($mod, $submod, $reloading) = @_;
    my $submod_name = $submod->name;
    $mod->Log("Unloading submodule $submod_name");

    # unload
    $mod->api->{indent}++;

        # ($mod, $unload_dependents, $force, $unloading_submodule, $reloading)
        #
        # do not force, as that might unload the parent
        # but do say we are unloading a submodule so it can be unloaded
        # independently (which usually wouldn't be allowed)
        #
        $mod->api->unload_module($submod, undef, undef, 1, $reloading);

    $mod->api->{indent}--;

    # remove from submodules
    if (my $submods = $mod->{submodules}) {
        @$submods = grep { $_ != $submod } @$submods;
    }
    delete $submod->{parent};

    return 1;
}

=head2 $mod->add_companion_submodule($mod_name, $submod_name)

Registers a submodule (provided by this module) as a companion of another
top-level module.

Companion submodules are submodules which are automatically loaded and unloaded



( run in 1.704 second using v1.01-cache-2.11-cpan-39bf76dae61 )