Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

In the past I have sometimes written perl code that goes something like this
my $souperdouper=0; eval { require "SuperDouperPrinter" } and $superdouper=1; if ($souperdouper) { SouperDouperPrinter::Print($something); } else { print $something; }
Its a case of if certain facilities are available, then I would like to use them and if not then I will get by without. Sometimes there is more than one module available to do something and I want my code to work with either and not fall over if my particular favourite is missing. The above works fine if I can use require, but not all modules will work with a simple require. What is the recomended way to code this and still be able to utilise a "use" statement?

Replies are listed 'Best First'.
Re: How can I catch a failure of use?
by McA (Priest) on Oct 14, 2014 at 11:29 UTC

    Hi

    just recently there was a blog entry of NEILB who published his new module Module::Loader. In this blog he linked to a comparison he did before comparing several CPAN modules helping to load modules dynamically.

    This comparison http://neilb.org/reviews/module-loading.html is probably worth a look for finding the best solution for your plugin/fallback kind use case.

    Regards
    McA


      With (eg) Devel::Peek I can do:
      C:\>perl -MDevel::Peek -le "Dump 2;" SV = IV(0x1d7f9e8) at 0x1d7f9ec REFCNT = 1 FLAGS = (PADTMP,IOK,READONLY,pIOK) IV = 2
      Is there any way that Module::Load (or any other method of runtime loading) can load Devel::Peek such that "Dump 2;" (no parentheses) will work ?
      C:\>perl -le "require Devel::Peek; Devel::Peek->import('Dump'); Dump 2 +;" Number found where operator expected at -e line 1, near "Dump 2" (Do you need to predeclare Dump?) syntax error at -e line 1, near "Dump 2" Execution of -e aborted due to compilation errors. C:\>perl -MModule::Load -le "load 'Devel::Peek'; Devel::Peek->import(' +Dump'); Dump 2;" Number found where operator expected at -e line 1, near "Dump 2" (Do you need to predeclare Dump?) syntax error at -e line 1, near "Dump 2" Execution of -e aborted due to compilation errors.
      (I'm not the OP, btw - just wondering ...)

      Cheers,
      Rob

        Hi,

        you just have to mimic what Perl does itself:

        perl -le "BEGIN { require Devel::Peek; Devel::Peek->import('Dump') } D +ump 2"

        Regards
        McA

Re: How can I catch a failure of use?
by Discipulus (Canon) on Oct 14, 2014 at 11:09 UTC
    as far as i know this is the right idiom
    eval {require 'UFOModule'}; if ($@) {say "UFOModule not loaded"} else{say "UFOModule correctly loaded.. if you need inspect your \%INC" +}

    HtH
    L*
    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: How can I catch a failure of use?
by Athanasius (Archbishop) on Oct 14, 2014 at 12:10 UTC

    You can use the core module Module::Load::Conditional in conjunction with the if pragma:

    #! perl use strict; use warnings; use Module::Load::Conditional qw( check_install ); my $something = "This is really something!\n"; my $superduper; BEGIN { $superduper = check_install(module => 'SuperDuperPrinter'); } if ($superduper) { use if $superduper, SuperDuperPrinter => qw( Print ); SuperDuperPrinter::Print($something); } else { print $something; }

    Update: The documentation is unclear (to me, anyway), and I didn’t test properly. On further investigation, it appears that the if pragma’s condition is evaluated (silently!) at compile time, not at run time as I assumed. So I have changed:

    my $superduper = check_install(module => 'SuperDuperPrinter');

    to:

    my $superduper; BEGIN { $superduper = check_install(module => 'SuperDuperPrinter'); }

    which allows the subsequent use if construct to work correctly.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: How can I catch a failure of use? (by not catching it)
by Anonymous Monk on Oct 14, 2014 at 10:49 UTC

    How can I catch a failure of use?

    You can do that by not catching it, by simply creating another more modules, so that  use MyStuff; just works

    For example

    package MyStuff; use Module::Load qw/ load /; my @mods = qw/ MyStuff::Win32MostFeatures MyStuff::Win32LessFeatures MyStuff::LinuxOnly /; for my $mod( @mods ){ if( load $mod ){ push @ISA, $mod; last; } } scalar @ISA;

    See Module::Pluggable, Module::Load ...

Re: How can I catch a failure of use?
by Anonymous Monk on Oct 14, 2014 at 12:35 UTC

    What you're looking for may be something like

    my $module_loaded; BEGIN { eval { require Foo; Foo->import(); $module_loaded = 'Foo'; 1; } or eval { require Bar; Bar->import(); $module_loaded = 'Bar'; 1; } or do { # Whatever you do when you can not load # any of the modules you want. }; }

    The BEGIN block is run the instant its close bracket is compiled, and anything imported by the import() is available to subsequent compilaiton. Any arguments you would pass to 'use' (except version) get passed to import(). If you wish to specify a minimum version, do

    Module->VERSION( 3.14 );

    after you require() the module. If you read the docs for 'use' you will find it explained in more or less the same terms I have given here.