strict::ModuleName - verify that current package name matches filename


strict-ModuleName documentation Contained in the strict-ModuleName distribution.

Index


Code Index:

NAME

Top

strict::ModuleName -- verify that current package name matches filename

SYNOPSIS

Top

    # In a file (some @INC dir)/Shazbot.pm:
    package Shazbot;
    use strict::ModuleName;
     # does nothing, because Shazbot.pm matches package name "Shazbot"

That does nothing, because the package name "Shazbot" is exactly what you'd expect from "Shazbot.pm" in an @INC directory.

But any of these will throw a fatal error:

    # In a file (some @INC dir)/Shazbot.pm:
    package ShazBot;
    use strict::ModuleName;
     # that's a fatal error, because Shazbot isn't ShazBot

    # In a file (some @INC dir)/Shazbot.pm:
    package Shaz::Bot;
    use strict::ModuleName;
     # that's a fatal error, because Shazbot isn't Shaz::Bot

    # In a file (not any @INC dir)/Shazbot.pm:
    package Shazbot;
    use strict::ModuleName;
     # That's a fatal error, because ShazBot wasn't findable
     #  via any @INC dir.

DESCRIPTION

Top

This module stops you from having your module's filename and package name disagree, such as might happen as you're changing the name as you're developing the module; or such as might happen if you are using a case insensitive filesystem, and get the case wrong in the filename.

A line saying use strict::ModuleName; in a module is basically an compile-time assertion that the current package name is compatible with the filename which the current source is being read from.

NOTES

Top

Maybe this module should just warn() more instead of die()ing?

COPYRIGHT

Top

AUTHOR

Top

Sean M. Burke sburke@cpan.org


strict-ModuleName documentation Contained in the strict-ModuleName distribution.

require 5;
package strict::ModuleName;     # Pod at end
$VERSION = '0.04';
use strict;
BEGIN { *DEBUG = sub () {0} unless defined &DEBUG }
use vars qw($DIE);
$DIE = 1 unless defined $DIE;

sub import {
  # Make sure that the calling package's name agrees with its filename
  if(@_ > 1) {
    require Carp;
    for my $msg (
     "Proper usage: use " . __PACKAGE__ . "; #(with no parameters) "
    ) { $DIE ? Carp::croak($msg) : Carp::carp($msg) }
  }
  my($package, $filename) = caller(0);

  unless($filename =~ m/.\.pm$/s) {  # catch this first off
    if($filename =~ m/.(\.pm)$/is) {
      for my $msg (
       "filename \"$filename\" should end in \".pm\", not \"$1\"\n"
      ) { return $DIE ? die($msg) : warn($msg) }
    } else {
      for my $msg (
       "filename \"$filename\" should end in \".pm\"!\n"
      ) { return $DIE ? die($msg) : warn($msg) }
    }
  }

  my $pre = quotemeta($package);
  $pre =~ s/(\\[\'\:])+/./g;  # Foo::Bar => Foo.Bar
  
  DEBUG and print ">>>>. $package in $filename\n";

  my $re = join '',
    '^(',
    join('|', map quotemeta($_), 
      sort {length($b) <=> length($a)}
        @INC
    ),
    ')',
    '\W{0,2}',
       # generous RE matching trailing pathsep thing like / or \ or :
    $pre,
    '\.pm$',
  ;

  if(DEBUG) {
    DEBUG and print $re, "\n\n";
    for(0 .. 10) {
      print("\n"), last unless defined caller($_);
      print "caller($_) is ", join(" # ", map $_ || '', (caller($_))[0..7] ), "\n";
    }
  }
  
  if($filename =~ m/$re/s) {
    DEBUG and print "file \"$filename\" producing package \"$package\" is okay\n";
    
  } else {
  
    {
      # Jump thru hoops to check for a very common case:
      #  whether that package was like "perl -cw X.pm" or "perl -w X.pm"
      
      my @callstack;
      my $back_count = 0;
      my $real_depth = 0;
      while(1) {
        last unless defined caller($back_count);
        my $sub_name = (caller($back_count))[3];
        ++$real_depth
         unless $sub_name eq '(eval)' or $sub_name =~ m/\:\:BEGIN$/s;
        ++$back_count;
      }
      my $fn = $filename;
      $fn =~ s/\.pm$//s or die "WHAAAAAT?";
      
      if($real_depth == 1
        and length($fn) <= length($package)
        and substr($package, 0 - length($fn)) eq $fn
      ) {
        warn(   # yes, merely warn
         "Can't verify whether package name \"$package\" is good in \"$filename\""
         . "\n -- Instead try:  perl -M$package -e -1\n"
        );
        return;
      }
    }
    
    if(grep ref($_), @INC) {
      warn(
       "file \"$filename\" producing package \"$package\" may be bad,\n"
       . "  -- but I can't be sure, because there's coderefs in \@INC\n");
      return;
    }
      
    for my $msg (
     "file \"$filename\" producing package \"$package\" is bad\n"
    ) { return $DIE ? die($msg) : warn($msg) }
  }
  
  return;
}

&import(); # Yes, test myself!

1;

__END__