relative - Load modules with relative names


relative documentation Contained in the relative distribution.

Index


Code Index:

NAME

Top

relative - Load modules with relative names

VERSION

Top

Version 0.04

SYNOPSIS

Top

    package BigApp::Report;

    use relative qw(Create Publish);
    # loads BigApp::Report::Create, BigApp::Report::Publish

    use relative qw(..::Utils);
    # loads BigApp::Utils

    use relative -to => "Enterprise::Framework" => qw(Base Factory);
    # loads Enterprise::Framework::Base, Enterprise::Framework::Factory




DESCRIPTION

Top

This module allows you to load modules using only parts of their name, relatively to the current module or to a given module. Module names are by default searched below the current module, but can be searched upper in the hierarchy using the ..:: syntax.

In order to further loosen the namespace coupling, import returns the full names of the loaded modules, making object-oriented code easier to write:

    use relative;

    my ($Maker, $Publisher) = import relative qw(Create Publish);
    my $report    = $Maker->new;
    my $publisher = $Publisher->new;

    my ($Base, $Factory) = import relative -to => "Enterprise::Framework"
                                => qw(Base Factory);
    my $thing = $Factory->new;

This can also be written using aliases:

    use relative -aliased => qw(Create Publish);
    my $report    = Create->new;
    my $publisher = Publisher->new;

    use relative -to => "Enterprise::Framework", -aliased => qw(Base Factory);
    my $thing = Factory->new;




IMPORT OPTIONS

Top

Import options can be given as an hashref or an arrayref as the first argument:

    # options as a hashref
    import relative { param => value, ... },  qw(Name ...);

    # options as an arrayref
    import relative [ param => value, ... ],  qw(Name ...);

In order to simplyfing syntax, options can also be given as dash-prefixed params:

    import relative -param => value, qw(name ...);

Available options:

import will die as soon as a module can't be loaded.

import returns the full names of the loaded modules when called in list context, or the last one when called in scalar context.

AUTHOR

Top

Sébastien Aperghis-Tramoni, <sebastien at aperghis.net>

BUGS

Top

Please report any bugs or feature requests to bug-relative at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=relative. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

Top

You can find documentation for this module with the perldoc command.

    perldoc relative

You can also look for information at:

* AnnoCPAN: Annotated CPAN documentation

http://annocpan.org/dist/relative

* CPAN Ratings

http://cpanratings.perl.org/d/relative

* RT: CPAN's request tracker

http://rt.cpan.org/NoAuth/Bugs.html?Dist=relative

* Search CPAN

http://search.cpan.org/dist/relative

ACKNOWLEDGEMENTS

Top

Thanks to Aristotle Pagaltzis, Andy Armstrong, Ken Williams and Curtis Poe for their suggestions and ideas.

COPYRIGHT & LICENSE

Top


relative documentation Contained in the relative distribution.
package relative;
use strict;
use Carp;
use UNIVERSAL::require;

{
    no strict "vars";
    $VERSION = '0.04';
}

sub import {
    return if @_ <= 1;  # called with no args
    my ($package, @args) = @_;
    my ($caller) = caller();
    my @loaded = ();

    # read the optional parameters
    my %param = ();

    if (ref $args[0] eq 'HASH') {
        %param = %{shift @args}
    }
    elsif (ref $args[0] eq 'ARRAY') {
        %param = @{shift @args}
    }

    # go through the args list, looking to parameters with the dash syntax,
    # and module names and optional arguments
    my %args_for = ();  # modules arguments
    my @modules  = ();  # will be filled with only the module names
    my $prev     = "";

    for my $item (@args) {
        # if $prev is true, the previous thing (parameter or module name)
        # is expecting a value
        if ($prev) {
            # this is a parameter
            if (index($prev, "-") == 0) {
                $param{substr($prev, 1)} = $item;
                $prev = "";
            }
            # this is a module name
            else {
                push @modules, $prev;

                # this isn't a ref, so the previous module is just stored
                # and the current item becomes the new $prev
                if (not ref $item) {
                    $prev = $item;
                }
                # this is an arrayref, which will be used as the import list
                # for the module name in $prev
                elsif (ref $item eq "ARRAY") {
                    $args_for{$prev} = $item;
                    $prev = "";
                }
                else {
                    my $that = "a ".lc(ref $item)."ref";
                    croak "error: Don't know how to deal with $that (after '$prev')";
                }
            }
        }
        else {
            if ($item eq "-aliased") {
                # -aliased is a flag, so it doesn't expect a value
                $param{aliased} = 1
            }
            else {
                $prev = $item
            }
        }
    }

    push @modules, $prev if $prev;

    # determine the base name
    my $base = exists $param{to} ? $param{to} : $caller;

    # load the modules
    for my $relname (@modules) {
        # resolve the module relative name to absolute name
        my $module = "$base\::$relname";
        1 while $module =~ s/::\w+::(?:\.\.)?::/::/g;
        $module =~ s/^:://;

        # load the module, die if it failed
        $module->require or croak _clean($@);

        # import the symbols from the loaded module into the caller module
        if (exists $args_for{$relname}) {
            my $args = $args_for{$relname};

            # an arguments list has been defined, but only call import if 
            # there are some arguments
            if (@$args) {
                my $args_str = join ", ", map {"q/\Q$_\E/"} @$args;
                eval qq{ package $caller; $module->import($args_str); 1 }
                    or croak _clean($@);
            }
        }
        else {
            # use the default import method
            eval qq{ package $caller; $module->import; 1 } or croak _clean($@);
        }

        # define alias if asked to
        if ($param{aliased}) {
            my ($alias) = $module =~ /\b(\w+)$/;
            eval qq{ package $caller; sub $alias () { q/$module/ } };
        }

        # keep a list of the loaded modules
        push @loaded, $module;
    }

    return wantarray ? @loaded : $loaded[-1]
}


sub _clean {
    my ($msg) = @_;
    $msg =~ s/ at .*relative.pm line \d+\.\s*$//s;
    return $msg
}


"evitaler fo dnE" # "End of relative"