Commands-Guarded

 view release on metacpan or  search on metacpan

lib/Commands/Guarded.pm  view on Meta::CPAN


Returns a filehandle opened on FILENAME for writing.  Will throw an
exception if the file cannot be opened for writing.

=item appendf

Returns a filehandle opened on FILENAME for appending.  Will throw an
exception if the file cannot be opened for appending.

=back

=head1 RATIONALE

People often intuitively refer to some sorts of executables as
"scripts" and others as "programs."  When pressed for a definition,
they will often fall back on language-specific criteria (such as
whether the program is compiled or interpreted) that really do not
capture the essence of the difference between scripting and more
general-purpose programming.

A I<script> generally differs from other programs in the following ways
(there are exceptions):

=over

=item 1.

It makes heavy use of the external environment in which it
runs

=item 2.

It exports no complex data structures (though it may use them)

=item 3.

It has no outer event loop and does not daemonize (a simple
interactive prompt loop does not count)

=item 4.

It is usually run by the author, the author's agent (I<cron>,
etc.), or by a system administrator, rather than by the anonymous
"user"

=item 5.

It has as its primary purpose ensuring that some desired state
obtains in the system on which it runs (with "system" being defined as
broadly as necessary).

=back

Much has been written on good programming methodology, but in general
such methodologies have general-purpose programs in mind.  When
applied to scripts, which are generally very high-level and procedural
in nature, the methodologies can rapidly result in unreadable
spaghetti, with more code devoted to methodology than to method.

Most scripters react in one of two ways: they either let the spaghetti
ensue, or they throw up their hands and write fragile code.

=head2 An example

Suppose you want to write a script to mount a scratch directory from
an NFS server.  (This would usually be accomplished via a shell
language such as I<bash>, but for the sake of argument let's suppose
that you're writing in Perl, because you need access to another module
or perhaps just because you like Perl better.)

An optimistic implementation on a Red Hat Linux machine might be:

  # Add mount to filesystem table
  open FSTAB, ">>/etc/fstab";
  print FSTAB "$source:$scratch /net/$source/$scratch nfs $mount_opts\n";
  close FSTAB;
  # Create mountpoint
  mkdir $scratch;
  # Symlink to /scratch
  symlink "/net/$source/$scratch", '/scratch';
  # Start NFS services automatically at boot
  system "/sbin/chkconfig --level 3 portmap on";
  system "/sbin/chkconfig --level 3 nfslock on";
  # Start NFS services
  system "/sbin/service portmap start";
  system "/sbin/service nfslock start";
  # Mount at boot time
  system "/sbin/chkconfig --level 3 netfs on";
  # Mount now
  system "/sbin/service netfs start";

With no error-checking at all, this script would blindly charge on
oblivious to any problems.  If anything at all went wrong, the user
would be left to pick up the pieces afterwards.  Running the script a
second time could be perilous, as the print statement would continue
to append to I</etc/fstab> even if it had previously succeeded.

Good scripters will check for errors.  The most common response to
such errors is to abort:

  # Add mount to filesystem table
  open FSTAB, ">>/etc/fstab"
    or die "Can't open fstab for appending: $!\n";
  print FSTAB "$source:$scratch /net/$source/$scratch nfs $mount_opts\n";
  close FSTAB;
  # Create mountpoint
  mkdir $scratch
    or die "Can't create directory $scratch: $!\n";
  # Symlink to /scratch
  symlink "/net/$source/$scratch", '/scratch'
    or die "Can't make symlink to /scratch: $!\n";
  # Start NFS services automatically at boot
  system "/sbin/chkconfig --level 3 portmap on";
  if ($?) {
     die "Couldn't chkconfig on portmap\n";
  }
  system "/sbin/chkconfig --level 3 nfslock on";
  if ($?) {
     die "Couldn't chkconfig on nfslock\n";
  }
  # Start NFS services



( run in 1.152 second using v1.01-cache-2.11-cpan-364913b4093 )