Badger
view release on metacpan or search on metacpan
lib/Badger/Modules.pm view on Meta::CPAN
# We have to be careful because symbols may be defined in a
# package's symbols table *before* the module is loaded (e.g. the
# $DEBUG package variable that Badger::Debug uses). So we only
# assume that the module is loaded if VERSION or BADGER_LOADED is
# defined
if ($symtab->{ VERSION } || $symtab->{ BADGER_LOADED }) {
$self->debug("found an existing VERSION/BADGER_LOADED in $module symbol table") if DEBUG;
return $self->found( $name, $module );
}
$file = $module;
$file =~ s/::/\//g; # TODO: check Perl maps this to OS
$file .= '.pm';
$self->debug("Attempting to load $module as $file") if DEBUG;
eval {
# We use eval so that we can "use" the module and force any
# import hooks to run. But it might be better to load the
# module with "require" and then manually call import()
require $file;
};
if ($@) {
$self->debug("Failed to load $module: $@") if DEBUG;
# Don't confuse "Can't locate A/Module/Used/In/Your/Module.pm"
# messages with "Can't locate Your/Module.pm". The former is
# an error that should be reported, the latter isn't. We convert the
# class name to a regex that matches any non-word directory
# separators, e.g. Your::Module => Your\W+Module
my $qmfile = quotemeta($file);
$self->failed($name, $module, $@) if $@ !~ /^Can't locate $qmfile.*? in \@INC/;
next;
}
# Some filesystems are case-insensitive (like Apple's HFS), so an
# attempt to load Badger::Example::foo may succeed, when the
# correct package name is actually Badger::Example::Foo. We
# double-check by looking to see if anything extra has been added
# to the symbol table.
$self->debug("$module symbol table keys: ", join(', ', keys %$symtab)) if DEBUG;
next unless %$symtab;
$self->debug("calling $module->import") if DEBUG;
# now call the import() method to fire any import actions
$module->import;
# add the $BADGER_LOADED package variable to indicate that the
# module has been loaded and add an entry to the internal cache
${ $module.PKG.BADGER_LOADED } ||= 1;
$loaded->{ $module } = $module;
return $self->found( $name, $module );
}
}
# add entry to indicate module not found
$loaded->{ $name } = 0;
return $self->not_found($name);
}
sub found {
# my ($self, $name, $module) = @_;
return $_[2];
}
sub not_found {
my $self = shift;
return $self->{ tolerant }
? $self->decline_msg( not_found => $self->{ item } => @_ )
: $self->error_msg( not_found => $self->{ item } => @_ );
}
sub failed {
my $self = shift;
$self->error_msg( failed => $self->{ item }, @_ );
}
1;
__END__
=head1 NAME
Badger::Modules - a module for loading modules
=head1 SYNOPSIS
=head2 Example 1 - using a Badger::Modules object
use Badger::Modules;
my $modules = Badger::Modules->new(
path => ['My::Example','Your::Example'],
);
# load either My::Example::Foo or Your::Example::Foo
my $foo_module = $modules->module('foo');
# module() returns the module (class) name that was loaded
my $foo_object = $foo_module->new;
=head2 Example 2 - creating a Badger::Modules subclass
package My::Project::Modules;
use base 'Badger::Modules';
our $PATH = 'My::Project'; # or a list reference
1;
=head2 Example 3 - using the Badger::Modules subclass
use My::Project::Modules;
# either by creating an object...
my $modules = My::Project::Modules->new;
my $module = $modules->module('foo')
|| die $modules->reason;
# ...or by calling class methods
my $module = My::Project::Modules->module('foo')
|| die $modules->reason;
=head2 Example 4 - pre-loading modules (TODO)
# This doesn't work yet
use My::Project::Module
preload => 'foo bar baz';
lib/Badger/Modules.pm view on Meta::CPAN
This work well enough for most modules, but some do not capitalise
consistently. Modules whose names contain acronyms like C<URL> are typically
prone to a dose of fail.
$module = $modules->module('url'); # looks for XXX::Url not XXX::URL
If you specify the name in the correct capitalisation then you'll have no
problem.
$module = $modules->module('URL');
If like me you prefer to use case-insensitive throughout and leave it up to
the module loader to worry about the correct capitalisation then the C<names>
option is your friend. You can use to define any number of simple aliases
for the L<module()> method to use.
$modules = Badger::Modules->new(
path => ['My::Plugin', 'Your::Plugin'],
names => {
url => 'URL',
cgi => 'CGI',
foo => 'iFoo::XS'
}
);
Note that the values specified in the C<names> hash array are partial module
names. They will still be applied to the base paths specified in the C<path>
option to generate complete candidate module paths.
=head2 tolerant
This option affects what happens when a module requested via the L<module()>
method cannot be found. In the usual case, the tolerant option is not set and
the L<module()> method will throw a "module not found: XXX" error. If the
C<tolerant> option is set then the method will instead return C<undef>
=head1 METHODS
=head2 new()
Constructor method to create a new C<Badger::Modules> object. Inherited from
the L<Badger::Base> base class.
=head2 module($name)
Method to load a module identified by C<$name>.
[ROUGH DRAFT]
* name is aliased via names lookup table
* name is expanded to various possible capitalisations
* each base namespace in path is tried...
* with each name...
* until one is located and loaded, in which case found() is called
(or failed() if an error occurred while loading the module)
* or we exhaust all possibilities, in which case not_found() is called.
=head2 modules()
This method can be used to get or set the internal mapping of names to
modules. It's not used at present... there's some more refactoring to be
done with L<Badger::Factory> to sort out how this is going to work.
=head2 path()
Method to get or set the module search path. It returns a reference to a
list of the current search path namespaces when called without any arguments.
my $path = $modules->path;
It can be called with arguments to set a new search path. One or more
modules namespaces can be specified as arguments:
$modules->path('My::App', 'Your::App');
These can also be specified as a reference to an array.
# either
$modules->path(['My::App', 'Your::App']);
# or
@namespaces = ('My::App', 'Your::App');
$modules->path(\@namespaces);
=head2 names()
=head1 INTERNAL METHODS
=head2 init_modules($config)
Internal initialisation method used to prepare newly created
C<Badger::Modules> objects. The C<init()> method is an alias to
C<init_modules()> for the default L<new()> method inherited from the
L<Badger::Base> base class to call.
If you subclass the C<Badger::Modules> module and define your own
L<init()|Badger::Base/init()> method then it should call the C<init_modules()>
to perform the base class initialisation either before, after or in between
blocks of your own code.
package Your::Modules;
use base 'Badger::Modules';
sub init {
my ($self, $config) = @_;
# your code here
$self->init_modules($config);
# more of your code here
return $self;
}
This has the same effect as calling C<$self-E<gt>SUPER::init($config)> but
with less ambiguity in the face of multiple inheritance (usually considered
a bad thing to be avoided wherever possible) or in obscure cases where you
are monkeying around with the heritage (i.e. base classes) of a module and
Perl can't reliably resolve the correct L<init()|Badger::Base/init()> method
at compile time.
=head2 module_names($name)
This method maps the name passed as an argument to the correct case (or
variations of case) for Perl modules.
TODO: More on this. Method should possibly be renamed to expand_names()?
=head2 found($name,$module)
This method is called by the L<module()> method when a requested module
is found. The implementation in the base class simply returns the module
name passed to it as the second argument. This becomes the return value
for the successful invocation of the L<module()> method.
Subclasses may redefine this method to perform some other functionality.
=head2 not_found($name)
This method is called by the L<module()> method when a requested module cannot
be found. The default behaviour for this implementation in the base class
throws an error (via the L<error|Badger::Base/error()> method inherited from
the L<Badger::Base> base class). If the L<tolerant> configuration option is
set to a true value then it instead returns C<undef> by calling the
L<decline()|Badger::Base/decline()> method, also inherited from
L<Badger::Base>.
Subclasses may redefine this method to perform some other functionality.
=head2 failed($message)
This method is used internally to report a failure to load a module.
=head1 AUTHOR
Andy Wardley L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 2006-2010 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Badger::Factory>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:
( run in 1.391 second using v1.01-cache-2.11-cpan-5a3173703d6 )