http://qs1969.pair.com?node_id=944649

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

Good morning Monks,

I am working on a script that will output datasets which users at astronomical observatories can then pick up and plot, say with OpenOffice (or Excel, or what have you).

It has to be pretty generic, that is,

1) the fields in the data have to be pretty dependably common to any of the environments where this script might be used;

2) the default output is just tab-delimited text files.

So far, so easy; however, what is giving me pause is

3) no non-core modules should be required. We can't predict what users will have nor whether they will be allowed to install anything ...

... what I'd like to do is code up routines that will actually go ahead and do graphical plotting if the user has the GD::Graph module installed, but still output the plain text files if s/he doesn't.

So I am thinking I should be able to have the script test whether GD::Graph is installed and, if it isn't, to print a useful message (a suggestion that the user install the module, say) ... but then to continue and generate the plain text files.

I have tried using eval, as follows:

eval { use Fictitious::Module }; print "run anyway\n" if $@;

... but this doesn't work: I don't seem to be able to get around the complaint that Fictitious::Module is not found.

Is there a clean way to do this?

Replies are listed 'Best First'.
Re: Base script behavior on presence (or non-) of a module
by Eliya (Vicar) on Dec 21, 2011 at 15:50 UTC
    eval { use Fictitious::Module };

    Use require, not useuse is executed at compile time of that code fragment, before the eval executes.

    See also BEGIN — because use behaves like* a require with BEGIN wrapped around it.

    ___

    *  roughly like, that is.  If you want to import stuff, you have to call import explicitly when you use require.  See the mentioned docs for details.

Re: Base script behavior on presence (or non-) of a module
by flexvault (Monsignor) on Dec 21, 2011 at 15:56 UTC

    I just did this, and the "how to" was in the "Perl Cookbook" on pages 451-452. I recommend you take a look at the description, but the explanation is to but the test in a BEGIN block:

    BEGIN { unless ( eval "use Fictitious::Module;, 1" ) { warn "couldn't use Fictitious::Module: $@"; } }
    I hope I typed it correctly, as the book explains why it has to be exactly this way.

    Good Luck

    "Well done is better than well said." - Benjamin Franklin

Re: Base script behavior on presence (or non-) of a module
by jhourcle (Prior) on Dec 21, 2011 at 21:31 UTC

    Others have already answered the actual question ...

    But in part, because I'm a bit of a file-formats snob, and in part because I've been dealing with an e-mail discussion for the last month or so on trying to come up with standards on describing these sort of text files as used in space physics for time tagged data for event lists (vs. time series at a fixed cadence) ... as you mention you're in the astronomy field, have you considered spitting out VOTable? None of the perl libraries comply with the 1.2 spec unfortunately (and only the version in Astro::Catalog is actually in CPAN), but you should then be able to hand off the plotting work to dedicated tools like VOPlot (docs/examples).

    (and I say this *only* because you're in astronomy; if you were in earth sciences, I'd recommend HDF; CDF for space physics; NetCDF for storage for atmospheric sciences (with an OPeNDAP service for distribution). There's also Google's Dataset Publishing Language to document CSV files so that some of Google's tools can be used to visualize them, and there's lots of other standards out there for all of the various javascript visualization tools.

      +1 for that link to Google's Dataset Publishing Language... didn't know that existed, and it looks *very* cool!
      Thanks!
      -bib
Re: Base script behavior on presence (or non-) of a module
by tobyink (Canon) on Dec 21, 2011 at 16:29 UTC

    I strongly suggest using Class::Load to handle this, but if you'd rather homebrew a solution...

    if (eval { require Fictitious::Module; 1 }) { # code that uses Fictitious::Module } else { warn "Can't find Fictitious::Module. Soldiering on regardless..."; }

        It's perhaps worth pointing out that those weren't core modules in 5.8.x.

        To me, it sounds like the OP is targetting a wide range of installations, trying to settle on the "lowest common denominator". So there might be 5.8 installations among them.

        In other words, in order to not have the program die with "Can't locate Module/Load.pm in @INC ..." on 5.8 installations, the OP would have to first check the classic "built-in" way, i.e. BEGIN { eval {require Module::Load; 1} or ...}, if the module itself is available — and depending on the outcome, possibly do the loading of the other non-core modules the same classic way...

        Just a thought.

        Module::Pluggable does an entirely different job to Class::Load - it searches for (and optionally requires and instantiates) modules with names matching a particular pattern.

        Module::Load does something similar to Class::Load but has less functionality. Class::Load not only provides load_class (equivalent to Module::Load's load function), but also try_load_class and is_class_loaded, load_first_existing_class and load_optional_class which can be pretty handy at times.

        (The difference between try_load_class and load_optional_class is subtle. Both will check whether the class exists, attempt to load it, and return a boolean indicating successfulness. The difference is in how they handle the situation where the module exists, but cannot be loaded - e.g. due to syntax errors - the former just returns false; the latter croaks.)

Re: Base script behavior on presence (or non-) of a module
by Anonymous Monk on Dec 21, 2011 at 16:26 UTC