How can I override hardcoded configuration in my Perl program?

I have a Perl script that sets variables at the top for the directories and files it will use. It also requires several variables to be specified as command line arguments. Example:

use Getopt::Long;

my ($mount_point, $sub_dir, $database_name, $database_schema);
# Populate variables from the command line:
GetOptions(
    'mount_point=s'       => \$mount_point,
    'sub_dir=s'           => \$sub_dir,
    'database_name=s'     => \$database_name,
    'database_schema=s'   => \$database_schema
);
# ...  validation of required arguments here

################################################################################
# Directory variables
################################################################################
my $input_directory    = "/${mount_point}/${sub_dir}/input";
my $output_directory   = "/${mount_point}/${sub_dir}/output";
my $log_directory      = "/${mount_point}/${sub_dir}/log";
my $database_directory = "/db/${database_name}";
my $database_scripts   = "${database_directory}/scripts";

################################################################################
# File variables
################################################################################
my $input_file       = "${input_dir}/input_file.dat";
my $output_file      = "${output_dir}/output_file.dat";
# ... etc

      

This works great in my dev / test / production environment. However, I tried to make it easier to override certain variables (without going into the debugger) for development and testing. (For example, if I want to set my input_file = "/tmp/my_input_file.dat"). My thought was to use the GetOptions function to handle this, something like this:

GetOptions(
    'input_directory=s'      => \$input_directory,
    'output_directory=s'     => \$output_directory,
    'database_directory=s'   => \$database_directory,
    'log_directory=s'        => \$log_directory,
    'database_scripts=s'     => \$database_scripts,
    'input_file=s'           => \$input_file,
    'output_file=s'          => \$output_file
);

      

GetOptions can only be called once (as far as I know). The first 4 arguments in my first snippit are required, the last 7 directly above are optional. I think the ideal situation would be to set the defaults as in my first code snippet and then somehow override any of them that were set if the arguments were passed on the command line. I thought about storing all my options in a hash and then using that hash when setting each variable with a default value, unless the entry exists in the hash, but it seems to add a lot of extra logic. Is there a way to call GetOptions at two different places in the script?

Not sure if this makes sense.

Thank!

-1


source to share


4 answers


Here's another approach. It uses arrays of names and hash to store parameters. It makes all options truly optional, but checks for required ones unless you include "-debug" on the command line. Regardless of whether you use "-debug", you can override any of the others.

You can do more explicit boolean checks if that's important to you, of course. I've included "-debug" as an example of how to omit basic options like "mount_point" if you're just going to override the "input_file" and "output_file" variables anyway.



The basic idea is that by storing sets of sets of option names in arrays, you can enable boolean checks against groups with relatively little code.

use Getopt::Long;

my @required_opts = qw(
    mount_point
    sub_dir
    database_name
    database_schema
);

my @internal_opts = qw(
    input_directory
    output_directory
    log_directory
    database_directory
    database_scripts
    input_file
    output_file
);

my @opt_spec = ("debug", map { "$_:s" } @required_opts, @internal_opts);

# Populate variables from the command line:
GetOptions( \(my %opts), @opt_spec );

# check required options unless 
my @errors = grep { ! exists $opts{$_} } @required_options;
if ( @errors && ! $opts{debug} ) {
    die "$0: missing required option(s): @errors\n";
}

################################################################################
# Directory variables
###############################################################################
my $opts{input_directory}    ||= "/$opts{mount_point}/$opts{sub_dir}/input";
my $opts{output_directory}   ||= "/$opts{mount_point}/$opts{sub_dir}/output";
my $opts{log_directory}      ||= "/$opts{mount_point}/$opts{sub_dir}/log";
my $opts{database_directory} ||= "/db/$opts{database_name}";
my $opts{database_scripts}   ||= "$opts{database_directory}/scripts";

################################################################################
# File variables
################################################################################
my $opts{input_file}    ||= "$opts{input_directory}/input_file.dat";
my $opts{output_file}   ||= "$opts{output_directory}/output_file.dat";
# ... etc

      

+6


source


It looks like you need to modify your program to use config files and not hardcoded config. I've devoted an entire chapter to Mastering Perl . You don't want to change the source code to test the program.

CPAN has many Perl modules that make it easy to add configuration files. Choose the one that works best for your input.



Once you get the best config model, you can easily set defaults, take values ​​from multiple locations (files, command line, etc.), and easily test the program with different values.

+7


source


I think what I would do is to set input_directory

et al to "undef" and then put them in getopts and then check if they are all undef and if so assign them as shown. If your users are technically sophisticated enough to figure out "if I am giving a relative path to $mount_point/$sub_dir

" then I would do some extra parsing looking for the original "/".

+3


source


GetOptions can be called with an array as input. Read the documentation.

0


source







All Articles