package Module::Prefer; use strict; use warnings; use base 'Exporter'; our @EXPORT_OK = qw'prefer'; sub prefer { # prefer ([ module_name => [ $use_defaults, @param ], ... ], @default_args); my $modspec = shift; my $caller = caller; my $module; my @err; if (@$modspec % 2 != 0) { die "need a hash-like ARRAYref in first argument" } for (my $i = 0; $i <= @$modspec/2; $i+=2) { eval 'require '.$modspec->[$i].';'; if ($@) { push @err, [ $modspec->[$i], $@ ]; next; } $module = $i; last; } if (defined $module && $$modspec[$module]->can('import')) { # Required one of the modules! my $call = "package $caller; ".$$modspec[$module]."->import"; my $param = $modspec->[$module+1]; # mod_name => undef same as mod_name => [0] unless (defined $param && @$param) { $param = [0] } my $use_defaults = shift @$param; if ($use_defaults) { # we should use defaults, if there are any if (@_ || @$param) { # only add things if we either have some defaults, or some specials $call .= '('.(@_ ? "\@_," : '').(@$param ? "\@\$param" : ''); $call =~ s/,$//; $call .= ')'; } } elsif (@$param) { # only use the specials, if any exist $call .= "(\@\$param)"; } $call .= ';'; eval "$call"; die ($@) if $@; } else { # couldn't require any module; die "Failed to require any of the modules:\n\t" .join("\n\t",map { sprintf('%20s => %s', @$_) } @err) ."\n"; } return $modspec->[$module]; #return name of module used } 1; __END__ =head1 NAME This document describes Module::Prefer =head1 SYNOPSIS Allows a package to effectively C preferred modules, while falling back on less-preferred modules when needed. use Module::Prefer 'prefer'; # use CGI::Simple if possible, CGI otherwise my $CGI = prefer( [ 'CGI::Simple' => [1], 'CGI' => [1], ], ); See the section on C for the full explanation of the parameter spec. Note that nothing is exported by default, so one must explicitly ask for C to be exported if this is desired. =head1 SUBROUTINES =over 8 =item C $chosen = prefer( \@modspec, @default_args ); The C subroutine accepts a reference to a L array, and a list of default arguments to pass to the imported module's C method (if any). That is, the same list that would be passed after the C statement. If none of the modules can be Cd, C will die, dumping the errors given by each module, in order. Once a module is sucessfully required, it will be Ced, if possible, and the name of the chosen module will be returned. =back =head1 DETAILS =head2 modspec The modspec is a reference to a "hash-like" array, in the following form: [ 'Module::Name' => [ $use_default_args, @special_args ], ... ] Modules should occur in order of preference, from most to least preferred. The element immediately following the module name may either be C or an ARRAYref containing at least one value. If it is C, this modules C function will be called without any parameters (if this module is selected for import). The first element in that ARRAYref will be a true/false value that determines whether or not the default args list will be applied if this module is selected for C. The remaining arguments, if any, will always be passed to this modules C (if this module is chosen). For example, assume B must manually import C, but B imports this automatically; also, both modules need to be told to import C. Two approaches are possible: # 'bar' in default, special args for Module::A $chosen = prefer ( [ 'Module::A' => [1,'foo'], # use Module::A ('bar','foo'); 'Module::B' => [1], # use Module::B ('bar'); ], 'bar', ); # 'foo' and 'bar' in default, Module::B does it's own thing $chosen = prefer ( [ 'Module::A' => [1], # use Module::A ('bar','foo'); 'Module::B' => [0,'bar'], # use Module::B ('bar') - ignore defaults! ], 'foo', 'bar', ); =cut