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


in reply to What's the best way to use Any YAML (not YAML::Any)

G'day djerius,

One of the main reasons for avoiding the use of 'string eval' is that it doesn't provide compile time warnings. If you use a BEGIN block, you can get around this.

BEGIN { my @yaml_classes = qw{YAML::XS YAML::Syck YAML::Tiny YAML}; first { eval "use $_; 1" } @yaml_classes or die "Unable to load any of these YAML classes: @yaml_classe +s"; }

That will give a compile time warning which, is not only very obvious with respect to the problem, but also will obviate the need for any specific testing to check if a YAML class was found and loaded, i.e. Test::More's use_ok('Your::Module::Name'), which I'd hope you already have, would suffice.

Unable to load any of these YAML classes: YAML::XS YAML::Syck YAML::Ti +ny YAML at ... BEGIN failed--compilation aborted at ...

If you need to know which YAML class was loaded and, as your code suggests, this is stored in a variable with package scope, I'd recommend something other than the all-too-common $class. You'd need to declare that variable outside the BEGIN block.

my $yaml_class; BEGIN { my @yaml_classes = qw{YAML::XS YAML::Syck YAML::Tiny YAML}; $yaml_class = first { eval "use $_; 1" } @yaml_classes or die "Unable to load any of these YAML classes: @yaml_classe +s"; } print $yaml_class;
"My module doesn't care which YAML is around, just that it can Dump and Load."

I'd probably consider testing for that in the BEGIN block also. The following example clearly documents the intent and would only need a change, in a single and obvious place (i.e. near the top of the module code), if that requirement ever changed.

BEGIN { # YAML classes to attempt to load (in order of preference) my @yaml_classes = qw{YAML::XS YAML::Syck YAML::Tiny YAML}; # Expecting to import these functons from whatever YAML class is l +oaded my @yaml_imports = qw{Dump Load}; my $yaml_class = first { eval "use $_ (\@yaml_imports); 1" } @yaml +_classes or die "Unable to load any of these YAML classes: @yaml_classe +s", " (using import list: @yaml_imports)"; }

Update (improved solution): I've changed that final BEGIN block (i.e. the changes are in the block immediately above here).

After posting, I realised that the original solution (see spoiler below) had some drawbacks: it loaded the YAML class before checking @EXPORT; it didn't allow imports from @EXPORT_OK; and, it would fail for all @yaml_classes if any particular class successfully loaded but then failed the @EXPORT test. The "improved solution" has none of these problems.

As already stated (i.e. "original solution (see spoiler below)"), but apparently not clear to some, I repeat, the original solution is hidden in the spoiler below.

[Minor Update: I've added some additional text, to the "Update (improved solution)" text, to make it clear that the original code is hidden in the spoiler.]

-- Ken