Move module to lib/
[pod-constants.git] / lib / Pod / Constants.pm
CommitLineData
f46dd377
MG
1# Copyright (C) 2001, 2002, 2007 Sam Vilain. All Rights Reserved.
2# This module is free software. It may be used, redistributed and/or
3# modified under the terms of the Perl Artistic License, version 2 or
4# later, OR the terms of the GNU General Public License, v3 or later.
5
6package Pod::Constants;
7
8=head1 NAME
9
10Pod::Constants - Include constants from POD
11
12=head1 SYNOPSIS
13
14 use vars qw($myvar $VERSION @myarray $html %myhash);
15
16 use Pod::Constants -trim => 1,
17 'Pod Section Name' => \$myvar,
18 'Version' => sub { eval },
19 'Some list' => \@myarray,
20 html => \$html,
21 'Some hash' => \%myhash;
22
23 =head2 Pod Section Name
24
25 This string will be loaded into $myvar
26
27 =head2 Version
28
29 # This is an example of using a closure. $_ is set to the
30 # contents of the paragraph. In this example, "eval" is
31 # used to execute this code at run time.
32 $VERSION = 0.17;
33
34 =head2 Some list
35
36 Each line from this section of the file
37 will be placed into a seperate array element.
38 For example, this is $myarray[2].
39
40 =head2 Some hash
41
42 This text will not go into the hash, because
43 it doesn't look like a definition list.
44 key1 => Some value (this will go into the hash)
45 var2 => Some Other value (so will this)
46 wtf = This won't make it in.
47
48 =head2 %myhash's value after the above:
49
50 ( key1 => "Some value (this will go into the hash)",
51 var2 => "Some Other value (so will this)" )
52
53 =begin html <p>This text will be in $html</p>
54
55 =cut
56
57=head1 DESCRIPTION
58
59This module allows you to specify those constants that should be
60documented in your POD, and pull them out a run time in a fairly
61arbitrary fashion.
62
63Pod::Constants uses Pod::Parser to do the parsing of the source file.
64It has to open the source file it is called from, and does so directly
65either by lookup in %INC or by assuming it is $0 if the caller is
66"main" (or it can't find %INC{caller()})
67
68=head2 ARBITARY DECISIONS
69
70I have made this code only allow the "Pod Section Name" to match
71`headN', `item', `for' and `begin' POD sections. If you have a good
72reason why you think it should match other POD sections, drop me a
73line and if I'm convinced I'll put it in the standard version.
74
75For `for' and `begin' sections, only the first word is counted as
76being a part of the specifier, as opposed to `headN' and `item', where
77the entire rest of the line counts.
78
79=cut
80
81use 5.004;
82use strict;
83
84use base qw(Pod::Parser Exporter);
85use Data::Dumper;
86use Carp;
87
88use vars qw($VERSION);
89$VERSION = 0.17;
90
91# An ugly hack to go from caller() to the relevant parser state
92# variable
93my %parsers;
94
95sub end_input {
96 #my ($parser, $command, $paragraph, $line_num) = (@_);
97 my $parser = shift;
98
99 return unless $parser->{active};
100
101 print "Found end of $parser->{active}\n" if ($parser->{DEBUG});
102 my $whereto = $parser->{wanted_pod_tags}->{$parser->{active}};
103 print "\$_ will be set to:\n---\n$parser->{paragraphs}\n---\n"
104 if ($parser->{DEBUG});
105
106 $parser->{paragraphs} =~ s/^\s*|\s*$//gs
107 if $parser->{trimmed_tags}->{$parser->{active}};
108
109 if (ref $whereto eq "CODE") {
110 print "calling sub\n" if $parser->{DEBUG};
111 local ($_) = $parser->{paragraphs};
112 $whereto->();
113 print "done\n" if $parser->{DEBUG};
114 } elsif (ref $whereto eq "SCALAR") {
115 print "inserting into scalar\n" if $parser->{DEBUG};
116 $$whereto = $parser->{paragraphs};
117 } elsif (ref $whereto eq "ARRAY") {
118 print "inserting into array\n" if $parser->{DEBUG};
119 @$whereto = split /\n/, $parser->{paragraphs};
120 } elsif (ref $whereto eq "HASH") {
121 print "inserting into hash\n" if $parser->{DEBUG};
122 # Oh, sorry, should I be in LISP101?
123 %$whereto = (map { map { s/^\s*|\s*$//g; $_ }
124 split /=>/, $_ }
125 grep m/^
126 ( (?:[^=]|=[^>])+ ) # scan up to "=>"
127 =>
128 ( (?:[^=]|=[^>])+ =? )# don't allow more "=>"'s
129 $/x,
130 split /\n/, $parser->{paragraphs});
131 } else { die $whereto }
132 $parser->{active} = undef;
133}
134
135# Pod::Parser overloaded command
136sub command {
137 my ($parser, $command, $paragraph, $line_num) = @_;
138
139 $paragraph =~ s/(?:\r\n|\n\r)/\n/g;
140
141 print "Got command =$command, value=$paragraph\n"
142 if $parser->{DEBUG};
143
144 $parser->end_input() if $parser->{active};
145
146 my $does_she_want_it_sir;
147
148 my ($lookup);
149 # first check for a catch-all for this command type
150 if ( exists $parser->{wanted_pod_tags}->{"*$command"} ) {
151 $parser->{paragraphs} = $paragraph;
152 $parser->{active} = "*$command";
153 $does_she_want_it_sir = "oohw";
154
155 } elsif ($command =~ m/^(head\d+|item|(for|begin))$/) {
156 if ( $2 ) {
157 # if it's a "for" or "begin" section, the title is the
158 # first word only
159 ($lookup, $parser->{paragraphs}) =
160 ($paragraph =~ m/^\s*(\S*)\s*(.*)/s);
161 } else {
162 # otherwise, it's up to the end of the line
163 ($lookup, $parser->{paragraphs})
164 = ($paragraph =~ m/^\s*(\S[^\n]*?)\s*\n(.*)$/s);
165 }
166
167 # Look for a match by name
168 if (defined $lookup
169 and exists $parser->{wanted_pod_tags}->{$lookup}) {
170 print "Found $lookup\n" if ($parser->{DEBUG});
171 $parser->{active} = $lookup;
172 $does_she_want_it_sir = "suits you sir";
173 }
174
175 } else {
176 # nothing
177 print "Ignoring =$command (not known)\n" if $parser->{DEBUG};
178 }
179
180 {
181 local $^W = 0;
182 print "Ignoring =$command $paragraph (lookup = $lookup)\n"
183 if (!$does_she_want_it_sir and $parser->{DEBUG})
184 }
185}
186
187# Pod::Parser overloaded verbatim
188sub verbatim {
189 my ($parser, $paragraph, $line_num) = @_;
190 $paragraph =~ s/(?:\r\n|\n\r)/\n/g;
191
192 print("Got paragraph: $paragraph ("
193 .($parser->{active}?"using":"ignoring").")\n")
194 if $parser->{DEBUG};
195
196 if (defined $parser->{active}) {
197 $parser->{paragraphs} .= $paragraph;
198 }
199}
200
201# Pod::Parser overloaded textblock
202sub textblock { goto \&verbatim }
203
204=head1 FUNCTIONS
205
206=head2 import(@args)
207
208This function is called when we are "use"'d. It determines the source
209file by inspecting the value of caller() or $0.
210
211The form of @args is HOOK => $where.
212
213$where may be a scalar reference, in which case the contents of the
214POD section called "HOOK" will be loaded into $where.
215
216$where may be an array reference, in which case the contents of the
217array will be the contents of the POD section called "HOOK", split
218into lines.
219
220$where may be a hash reference, in which case any lines with a "=>"
221symbol present will have everything on the left have side of the =>
222operator as keys and everything on the right as values. You do not
223need to quote either, nor have trailing commas at the end of the
224lines.
225
226$where may be a code reference (sub { }), in which case the sub is
227called when the hook is encountered. $_ is set to the value of the
228POD paragraph.
229
230You may also specify the behaviour of whitespace trimming; by default,
231no trimming is done except on the HOOK names. Setting "-trim => 1"
232turns on a package "global" (until the next time import is called)
233that will trim the $_ sent for processing by the hook processing
234function (be it a given function, or the built-in array/hash
235splitters) for leading and trailing whitespace.
236
237The name of HOOK is matched against any "=head1", "=head2", "=item",
238"=for", "=begin" value. If you specify the special hooknames "*item",
239"*head1", etc, then you will get a function that is run for every
240
241Note that the supplied functions for array and hash splitting are
242exactly equivalent to fairly simple Perl blocks:
243
244Array:
245
246 HOOK => sub { @array = split /\n/, $_ }
247
248Hash:
249
250 HOOK => sub {
251 %hash =
252 (map { map { s/^\s+|\s+$//g; $_ } split /=>/, $_ }
253 (grep m/^
254 ( (?:[^=]|=[^>])+ ) # scan up to "=>"
255 =>
256 ( (?:[^=]|=[^>])+ =? )# don't allow more "=>"'s
257 $/x, split /\n/, $_));
258 }
259
260Well, they're simple if you can grok map, a regular expression like
261that and a functional programming style. If you can't I'm sure it is
262probably voodoo to you.
263
264Here's the procedural equivalent:
265
266 HOOK => sub {
267 for my $line (split /\n/, $_) {
268 my ($key, $value, $junk) = split /=>/, $line;
269 next if $junk;
270 $key =~ s/^\s+|\s+$//g
271 $value =~ s/^\s+|\s+$//g
272 $hash{$key} = $value;
273 }
274 },
275
276=cut
277
278sub import {
279 my $class = shift;
280
281 # if no args, just return
282 return unless (@_);
283
284 # try to guess the source file of the caller
285 my $source_file;
286 if (caller ne "main") {
287 (my $module = caller().".pm") =~ s|::|/|g;
288 $source_file = $INC{$module};
289 }
290 $source_file ||= $0;
291
292 ( -f $source_file )
293 or croak ("Cannot find source file (guessed $source_file) for"
294 ." package ".caller());
295
296 # nasty tricks with the stack so we don't have to be silly with
297 # caller()
298 unshift @_, $source_file;
299 goto \&import_from_file;
300}
301
302=head2 import_from_file($filename, @args)
303
304Very similar to straight "import", but you specify the source filename
305explicitly.
306
307=cut
308
309use IO::Handle;
310
311sub import_from_file {
312 my $filename = shift;
313
314 my $parser = __PACKAGE__->new();
315
316 $parser->{wanted_pod_tags} = {};
317 $parser->{trimmed_tags} = {};
318 $parser->{trim_next} = 0;
319 $parser->{DEBUG} = 0;
320 $parser->{active} = undef;
321 $parsers{caller()} = $parser;
322
323 $parser->add_hook(@_);
324
325 print "Pod::Parser: DEBUG: Opening $filename for reading\n"
326 if $parser->{DEBUG};
327 my $fh = new IO::Handle;
328 open $fh, "<$filename"
329 or die ("cannot open $filename for reading; $!");
330
331 $parser->parse_from_filehandle($fh, \*STDOUT);
332
333 close $fh;
334}
335
336=head2 add_hook(NAME => value)
337
338This function adds another hook, it is useful for dynamic updating of
339parsing through the document.
340
341For an example, please see t/01-constants.t in the source
342distribution. More detailed examples will be added in a later
343release.
344
345=cut
346
347sub add_hook {
348 my $parser;
349 if ( UNIVERSAL::isa($_[0], __PACKAGE__) ) {
350 $parser = shift;
351 } else {
352 $parser = $parsers{caller()}
353 or die("add_hook called, but don't know what for - "
354 ."caller = ".caller());
355 }
356 while (my ($pod_tag, $var) = splice @_, 0, 2) {
357 #print "$pod_tag: $var\n";
358 if (lc($pod_tag) eq "-trim") {
359 $parser->{trim_next} = $var;
360 } elsif ( lc($pod_tag) eq "-debug" ) {
361 $parser->{DEBUG} = $var;
362 } elsif (lc($pod_tag) eq "-usage") {
363 # an idea for later - automatic "usage"
364 #%wanted_pod_tags{@tags}
365 } else {
366 if ((ref $var) =~ /^(?:SCALAR|CODE|ARRAY|HASH)$/) {
367 print "Will look for $pod_tag.\n"
368 if ($parser->{DEBUG});
369 $parser->{wanted_pod_tags}->{$pod_tag} = $var;
370 $parser->{trimmed_tags}->{$pod_tag} = 1
371 if $parser->{trim_next};
372 } else {
373 die ("Sorry - need a reference to import POD "
374 ."sections into, not the scalar value $var"
375 ." importing $pod_tag into ".caller());
376 }
377 }
378 }
379}
380
381=head2 delete_hook(@list)
382
383Deletes the named hooks. Companion function to add_hook
384
385=cut
386
387sub delete_hook {
388 my $parser;
389 if ( UNIVERSAL::isa($_[0], __PACKAGE__) ) {
390 $parser = shift;
391 } else {
392 $parser = $parsers{caller()}
393 or die("delete_hook called, but don't know what for - "
394 ."caller = ".caller());
395 }
396 while ( my $label = shift ) {
397 delete $parser->{wanted_pod_tags}->{$label};
398 delete $parser->{trimmed_tags}->{$label};
399 }
400}
401
402=head2 CLOSURES AS DESTINATIONS
403
404If the given value is a ref CODE, then that function is called, with
405$_ set to the value of the paragraph. This can be very useful for
406applying your own custom mutations to the POD to change it from human
407readable text into something your program can use.
408
409After I added this function, I just kept on thinking of cool uses for
410it. The nice, succinct code you can make with it is one of
411Pod::Constant's strongest features.
412
413Below are some examples.
414
415=head1 EXAMPLES
416
417=head2 Module Makefile.PL maintenance
418
419Tired of keeping those module Makefile.PL's up to date? Note: This
420method seems to break dh-make-perl.
421
422=head2 Example Makefile.PL
423
424 eval "use Pod::Constants";
425 ($Pod::Constants::VERSION >= 0.11)
426 or die <<EOF
427 ####
428 #### ERROR: This module requires Pod::Constants 0.11 or
429 #### higher to be installed.
430 ####
431 EOF
432
433 my ($VERSION, $NAME, $PREREQ_PM, $ABSTRACT, $AUTHOR);
434 Pod::Constants::import_from_file
435 (
436 'MyTestModule.pm',
437 'MODULE RELEASE' => sub { ($VERSION) = m/(\d+\.\d+)/ },
438 'DEPENDANCIES' => ($PREREQ_PM = { }),
439 -trim => 1,
440 'NAME' => sub { $ABSTRACT=$_; ($NAME) = m/(\S+)/ },
441 'AUTHOR' => \$AUTHOR,
442 );
443
444 WriteMakefile
445 (
446 'NAME' => $NAME,
447 'PREREQ_PM' => $PREREQ_PM,
448 'VERSION' => $VERSION,
449 ($] >= 5.005 ? ## Add these new keywords supported since 5.005
450 (ABSTRACT => $ABSTRACT,
451 AUTHOR => $AUTHOR) : ()),
452 );
453
454=head2 Corresponding Module
455
456 =head1 NAME
457
458 MyTestModule - Demonstrate Pod::Constant's Makefile.PL usefulness
459
460 =head2 MODULE RELEASE
461
462 This is release 1.05 of this module.
463
464 =head2 DEPENDANCIES
465
466 The following modules are required to make this module:
467
468 Some::Module => 0.02
469
470 =head2 AUTHOR
471
472 Ima Twat <ima@twat.name>
473
474 =cut
475
476 use vars qw($VERSION);
477 use Pod::Constants -trim => 1,
478 'MODULE RELEASE' => sub { ($VERSION) = m/(\d+\.\d+) or die };
479
480=head1 AUTHOR
481
482Sam Vilain, <samv@cpan.org>
483
484=head1 BUGS/TODO
485
486I keep thinking it would be nice to be able to import an =item list
487into an array or something, eg for a program argument list. But I'm
488not too sure how it would be all that useful in practice; you'd end up
489putting the function names for callbacks in the pod or something
490(perhaps not all that bad).
491
492Would this be useful?
493
494 Pod::Constants::import(Foo::SECTION => \$myvar);
495
496Debug output is not very readable
497
498=head1 PATCHES WELCOME
499
500If you have any suggestions for enhancements, they are much more likely
501to happen if you submit them as a patch to the distribution.
502
503Source is kept at
504
505 git://utsl.gen.nz/Pod-Constants
506
507=cut
508
509BEGIN {
510 Pod::Constants->import
511 (
512 SYNOPSIS => sub {
513 eval pop @{[ grep /^\s*\$VERSION/, split /\n/, $_ ]}
514 }
515 )
516};
517
5181.4142;
This page took 0.03705 seconds and 4 git commands to generate.