Reputation: 781
I have an application where I would like to allow users to specify configuration files in Perl.
What I have in mind is similar to how PKGBUILD is used in Arch: it is a Bash script, which is simply source
d from the "makepkg" tool. It contains variable and function definitions:
pkgbase=somepkg
pkgname=('somepkg')
pkgver=0.0.1
...
prepare() {
cd "${srcdir}/${pkgbase}-${pkgver}"
...
}
I would like to do something like this in Perl. I guess I'm looking for a Perl version of source
, which can read a file, evaluate it, and import all the subroutines and variables into the namespace of the caller. With the Bash version, I could conceivably write a PKGBUILD which sources another PKGBUILD and then overrides one or two variables; I want this kind of "inheritance" to be possible in my Perl config files as well.
One problem with Perl's do
is that it seems to put the file's variables and subroutines into a separate namespace. Also, I can't figure out how to override true subroutines, only named anonymous ones.
Here's a version that may serve to illustrate what I want to do. It is not very elegant, but it demonstrates overriding both subroutines and variables, as well as calling a previously-defined subroutine:
$ cat test-override
#!/usr/bin/perl
use warnings;
use strict;
my @tables = qw(a b c);
my $print_tables = sub {
print join(", ", @tables), "\n";
};
eval(`cat "test-conf"`) or die "$@";
&$print_tables();
$ cat test-conf
@tables = qw(d e f);
my $old_print_tables = $print_tables;
$print_tables = sub {
warn "In test-conf \$print_tables\n";
&$old_print_tables();
}
$ ./test-override
In test-conf $print_tables
d, e, f
Another way to do this would be to let the configuration file return a hash, with data and subroutines as values. There is also the option of using classes and inheritance. However, I want the configuration files to be as light-weight as possible, syntactically.
The documentation for "do" mentions the possibility of using Perl in configuration files, so I know this problem has been considered before. Is there a canonical example of how to do it in a "user-friendly" manner?
Upvotes: 1
Views: 276
Reputation: 46187
Your description of what you want this "configuration file" to do (import subs and variables into the caller's namespace, override subs, classes, inheritance...) sounds suspiciously like a standard Perl module. So use a standard Perl module.
Note that there is widespread precedent for this approach: The standard cpan
command-line client stores its configuration in a module, located at the default path of ~/.cpan/CPAN/MyConfig.pm
on *nix-type systems. Granted, cpan
's MyConfig.pm
is a very simple example which just sets the hashref $CPAN::Config
, but there's no reason it couldn't also do all the other things any module does.
But doing it with do
is quite simple. I suspect you're just overthinking it:
$ cat test-override
#!/usr/bin/perl
use warnings;
use strict;
our @tables = qw(a b c);
sub print_tables {
print join(", ", @tables), "\n";
};
print_tables;
do "test-conf";
print_tables;
print "\@tables is @tables\n";
$ cat test-conf
@tables = qw(d e f);
sub print_tables {
print "print_tables from test_conf\n";
}
$ ./test-override
a, b, c
print_tables from test_conf
@tables is d e f
The important change I made with @tables
was to change it from my
, which is visible only within the current scope and the current file, to our
, which is visible anywhere within the same package (or from other packages if it's qualified with the package name).
But my print_tables
from the config file doesn't call the original print_tables
, and you're just out of luck on that one. Because there can only be one &main::print_tables
, replacing it completely overwrites the original one, which no longer exists. If you want to be able to override it and still be able to call the original, you need to put the two declarations into different packages, which kind of implies using OO Perl (so that you'll be able to polymorphically call the right one).
Also note that use
has the same lexical scoping as my
, which means that your use strict; use warnings;
does not carry over into the conf file. You can easily demonstrate this by adding a use warnings;
to my version of test-conf
, at which point it will then generate the warning Subroutine print_tables redefined
.
Upvotes: 3