5.4. At Your Command

It would be better to have the option to change the combination of debug flags at run time rather than having to change the source each time we want different output.

Migrate option setting to the most convenient input interface possible.


In other words, when you have an option that can be changed, the least desirable place to do it is in the source code (within which there are better and worse ways of doing it, as we have discussed); the next best choice is an input configuration file (if your program supports one), an environment variable, or a command line option. Many people write programs that support all four methods with each one being overridden by the next. We'll just demonstrate how to set debug flags from the command line, falling back to the source code.

use constant WEB => 1;
use constant SQL => 2;
use constant REG => 4;
use Getopts::Std;
use vars qw($opt_D);

$opt_D = WEB | REG;  # Default

sub dbgprt ($@);
getopts('D:') or die &usage;

# code ...

dbgprt SQL, "Query returned $rows rows
";

# more code ...

sub dbgprt ($@)
   {
   my $class = shift;
   warn "***DEBUG*** ", @_ if $opt_D & $class;
   }

We can now call our program with the command line option -D, followed by the number resulting from ORing all our desired options:[6]

[6] Of course, e-commerce middleware is unlikely to be started from the command line or to log its output to the terminal, but those are orthogonal issues.

% whizzbang.pl -D 5   # Set WEB and REG options

Later in our program we include a usage subroutine that returns a string telling the user how they should really call this program if they made a mistake. If this program takes other command line options—a distinct possibility—then their letters will be added to the getopts call.

Hmm, wouldn't it be nice if we could set them from the command line symbolically as well? Say, with something like

% whizzbang.pl -D WEB,REG
				

Now we've not only decoupled the interface from the constant definitions in the source, but we've done away with the need for the comment. Easy enough:

use constant WEB => 1;
use constant SQL => 2;
use constant REG => 4;
my %dbg_flag = (
   WEB => WEB,
   SQL => SQL,
   REG => REG);

use Getopt::Std;
use vars qw($opt_D);

my $DEBUG = WEB | REG;  # Default

sub dbgprt ($@);
getopts('D:') or die &usage;

if ($opt_D)
   {
   $DEBUG = 0;
   $DEBUG |= $dbg_flag{uc $_}
          || die "Unknown debug flag $_
"
      foreach split /,/, $opt_D;
   }

# code ...

dbgprt REG, "User $username has $$bal left in account
";

# more code ...

sub dbgprt ($@)
   {
   my $class = shift;
   warn "***DEBUG*** ", @_ if $DEBUG & $class;
   }

There's nothing to be intimidated by here. First, we've added a hash %dbg_flag that maps the symbolic names to their values. We wisely chose the strings that we're accepting as input to have the same names as the symbolic constants in our source code, so the hash initialization becomes extremely easy to maintain (the keys on the left are barewords that get converted to strings without the need for quoting because we're using the => operator to do it for us).

Then we set a default for $DEBUG, our variable holding the set of debug message classes to print, and checked to see whether $opt_D was set, which it would be only if the user put it on the command line. If it was, then we logically ORed together the values in the hash whose keys are each string between commas in the option value and, of course, provided feedback if the user specified an incorrect one.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset