Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

comment on

( #3333=superdoc: print w/replies, xml ) Need Help??

The other day, BooK posted the following to the module-authors mailing list:

[Y]ou could also simply expose the information in the documentation, and fetch it from there: (a trick I discovered thanks to Abigail’s additions to Acme::MetaSyntactic, see the upcoming Acme::MetaSyntactic::tour_de_france for an example):

my %const = map { s/\s+//; $_ } map { split /\s*=>\s*/ } grep { /=>/ } map { split /\n/ } << '=cut'; =pod This module uses the following constants: bang_eth => 1 biff => 2 krunch => 3 =cut

Now, that in itself is a damn cool hack.

But it immediately set my mind thinking about how to use it for the one thing that always annoys me about module maintenance: updating the module version in both your POD and on the $VERSION line.

Turns out, this is actually very tricky because you have to get the polyglot understood by three different tools:

  1. perl, which uses a very simple rule for what it regards as non-POD. Easy – see above.

  2. POD formatters, which use even simpler rules for what they regard as POD. No problems here, and it’s what makes Abigail’s trick possible.

  3. ExtUtils::MakeMaker – or to be precise, its MM->parse_version method –, which is what a lot of modules use to extract version information from modules. Oh dear.

    It skips POD using… shall we say, simplistic rules, much like POD formatters, so it will tend to successfully ignore precisely the things that a POD formatter will accept. It will also accept only a single line, which will be eval’ed in isolation.

In other words, something like this, which was my first thought, won’t work:

$VERSION = ( <<'=cut' =~ /\b\d+\.\d+\b/ ); =head1 VERSION This document describes Some::Module 0.1 =cut

If you try that, you will find that parse_version will eval just this:

$VERSION = ( <<'=cut' =~ /\b\d+\.\d+\b/ );


I had to resort to treachery: reading MakeMaker’s source to find its weaknesses. And the weakness, it turns out, is that it uses /^=cut/ to stop skipping. Notice something? That matches too many things… Gotcha! You’re going down.

Unfortunately, the single-line requirement means that the version number must be on the same line as the string $VERSION, which means we’ll have to have that in the POD:

eval "package Some::Module; $_" for grep m/ = /, split /\n/, <<'=cut'; =head1 VERSION =for fooling makemaker =cut-feigned This document describes Some::Module, $VERSION = 0.1 =cut

Here, the heredoc operator on the first line sets perl up to treat the entire following section as a string. In that string we look for a line with an equals operator, then eval it.

The =for line makes POD formatters ignore what’s on the next line, unless one of them thinks it’s the formatter for the output format called “fooling”, for which a formatter is unlikely to ever be written.

And what’s on the next line, the =cut-feigned, makes parse_version stop skipping and look for a line which sets $VERSION.

It works:

$ perldoc ./Some/ | grep VERSION This document describes Some::Module, $VERSION = 0.1 $ perl -MSome::Module \ -le'print Some::Module->VERSION' 0.1 $ perl -MExtUtils::MakeMaker \ -le'print MM->parse_version(shift)' Some/ 0.1

Would I use this is actual CPAN-published code? I don’t know. But you have to admit, it is really quite a fun hack.

Makeshifts last the longest.

In reply to Don’t Repeat Your… version number by Aristotle

Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others musing on the Monastery: (4)
    As of 2021-01-21 22:59 GMT
    Find Nodes?
      Voting Booth?