Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass parameters to Perl subroutines defined using eval?

I'm using a config file (in YAML) to define types that are used later on to validate other config values required for my app:

---
action: >
        use List::MoreUtils;
        my $value = $_;
        any { $value eq $_ } qw(fatal keep merge non-fatal replace);
dir   : return defined $_ ? -d $_ : -1;
file  : return defined $_ ? -f $_ : -1;
string: 1;


---
config-element:
    value: foo
    type : file
etc ...

The idea is to eval each type definition, throw them into a hash and then call to validate configuration data (the following is schematic for easy comprehensibility):

#throw sub refs into hash
my %type_sub;
foreach my $key (keys %$type_def_ref) {
    my $sub_str = "sub {$type_def_ref->{$key}}";
    $type_sub{$key} = eval $sub_str;

}

#validate (myfile is a real file in the cwd)
print $type_sub{file}->('myfile'),"\n";
print $type_sub{action}->('fatal'), "\n";

The problem is that the subroutines in %type_sub don't seem to accept parameters. In the above case, the first print statement outputs -1 while the second outputs:

Use of uninitialized value $value in string eq at (eval 15) line 1.
Use of uninitialized value $_ in string eq at (eval 15) line 1.
Can't call method "any" without a package or object reference at 
(eval 15) line 1.

which is not at all what I expect, yet the subroutines are being called.

What am I doing wrong?

EDIT: I was being sloppy and everything works fine now. Thanks to Friedo.

like image 718
gvkv Avatar asked Feb 27 '23 18:02

gvkv


2 Answers

Don't write code in configuration. Create a library with the code and simply configure which subroutine name you want to use. That should save you an huge amount of work translating strings to code and managing the process. It also saves you a ton of time tracking down problems when someone adjusts the configuration and introduces a syntax error.

I talk about this extensively in the "Configuration" chapter in Mastering Perl, as well as the chapters on dynamic subroutines.

Code doesn't belong in configuration. Say that until you believe it.

like image 77
brian d foy Avatar answered Mar 02 '23 06:03

brian d foy


Your subroutine parameters will be in the @_ array, not $_. To get the first parameter, look in $_[0] or do my $foo = shift;. (shift operates on @_ by default.)

As for any, I believe the problem is due to any not being able to load its prototype at runtime (subroutine prototypes can only be called at compile-time.) You may need to use explicit parens and an explicit subroutine reference:

any( sub { $value eq $_ }, qw(fatal keep merge non-fatal replace) );
like image 38
friedo Avatar answered Mar 02 '23 08:03

friedo