j3 has asked for the wisdom of the Perl Monks concerning the following question:

I've just begun poking around the nms scripts, and am seeing something odd (to me) at the end of some of their files.

The files in question (guestbook.pl, for example) end with a long BEGIN block. I'm hoping someone here can explain a few details about what it's doing. Here's a sample (taken from guestbook.pl, edited for brevity):

(Edit: removed readmore tags)

#/usr/bin/perl -wT # ... {snip regular stuff (code + some POD)} ... # ... {and now, at the end of the file} BEGIN { eval 'local $SIG{__DIE__} ; require CGI::NMS::IPFilter'; $@ and $INC{'CGI/NMS/IPFilter.pm'} = 1; $@ and eval <<'END_CGI_NMS_IPFILTER' || die $@; ## BEGIN INLINED CGI::NMS::IPFilter package CGI::NMS::IPFilter; use strict; + require 5.00404; + use Socket; # ... {snip code interspersed with POD} ... 1; ## END INLINED CGI::NMS::IPFilter END_CGI_NMS_IPFILTER CGI::NMS::IPFilter->import(); }

Now, there seems to be a number of things going on here, a couple of which I might even understand :) :

  1. The author wants this BEGIN {...} code in the script to happen first, evidently in case the CGI::NMS::IPFilter module can't be found in the usual places.
  2. Why localize $SIG{__DIE__} in that first eval? So far, I've only seen %SIG used where you set a value to the name of a sub you want called in the event of getting the given signal. But we're not setting it to anything here...
  3. Why require instead of use here?
  4. If there's an error in running the short line of eval'd code, add a special value to $INC. Why set that $INC{...} value to 1 instead of a fully-qualified path name (say, the name of this current file)?
  5. Again, if something died during the first eval, eval this big single-quoted heredoc string (which is a module itself, taking up a couple of pages) -- and if *that* eval fails, just bail out.
    • Why doesn't the author simply distribute an external .pm file for this purpose? Is this a common practice?

What's the most common way of handling this situation, where you want either the installed module first, or else the one that you're including in your distribution? I've heard the term ``Orcish maneuver'', but I guess this is more of an ... hmm... maybe ``or just use this one here'' (the Ojutoh defense? ).

Replies are listed 'Best First'.
Re: Odd convention? (fall-back module in BEGIN block)
by kyle (Abbot) on Feb 15, 2007 at 21:10 UTC

    Why localize $SIG{__DIE__} in that first eval?

    I think this is in case there's already a $SIG{__DIE__} defined. The author would prefer that it be undefined—that die behave normally.

      Whoops. Right. Of course. Thanks kyle.

      (Edit): But why was it placed inside the eval string instead of the line above the eval?

        So as not to disturb the subsequent eval.

        As to why to inline the module, my guess is so as to keep the entire file runnable as a single file. To install it, just put it in the appropriate directory for your web server. That's it.

Re: Odd convention? (fall-back module in BEGIN block)
by Anonymous Monk on Feb 15, 2007 at 21:06 UTC
    local $SIG{__DIE__}=sub{warn 'sig die ', @_}; eval 'die'; eval 'local $SIG{__DIE__}; die'; __END__ sig die Died at (eval 1) line 1.
Re: Odd convention? (fall-back module in BEGIN block)
by Anonymous Monk on Feb 15, 2007 at 21:05 UTC
    perldoc -f require
    perldoc -f use
    

      I created my own small example, and both use and require seem to work equally well.

      I realize that use Foo; is equivalent to BEGIN { require Foo; Foo->import; }, and so using use ``adds one more level''... but it doesn't seem to make a difference in this case, does it?

        It can make a difference depending on what's in "...".

        Compare

        BEGIN { eval 'sub foo { return 2 }'; } my $i = foo 3; # my $i = 2; print("$i\n");

        with

        eval 'sub foo { return 2 }'; my $i = foo 3; # Syntax error! print("$i\n");

        Compare

        BEGIN { eval 'sub foo(\@) { print(@{$_[0]}, "\n"); }'; } my @a = qw( foo bar ); foo(@a); # foobar

        with

        eval 'sub foo(\@) { print(@{$_[0]}, "\n"); }'; my @a = qw( foo bar ); foo(@a); # [undef]

        Compare

        BEGIN { eval 'sub foo { print("foo\n"); }'; } BEGIN { foo(); } # foo

        with

        eval 'sub foo { print("foo\n"); }'; BEGIN { foo(); } # Dies. Can't find foo()

        etc.