Skip to content

Instantly share code, notes, and snippets.

@neilb
Created April 4, 2021 10:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neilb/708dfe8f43363a89e85fab07515d81e5 to your computer and use it in GitHub Desktop.
Save neilb/708dfe8f43363a89e85fab07515d81e5 to your computer and use it in GitHub Desktop.
A module for optionally loading other modules and importing functions from them
package optionally;
# optionally load a module and import functions
#
# use optionally 'Some::Module', '1.23', qw/ fnA fnB /;
#
use 5.010;
use strict;
use warnings;
use Carp qw/ croak /;
use Module::Runtime qw/ require_module /;
my %have_loaded;
sub import
{
my $exporting_package = shift;
my $importing_package = caller;
croak "optionally: no package specified" unless @_ > 0;
my $optional_package = shift;
# If we can't even load the specified package, then silently do no more
return unless require_module($optional_package);
# Was a minimum version specified?
if (@_ > 0 && _looks_like_version_number($_[0])) {
my $required_version = shift;
eval "${optional_package}->VERSION($required_version)";
if ($@) {
# we've got the optional module available
# but it's an old version, which we're not interested in
# so we bail and pretend it was never loaded
delete $INC{ $optional_package };
# TODO: Possibly do more of what Class::Unload does as well?
return;
}
}
$have_loaded{ $optional_package } = 1;
return unless @_ > 0;
no strict 'refs';
foreach my $subname (@_) {
*{"${importing_package}::$subname"} = *{"${optional_package}::$subname"}{CODE}
// croak "No $subname() available for import from $optional_package";
}
}
# TODO: is there an official regexp for valid package version numbers?
sub _looks_like_version_number
{
my $string = shift;
return $string =~ m!^[0-9\.]+$!;
}
sub loaded
{
my $module_name = shift;
return $have_loaded{ $module_name };
}
1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment