John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

In my Work Backup program I configure the script with a list of things to do, thus:
my @locations= ( [ source => 'D:\\dev' ], [ docs => 'D:\\My Documents' ], [ source => 'F:\\dev' ], [ docs => 'F:\\Documents' ], [ norule => 'D:\\Program Files\\util'] );
Now, I want to make it more complex, allowing for multiple rules and modifiers to be applied to one item. So, what's a good way to do this?

In Tk, a general mechanism is used for passing arguments that begin with a dash, followed by arguments for that particular flag. Some general code pulls that out and deals with it.

[ qw ( -this -that 5 6 -other /path/etc/foo ) ],
Another idea is to have one item per list element, period, and if it had arguments include it in a nested list. E.g.
[ '-this', [ that => 5, 6 ], '-other', '/path/etc/foo' ],
This is easier to process and clear that the 5 and 6 go with the 'that'.

In the script, each modifier will be implemented via a callback, which takes the extra arguments too, and the callback can modify the job's object state in some way.

Does anyone else have any suggestions on a good way to do this? "good" for both the user configuring the thing, and the implementation.

—John

Replies are listed 'Best First'.
(jeffa) Re: Ways of Passing Configuration Parameters
by jeffa (Bishop) on Jun 08, 2001 at 04:39 UTC
    Just to give you an idea, here's a little trick i like to use when i need to process 1 or more items. If the argument is a scalar, then wrap it in an array ref:
    sub some_func { my ($args) = @_; # if $args is a scalar, then make it an array ref $args = [$args] unless ref $args eq 'ARRAY'; # now process foreach my $arg (@$args) { # do stuff } } # sample calls: some_func($one_item); some_func([qw(bunch of items)]);
    From there it is a piece of cake to pass multiples of these in the hash construct that you showed:
    some_func({ this => $one_item, that => [$some, $more, $items], });
    Hope this helps. :)

    Jeff

    R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
    L-L--L-L--L-L--L-L--L-L--L-L--L-L--
    
Re: Ways of Passing Configuration Parameters
by DrZaius (Monk) on Jun 08, 2001 at 08:37 UTC
    If you want to configure on the command line, which I think you are trying to do, take a peak at Getopt::Long.

    It does the dash stuff for you, and can also handle multi valued arguments (--foo=bar --foo=monkey)

    Cheers.

      No, it's not specified on the command line. It's a data declaration in a file. See the node referenced in the original note for details one what it's for.

      I thought about using Getopt::Long anyway, but don't know if I can make it look at an array other than @ARGV.

        I'm not sure that Getopt::Long is the right tool for the job here, but if you want to use it, localize @ARGV and assign your array of configuration parameters to @ARGV before making a call to Getopt::Long. There's some code and discussion of this here.

        ----
        Coyote

Re: Ways of Passing Configuration Parameters
by mirod (Canon) on Jun 08, 2001 at 11:47 UTC

    I have had some fun playing with Getopt::Declare, one of the many modules that do command line argument processing and one of the hundreds of modules Damian Conway committed, but I think the only one to belong in both categories.

    It is really very powerful and flexible, more in the spirit of Parse::RecDescent than of Getopt::Casual ;--)

(tye)Re: Ways of Passing Configuration Parameters
by tye (Sage) on Jun 09, 2001 at 01:58 UTC

    My preference is a single hash ref for options with an explicit "1" for Boolean options: subr( { this=>1, that=>[ 5, 6 ], other=>'/path/etc/foo' } )

    I prefer the hash ref for several reason. It requires the caller to put in the { and } which let's Perl catch if they put in a odd number of arguments. It fits in well with routines that have some required arguments besides the hash options. It makes it easier to extend the interface in the future. One routine can just pass the same options along to another without any copying involved. It makes validating the passed-in arguments easier [just UNIVERSAL::isa($opts,"HASH")]. It forces your code to not depend on the order in which the options are listed.

    One disadvantage is that you should be careful to avoid having your routine modify the ref'd hash.

            - tye (but my friends call me "Tye")
      I like that, and is one I'm considering, because the configurer will type the same kind of structure that the callback gets. But I'm expecting most things to have no arguments, so I don't like needing the arg.

        Well, another advantage I've found with this is that you can make changes more easily in many cases because: subr( { ..., debug=>0, ... } ) is often a smaller change than having to drop or insert an argument. (:

                - tye (but my friends call me "Tye")
Re: Ways of Passing Configuration Parameters
by bikeNomad (Priest) on Jun 08, 2001 at 19:43 UTC
    You can use Getopt::Long for much of this:
    #!/usr/bin/perl -w use strict; use Getopt::Long; use Data::Dumper; my @options = (-something => 'abc', -xyz => -abc => 'def', 'leftover') +; my @leftovers; my %opts; my @save_ARGV = @ARGV; { local @ARGV = @options; my $result = GetOptions(\%opts, 'something=s','xyz','abc=s'); @leftovers = @ARGV; } print Dumper(\%opts); print Dumper(\@leftovers); print Dumper(\@ARGV);