* your name * your email address * your homepage if you have one * your preferred user-ID on CPAN. It must be between 4 and 9 characters long, all uppercase, letters only. One dash allowed. * a short description of what you're planning to contribute
Jim Cromie [EMAIL PROTECTED] no homepage jcromie =head1 SYNOPSIS thisVersionIs thisVersionIs creates %VERSION in the calling package's namespace, and fills it with information extracted from the RCS identity string. =head1 USAGE package Foo; use thisVersionIs ('$Id$'); use thisVersionIs ( '$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $' ); use thisVersionIs ( RCS => '$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $' ); =head1 DESCRIPTION As the usage above shows, thisVersionIs is 'called' with an RCS_ID string as the 'symbol' to import. This string is parsed, and the info is extracted and written into the calling package's %VERSION hash. Note that the RCS_ID string is single-quoted so its not subject to perl variable substitution, but is subject to RCS-tag substitution. When the calling file has been checked out readonly, its complete version information is available for various uses. The 1st example shows a file checked out for edit. No version info is available via this package. In the 2nd example, the RCS revision control system is assumed, the data are stored into the following keys; file, date, time, author, status. In the 3rd example, RCS is explicitly named as the system of choice. Note also that in some contexts I use RCS as a generic term, referring to any such system. Attached is a pre-alpha version, but it runs.. if youre not too busy getting to 5.8.0, give it a try. if you'll accept it in principle (or with lots of caveats ), I'll package it up after fixing it accordingly. tia jimc
################ -*- perl -*- package thisVersionIs; # use base Exporter; use Carp(); =head1 NOTE TO INTERESTED REVIEWERS I put this out for your consideration, hopefully you find it cool and potentially useful. I invite your comments on even niggling little details, and on the grand scale 'what it might be useful for'. About terminology, Ive got some muddled names of things, but cant come up with better ones. Any suggestions? =head1 SYNOPSIS thisVersionIs thisVersionIs creates %VERSION in the calling package's namespace, and fills it with information extracted from the RCS identity string. =head1 USAGE use thisVersionIs ('$Id$'); use thisVersionIs ( '$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $' ); use thisVersionIs ( RCS => '$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $' ); =head1 DESCRIPTION As the usage above shows, thisVersionIs is 'called' with an RCS_ID string as the 'symbol' to import. This string is parsed, and the info is extracted and written into the calling package's %VERSION hash. Note that the RCS_ID string is single-quoted so its not subject to perl variable substitution, but is subject to RCS-tag substitution. When the calling file has been checked out readonly, its complete version information is available for various uses. The 1st example shows a file checked out for edit. No version info is available via this package. In the 2nd example, the RCS revision control system is assumed, the data are stored into the following keys; file, date, time, author, status. In the 3rd example, RCS is explicitly named as the system of choice. Note also that in some contexts I use RCS as a generic term, referring to any such system. =head2 IMPLEMENTATION thisVersionIs operates a bit differently than a typical exporting module; rather than creating a package-to-package typeglob alias, the import function creates tag value pairs directly into the caller package. The primary difference is that %VERSION info is not a shared, common item, but one built for each user. =head1 WHY BOTHER ? The following applications are possible, but their value is highly environmentally dependent. Im hoping you'll help find real applications. detailed build info available independent check on code release contents detect when release contains files that are checked-out-for-edit author can be sent email on appropriate events help identify version interactions in problem cases verify expected results of $PERL5LIB, use lib, etc (particularly for complex and hard to manage deployments) provide info to regression-test harness better info to feed automatic staging, promotional tools info for inter-version-bug cataloging =head1 TO DO 1st and foremost, its open to suggestion. Note the VERSION = 0.01 Is this mis-use of the import/export mechanizm fundamentally flawed ? Are there any suprises in multiple use of this package (consider the conventional use-Once semantics of the use directive). Any suprises when user modules are built with inheritance ? Ive done some cursory tests myself, but these questions warrant a 2nd consideration. Add working tagset for SCCS and other RCS-type systems. Consider unifying tags from various RCS-types. Currently the tags stored into %VERSION are nearly identical to the RCS-tags, but it might be useful to map all tags from all rcs-type systems into 1 common set. By default, the standard RCS tags would be used, and other tagsets would be mapped onto the default set. Is there any need for such unification ? Id guess that 99% of individual sites would use a single RCS-type system, but we do have CPAN, which brings 'foreign' code into a site. Ok, its basically native, but uniform use of RCS is a long way off, and its not clear that anyone is trying to go there. Consider using identical tags to those used in RCS, with the capitalization, see above. Consider adding traditional EXPORT_TAGS, something like :die-on-edit, :carp-on-edit. The purpose of these example tags is to control policy; ie what to do when a file is checked-out-for-edit, and thus doesnt have useful VERSION information. Is @EXPORT_FAIL = (qw/ edit_carp edit_die/) a better way to handle this (ie the way Carp.pm handles 'verbose' ? By inference from man Carp, this solves consistency problem. Consider partitioning into subclasses on policy decisions (and/or RCS-type). This partially addresses the shortcoming of the tag-approach where consistent policy depends upon consistent use of this package. Build a Makefile.PL which prompts for site-wide policies, and sets appropriate defaults into installed package. Figure out how to make policy reflect the type of host-machine; devel, integration-test, system-test, production. $VERSION is overwritten with $VERSION{rev}, thus guaranteeing consistency. This might not be a good idea because theyre counting different things. note that 'use base Exporter' is commented out, code works without it. Should I be inheriting Exporter functionality rather than re-implementing part of its interface ? Can someone explain why all repeat uses work (accumulating and overwriting info) except for the double self-(ab)?use in the package itself? =cut use strict; my %tagsets; my $opt_v = 1; $thisVersionIs::VERSION = 1000; # test override BEGIN { %tagsets = ( # top level keys have values which are hashes containing # tagsets for respective RCS-type systems RCS => { # keys validate the RCS-tags received from caller # values are used to store the following info Author => [qw/ author /], Date => [qw/ date /], Header => [qw/ path rev date time author state locker/], Id => [qw/ file rev date time author state locker/], Locker => [qw/ locker /], Log => [qw/ log /], Name => [qw/ name /], RCSfile => [qw/ file /], Revision=> [qw/ rev /], Source => [qw/ path /], State => [qw/ state /], }, SCCS => {}, PVCS => {}, ); print "loading Module\n" if $opt_v; }; sub carp { # slightly more readable output print STDERR "\t"; &Carp::carp; # transparent @_ passthru } sub import { my ($pkg) = caller; shift; # dump this package name # my ($tag, $info, @info, $rcstags); my ($rcstags); print "running thisVersionIs::import into '$pkg' with @_\n" if $opt_v; carp "no RCSID string provided" unless @_; # EXPORT_TAGS would be handled here my $rcstype = 'RCS'; # default # if more than 1 arg, treat as revision control system type $rcstype = shift if @_ > 1; # test if rcstype is known, if (grep $rcstype eq $_, keys %tagsets) { # explicit CSID, use it $rcstags = $tagsets{$rcstype}; } else { local $, = " "; print "known types ", keys %tagsets, "\n" if $opt_v; carp "unknown Rev Control System type: '$rcstype'"; } # NOTE: heres where RCS specific handling starts # chop RCS_ID string on spaces (generic?) my ($tag, @info) = split(/\s+/, $_[0]); # check RCS_ID string has populated format if ($tag =~ /\$(\w+)\$/) { carp "un-substituted format, file is checked out for edit"; } elsif ($tag =~ /\$(\w+):/) { # found tag with following colon, not checked out, OK # now verify that tag is legitimate $tag = $1; carp "illegal tag '$tag' for $rcstype system" if ! grep $tag eq $_, keys %$rcstags; # ok actually store the info # array context assignment into hash, see camel pg 82 # (one of my favorite Perl-isms, how many lines in Java?) # which of these 2 is 'better', and why ? # local *hash = *{$pkg.'::VERSION'}; # local *hash = *{"${pkg}::VERSION"}; use vars ('*aglob'); no strict 'refs'; local *aglob = *{"${pkg}::VERSION"}; @aglob{@{@$rcstags{$tag}}} = @info; # @{$pkg.'::VERSION'}{@{@$rcstags{$tag}}} = @info; # overwrite $VERSION to insure consistency $aglob = $aglob{rev}; if ($opt_v) { print "\t${pkg}::VERSION{$_} => ", ${"${pkg}::VERSION"}{$_},"\n" foreach (keys %{$pkg.'::VERSION'}); } } } # apply version feature to this package BEGIN { print "in BEGIN block neither self-init does anything\n"; use thisVersionIs( '$Id: thisVersionIs.pm 0.01 1999/07/01 17:40:15 jimc Exp $' ); thisVersionIs->import( '$Id: thisVersionIs.pm 0.01 1999/07/01 17:40:15 jimc Exp $' ); } print "w/o block either (but only 1) will work\n"; use thisVersionIs( '$Id: thisVersionIs.pm 0.01 1999/07/01 17:40:15 jimc Exp $' ); thisVersionIs->import( '$Id: thisVersionIs.pm 0.01 1999/07/01 17:40:15 jimc Exp $' ); 1; __END__ =head TESTING: paste this into another file, and run it. doing so from a different file seems to matter wrt to use mechanism is there a pod tag that works like <pre> ? ######## -*- perl -*- #!/usr/bin/perl -w # use a bunch of times to see what happens # create packages just for clearer reporting BEGIN { warn "\n RUN A BUNCH OF TESTS WHICH SHOULD GET ERRORS\n\n"; } package Empty; use thisVersionIs; package Editable; use thisVersionIs ('$Header$'); package SCCS; use thisVersionIs ( SCCS => '$Header: swill $'); package BadTag; use thisVersionIs ('$badTag: junk follows'); package BadRCSType; use thisVersionIs ( BOGUS_RCS => '$Header$'); use thisVersionIs ( BOGUS_RCS => '$Author: joeblow '); BEGIN { warn "THESE TESTS SHOULD RUN OK\n" } package main; use thisVersionIs ( '$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $' ); use thisVersionIs ( '$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $' ); foreach (keys %main::VERSION) { print "\tVERSION{$_} => $VERSION{$_}\n"; } print "VERSION = $VERSION\n"; package Junk; use thisVersionIs ( '$Id: junk.pm 5.4 1993/11/09 17:40:15 eggert Exp $' ); package Junk::Swill; use base Junk; use thisVersionIs ( '$Id: swill.pm 5.4 1993/11/09 17:40:15 eggert Exp $' );
#!/usr/bin/perl -w # -*- perl -*- # use a bunch of times to see what happens # create packages just for clearer reporting BEGIN { warn "\nRUN A BUNCH OF TESTS WHICH SHOULD GET ERRORS\n\n"; } package Empty; use thisVersionIs; package Editable; use thisVersionIs ('$Header$'); package SCCS; use thisVersionIs ( SCCS => '$Header: swill $'); package BadTag; use thisVersionIs ('$badTag: junk follows'); package BadRCSType; use thisVersionIs ( BOGUS_RCS => '$Header$'); use thisVersionIs ( BOGUS_RCS => '$Author: joeblow $'); BEGIN { warn "\nTHESE TESTS SHOULD RUN OK\n\n"; } package main; use thisVersionIs ( '$Author: joeblow $' ); # augment info use thisVersionIs ( '$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $' ); # override 1 field use thisVersionIs ( '$Author: joeblow $' ); print "in main, VERSION = $VERSION\n"; foreach (keys %main::VERSION) { print "\tVERSION{$_} => $VERSION{$_}\n"; } package Junk; use thisVersionIs ( '$Id: junk.pm 5.4 1993/11/09 17:40:15 eggert Exp $' ); package Junk::Swill; use base Junk; use thisVersionIs ( '$Id: swill.pm 5.4 1993/11/09 17:40:15 eggert Exp $' );