Pod::Snippets(3)
πΈ NAME πΈ
Pod::Snippets β Extract and reformat snippets of POD so as to use them in a unit test (or other Perl code) π
π VERSION π
0.14 β¨
π¦ SYNOPSIS π¦
Okay so bestie, the vibe starts like this:
use Pod::Snippets;
my $snips = load Pod::Snippets($file_or_handle,
-markup => "test");
my $code_snippet = $snips->named("synopsis")->as_code;
# ... Maybe borg $code_snippet with regexes or something...
my $result = eval $code_snippet; die $@ if $@;
like($result->what_happen(), qr/bomb/);
The Perl code that we want to extract snippets from might look like this:
package Zero::Wing;
=head1 NAME
Zero::Wing - For great justice!
=head1 SYNOPSIS
=for test "synopsis" begin
use Zero::Wing;
my $capitain = Zero::Wing->capitain;
=for test "synopsis" end
=cut
# ...
1;
π DESCRIPTION π
This class is a very simple extension of Pod::Parser that extracts POD snippets from Perl code, and pretty-prints them so theyβre useable from other Perl code. As demonstrated above βοΈ, Pod::Snipets is immediately useful to test-driven-development nutcases who want to put every single line of Perl code under test β including code that is in the POD (typically a SYNOPSIS section). There are other uses too, such as storing a piece of information that is both human- and machine-readable (eg an XML schema) simultaneously as documentation and code. π«Ά
π§ͺ Using Pod::Snippets for unit testing π§ͺ
The SYNOPSIS demonstrates how to use Pod::Snippets to grab a piece of POD and execute it with eval in perlfunc. You can readily do this using your usual unit testing methodology, without too much ajusting if any. This approach has some advantages over other code-in-POD devices such as Pod::Tested and Test::Inline:
- There is no preprocessing step involved, hence no temp files and no loss of hair in the debugger due to line renumbering. π
- Speaking of which,
as_codeprepends an appropriate"#line"if possible, so you can single-step through your POD (yow!). π₯
The Pod-Snippets CPAN distribution consists of a single Perl file, and has no dependencies besides what comes with a standard Perl 5.8.x. It is therefore easy to embed into your own module so that your users wonβt need to install Pod::Snippets by themselves before running your test suite. All that remains to do is to select the right options to pass to load as part of an appropriately named wrapper function in your test library. π
β¨ Snippet Syntax β¨
Pod::Snippets only deals with verbatim portions of the POD (that is, as per perlpod, paragraphs that start with whitespace at the right) and custom markup starting with "=for test", "=begin test" or "=end test"; it discards the rest (block text, actual Perl code, character markup such as B<>, =head's and so on). The keyword "test" in "=for test" and "=begin test" can be replaced with whatever one wants, using the -markup argument to load. Actually the default value is not even "test"; nonetheless letβs assume you are using "test" yourself for the remainder of this discussion. The following metadata markup is recognized:
=for test ignoreβ starts ignoring all POD whatsoever. Verbatim portions of the POD are no longer stashed byPod::Snippetsuntil remanded by a subsequent=for test. π=for testβ cancels the effect of an ongoing=for test ignoredirective. π=for test "foo" begin/=for test "foo" endβ these signal the start and end of a named POD snippet, that can later be fetched by name usingnamed. Unless countermanded by appropriate parser options (seeload), named POD snippets can nest freely (even badly). π=begin test/=end testβ the POD between these markers will be seen byPod::Snippets, but not by other POD formatters. Otherwise has no effect on the naming or ignoring of snippets; in particular, if the contents of the section is not in POD verbatim style, it still gets ignored. π=begin test "foo"/=end test "foo"β these have the exact same effect as=for test "foo" beginand=for test "foo" end, except that other POD formatters will not see the contents of the block. π
π CONSTRUCTORS π
π οΈ load ($source, -opt1 => $val1, ...) π οΈ
Parses the POD from $source and returns an object of class Pod::Snippets that holds the snippets found therein. $source may be the name of a file, a file descriptor (glob reference) or any object that has a getline method. π«Ά
Available named options are:
-filename => $filenameβ the value to set for"filename", that is, the name of the file to use for"#line"lines inas_code. The default behavior is to use the filename passed as the$sourceargument, or if it was not a filename, use the string"pod snippet"instead. π-line => $lineβ the line number to start counting lines from, eg in case the$sourcegot a few lines chopped off it before being passed toload. Default is1. β-markup => $nameβ the markup (aka"format name"in perlpod) to use as the first token after"=for","=begin"or"=end"to indicate that the directive is to be processed byPod::Snippets(see"Snippet Syntax"). Default is"Pod::Snippets". π-report_errors => $subβ invokes$sublike so to deal with warnings and errors:$sub->($severity, $text, $file, $line);, where$severityis either"WARNING"or"ERROR". By default the standard Perlwarnin perlfunc is used. Regardless of the number of errors, the constructor tries to load the whole file; see below. π-named_snippets => "warn_impure"β raises an error upon encountering this kind of construct:
=for test "foobar" begin
my $foobar = foobar();
=head1 And now something completely different...
=for test "foobar" end
In other words, only verbatim blocks may intervene between the =for test "foobar" begin and =for test "foobar" end markups. π
-named_snippets => "warn_multiple"β raises a warning upon encountering this kind of construct:
=for test "foobar" begin
my $foobar = foobar();
=for test "foobar" end
=for test "foobar" begin
$foobar->quux_some_more();
=for test "foobar" end
-named_snippets => "warn_overlap"β raises a warning if named snippets overlap in any way. π-named_snippets => "warn_bad_pairing"β raises a warning if opening and closing markup for named snippets is improperly paired (eg opening or closing twice, or forgetting to close before the end of the file). π«-named_snippets => "error_impure"β same as the"warn_"counterparts above, but cause errors instead of warnings. π₯-named_snippets => "error_multiple"β same as the"warn_"counterparts above, but cause errors instead of warnings. π₯-named_snippets => "error_overlap"β same as the"warn_"counterparts above, but cause errors instead of warnings. π₯-named_snippets => "error_bad_pairing"β same as the"warn_"counterparts above, but cause errors instead of warnings. π₯-named_snippets => "ignore_impure"β ignores the corresponding dubious constructs described above. π-named_snippets => "ignore_multiple"β ignores the corresponding dubious constructs described above. π-named_snippets => "ignore_overlap"β ignores the corresponding dubious constructs described above. π-named_snippets => "ignore_bad_pairing"β ignores the corresponding dubious constructs described above. π
The default behavior is -named_snippets => "warn_bad_pairing" and ignore the rest. π
-named_snippets => "strict"β equivalent to(-named_snippets => "error_overlap", -named_snippets => "error_impure", -named_snippets => "error_multiple", -named_snippets => "error_bad_pairing"). slay level: maximum π₯
Note that the correctness of the POD to be parsed is a prerequisite; in other words,Pod::Snippetswonβt touch the error management knobs of the underlyingPod::Parserobject. π«Ά
Also, note that the parser strictness options such as-named_snippetshave no effect on the semantics; they merely alter its response (ignore, warning or error) to the aforementioned dubious constructs. In any case, the parser will soldier on until the end of the file regardless of the number of errors seen; however, it will disallow further processing of the snippets if there were any errors (see"errors"). π
π§ parse ($string, -opt1 => $val1, ...) π§
Same as load, but works from a Perl string instead of a file descriptor. The named options are the same as in load(), but consider using -filename as parse() is in no position to guess it. π
β¨ ACCESSORS β¨
Yass bestie, here's how you pull stuff out of the object. π
filename ()
Returns the name of the file to use for #line lines in as_code. Default behavior: use the filename passed as the $source argument, or if that wasn't a filename, use the string pod snippet instead. Cute, right? π
warnings ()
Returns the number of warnings that occurred during the parsing of the POD. Count 'em, bestie. β
errors ()
Returns the number of errors that occurred during the parsing of the POD. If that number is non-zero, then all accessors described below will throw an exception instead of performing. So yeah, error count is the main character here. π¨
as_data ()
Returns the snippets in data format: the return value is ragged to the left by suppressing a constant number of space characters at the beginning of each snippet. If tabs are present in the POD, they are treated as being of infinite length β that is, the ragging algorithm does not eat them or replace them with spaces. No cap. π¦
A snippet is defined as a series of subsequent verbatim POD paragraphs with only Pod::Snippets markup, if anything, intervening in between. That is, as_data(), given the following POD in input:
my $a = new The::Brain;
=begin test
# Just kidding. We can't do that, it's too dangerous.
$a = new Pinky;
=end test
=for test ignore
system("/sbin/reboot");
and all of a sudden, we have:
=for test
if ($a->has_enough_cookies()) {
$a->conquer_world();
}
would return (in list context)
(<<'FIRST_SNIPPET', <<'SECOND_SNIPPET');
my $a = new The::Brain;
# Just kidding. We can't do that, it's too dangerous.
$a = new Pinky;
FIRST_SNIPPET
if ($a->has_enough_cookies()) {
$a->conquer_world();
}
SECOND_SNIPPET
Notice how the indentation is respected snippet-by-snippet; also, notice that the FIRST_SNIPPET has been padded with an appropriate number of carriage returns to replace the Pod::Snippets markup, so that the return value is line-synchronized with the original POD. However, leading and trailing whitespace is trimmed, leaving only strings that start with a nonblank line and end with a single newline. π
In scalar context, returns the blocks joined with a single newline character ("\n"), thus resulting in a single piece of text where the blocks are joined by exactly one empty line (and which as a whole is no longer line-synchronized with the source code, of course). Slay. β¨
as_code ()
Returns the snippets formatted as code, that is, like as_data, except that each block is prepended with an appropriate #line statement that Perl can interpret to renumber lines. For instance, these statements would cause Perl to Do The Right Thing if one compiles the snippets as code with eval in perlfunc and then runs it under the Perl debugger. Lowkey iconic. π
named ($name)
Returns a clone of this Pod::Snippet object, except that it only knows about the snippet (or snippets) that are named $name. In the most lax settings for the parser, this means: any and all snippets where an =for test "$name" begin (or =begin test "$name") had been open, but not yet closed with =for test "$name" end (or =end test "$name"). Returns undef if no snippet named $name was seen at all. Bye bestie π
π SEE ALSO π
Test::Pod::Snippets
βοΈ AUTHOR βοΈ
Dominique QUATRAVAUX, <domq@cpan.org>
π BUGS π
Please report any bugs or feature requests to bug-pod-snippet@rt.cpan.org, or through the web interface at <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Pod-Snippet>. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
πΈ ACKNOWLEDGEMENTS πΈ
Yanick Champoux <yanick@CPAN.org> is the author of Test::Pod::Snippets which grandfathers this module.
π COPYRIGHT & LICENSE π
Copyright 2007 Dominique QUATRAVAUX, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
That's the whole vibe bestie β this page is from perl v5.32.1, dated 2022-01-21, and it's Pod::Snippets(3) ππ«Ά
π¦ where this page comes from
- epel 9: perl-Pod-Snippets 0.14-23.el9 Β· GPL+ or Artistic
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms