Pod was designed first and foremost to be easy to write. As an
added benefit, pod's simplicity also lends itself to writing simple
tools for processing pod. If you're looking for pod directives, just
set your input record separator to paragraph mode (perhaps with the
-00
switch), and only pay attention to
paragraphs that look poddish.
For example, here's a simple olpod program to produce a pod outline:
#!/usr/bin/perl -l00n # olpod - outline pod next unless /^=head/; s/^=head(d)s+/ ' ' x ($1 * 4 - 4)/e; print $_, " ";
If you run that on the current chapter of this book, you'll get something like this:
Plain Old Documentation Pod in a Nutshell Verbatim Paragraphs Pod Directives Pod Sequences Pod Translators and Modules Writing Your Own Pod Tools Pod Pitfalls Documenting Your Perl Programs
That pod outliner didn't really pay attention to whether it was in a valid pod block or not. Since pod and nonpod can intermingle in the same file, running general-purpose tools to search or analyze the whole file doesn't always make sense. But that's no problem, given how easy it is to write tools for pod. Here's a tool that is aware of the difference between pod and nonpod, and produces only the pod:
#!/usr/bin/perl -00 # catpod - cat out just the pods while (<>) { if (! $inpod) { $inpod = /^=/; } if ($inpod) { $inpod = !/^=cut/; print; } } continue { if (eof) { close ARGV; $inpod = ''; } }
You could use that program on another Perl program or module, then pipe the output along to another tool. For example, if you have the wc (1) program[2] to count lines, words, and characters, you could feed it catpod output to consider only pod in its counting:
% catpod MyModule.pm | wc
There are plenty of places where pod allows you to write primitive tools trivially using plain, straightforward Perl. Now that you have catpod to use as a component, here's another tool to show just the indented code:
#!/usr/bin/perl -n00 # podlit - print the indented literal blocks from pod input print if /^s/;
What would you do with that? Well, you might want to do perl -wc checks on the code in the document, for one thing. Or maybe you want a flavor of grep (1)[3] that only looks at the code examples:
% catpod MyModule.pm | podlit | grep funcname
This tool-and-filter philosophy of interchangeable (and separately testable) parts is a sublimely simple and powerful approach to designing reusable software components. It's a form of laziness to just put together a minimal solution that gets the job done today--for certain kinds of jobs, at least.
For other tasks, though, this can even be counterproductive. Sometimes it's more work to write a tool from scratch, sometimes less. For those we showed you earlier, Perl's native text-processing prowess makes it expedient to use brute force. But not everything works that way. As you play with pod, you might notice that although its directives are simple to parse, its sequences can get a little dicey. Although some, um, subcorrect translators don't accommodate this, sequences can nest within other sequences and can have variable-length delimiters.
Instead of coding up all that parsing code on your own, laziness
looks for another solution. The standard
Pod::Parser
module fits that bill. It's especially
useful for complicated tasks, like those that require real parsing of
the internal bits of the paragraphs, conversion into alternative
output formats, and so on. It's easier to use the module for
complicated cases, because the amount of code you end up writing is
smaller. It's also better because the tricky parsing is already worked
out for you. It's really the same principle as using
catpod in a pipeline.
The Pod::Parser
module takes an interesting
approach to its job. It's an object-oriented module of a different
flavor than most you've seen in this book. Its primary goal isn't so
much to provide objects for direct manipulation as it is to provide a
base class upon which other classes can be built.
You create your own class and inherit from
Pod::Parser
. Then you declare subroutines to serve
as callback methods for your parent class's parser to invoke. It's a
very different way of programming than the procedural programs given
earlier. In a sense, it's more of a declarative programming style,
because to get the job done, you simply register functions and let
other entities invoke them for you. The program's tiresome logic is
handled elsewhere. You just give some plug-and-play pieces.
Here's a rewrite of the original catpod
program given earlier, but this time it uses the
Pod::Parser
module to create our own
subclass:
#!/usr/bin/perl
# catpod2, class and program
package catpod_parser;
use Pod::Parser;
@ISA = qw(Pod::Parser);
sub command {
my ($parser, $command, $paragraph, $line_num) = @_;
my $out_fh = $parser->output_handle();
$paragraph .= "
" unless substr($paragraph, -1) eq "
";
$paragraph .= "
" unless substr($paragraph, -2) eq "
";
print $out_fh "=$command $paragraph";
}
sub verbatim {
my ($parser, $paragraph, $line_num) = @_;
my $out_fh = $parser->output_handle();
print $out_fh $paragraph;
}
sub textblock {
my ($parser, $paragraph, $line_num) = @_;
my $out_fh = $parser->output_handle();
print $out_fh $paragraph;
}
sub interior_sequence {
my ($parser, $seq_command, $seq_argument) = @_;
return "$seq_command<$seq_argument>";
}
if (!caller) {
package main;
my $parser = catpod_parser::->new();
unshift @ARGV, '-' unless @ARGV;
for (@ARGV) { $parser->parse_from_file($_); }
}
1;
__END__
=head1 NAME
docs describing the new catpod program here
As you see, it's a good bit longer and more complicated. It's also more extensible because all you have to do is plug in your own methods when you want your subclass to act differently than its base class.
The last bit at the end there, where it says
!caller
, checks whether the file is being used as a
module or as a program. If it's being used as a program, then there is
no caller
. So it fires up its own parser (using the
new
method it inherited) and runs that parser on
the command-line arguments. If no filenames were supplied, it assumes
standard input, just as the previous version did.
Following the module code is an
__END__
marker, a blank line without whitespace on
it, and then the program/module's own pod documentation. This is an
example of one file that's a program and a module
and its own documentation. It's probably several
other things as well.