File Coverage

blib/lib/Pod/Eventual.pm
Criterion Covered Total %
statement 52 55 94.5
branch 26 30 86.6
condition 14 16 87.5
subroutine 5 8 62.5
pod 4 4 100.0
total 101 113 89.3


line stmt bran cond sub pod time code
1 4     4   21 use strict;
  4         7  
  4         259  
2 4     4   19 use warnings;
  4         8  
  4         198  
3             package Pod::Eventual;
4             {
5             $Pod::Eventual::VERSION = '0.094001';
6             }
7             # ABSTRACT: read a POD document as a series of trivial events
8 4     4   4303 use Mixin::Linewise::Readers 0.102;
  4         176545  
  4         33  
9              
10 4     4   1696 use Carp ();
  4         11  
  4         2657  
11              
12              
13             sub read_handle {
14 4     4 1 11 my ($self, $handle, $arg) = @_;
15 4   50     46 $arg ||= {};
16              
17 4 50       15 my $in_pod = $arg->{in_pod} ? 1 : 0;
18 4         7 my $current;
19              
20 4         145 LINE: while (my $line = $handle->getline) {
21 78 100 100     6608 if ($in_pod and $line =~ /^=cut(?:\s*)(.*?)(\n)\z/) {
22 4         14 my $content = "$1$2";
23 4         9 $in_pod = 0;
24 4 50       25 $self->handle_event($current) if $current;
25 4         6 undef $current;
26 4         22 $self->handle_event({
27             type => 'command',
28             command => 'cut',
29             content => $content,
30             start_line => $handle->input_line_number,
31             });
32 4         111 next LINE;
33             }
34              
35 74 100       208 if ($line =~ /\A=[a-z]/i) {
36 8 100 100     53 if ($current and not $in_pod) {
37 4         26 $self->handle_nonpod($current);
38 4         8 undef $current;
39             }
40              
41 8         15 $in_pod = 1;
42             }
43              
44 74 100       161 if (not $in_pod) {
45 17   100     105 $current ||= {
46             type => 'nonpod',
47             start_line => $handle->input_line_number,
48             content => '',
49             };
50              
51 17         229 $current->{content} .= $line;
52 17         586 next LINE;
53             }
54              
55 57 100 100     329 if ($line =~ /^\s*$/) {
    100          
56 25 100 66     183 if ($current and $current->{type} ne 'blank') {
57 23         78 $self->handle_event($current);
58              
59 23         69 $current = {
60             type => 'blank',
61             content => '',
62             start_line => $handle->input_line_number,
63             };
64             }
65             } elsif ($current and $current->{type} eq 'blank') {
66 20         81 $self->handle_blank($current);
67 20         27 undef $current;
68             }
69              
70 57 100       480 if ($current) {
71 32         83 $current->{content} .= $line;
72 32         1106 next LINE;
73             }
74              
75 25 100       168 if ($line =~ /^=([a-z]+\S*)(?:\s*)(.*?)(\n)\z/i) {
76 8         20 my $command = $1;
77 8         138 my $content = "$2$3";
78 8         46 $current = {
79             type => 'command',
80             command => $command,
81             content => $content,
82             start_line => $handle->input_line_number,
83             };
84 8         374 next LINE;
85             }
86              
87             $current = {
88 17         59 type => 'text',
89             content => $line,
90             start_line => $handle->input_line_number,
91             };
92             }
93              
94 4 100       204 if ($current) {
95 2 50       17 my $method = $current->{type} eq 'blank' ? 'handle_blank'
    100          
96             : $current->{type} eq 'nonpod' ? 'handle_nonpod'
97             : 'handle_event';
98              
99 2 50       20 $self->$method($current) if $current;
100             }
101              
102 4         17 return;
103             }
104              
105              
106             sub handle_event {
107 0     0 1   Carp::confess("handle_event not implemented by $_[0]");
108             }
109              
110              
111 0     0 1   sub handle_nonpod { }
112              
113              
114 0     0 1   sub handle_blank { }
115              
116             1;
117              
118             __END__
119              
120             =pod
121              
122             =head1 NAME
123              
124             Pod::Eventual - read a POD document as a series of trivial events
125              
126             =head1 VERSION
127              
128             version 0.094001
129              
130             =head1 SYNOPSIS
131              
132             package Your::Pod::Parser;
133             use base 'Pod::Eventual';
134              
135             sub handle_event {
136             my ($self, $event) = @_;
137              
138             print Dumper($event);
139             }
140              
141             =head1 DESCRIPTION
142              
143             POD is a pretty simple format to write, but it can be a big pain to deal with
144             reading it and doing anything useful with it. Most existing POD parsers care
145             about semantics, like whether a C<=item> occurred after an C<=over> but before
146             a C<back>, figuring out how to link a C<< LE<lt>E<gt> >>, and other things like
147             that.
148              
149             Pod::Eventual is much less ambitious and much more stupid. Fortunately, stupid
150             is often better. (That's what I keep telling myself, anyway.)
151              
152             Pod::Eventual reads line-based input and produces events describing each POD
153             paragraph or directive it finds. Once complete events are immediately passed
154             to the C<handle_event> method. This method should be implemented by
155             Pod::Eventual subclasses. If it isn't, Pod::Eventual's own C<handle_event>
156             will be called, and will raise an exception.
157              
158             =head1 METHODS
159              
160             =head2 read_handle
161              
162             Pod::Eventual->read_handle($io_handle, \%arg);
163              
164             This method iterates through the lines of a handle, producing events and
165             calling the C<handle_event> method.
166              
167             The only valid argument in C<%arg> (for now) is C<in_pod>, which indicates
168             whether we should assume that we are parsing pod when we start parsing the
169             file. By default, this is false.
170              
171             This is useful to behave differently when reading a F<.pm> or F<.pod> file.
172              
173             B<Important:> the handle is expected to have an encoding layer so that it will
174             return text, not bytes, on reads.
175              
176             =head2 read_file
177              
178             This behaves just like C<read_handle>, but expects a filename rather than a
179             handle. The file will be assumed to be UTF-8 encoded.
180              
181             =head2 read_string
182              
183             This behaves just like C<read_handle>, but expects a string containing POD
184             text rather than a handle.
185              
186             =head2 handle_event
187              
188             This method is called each time Pod::Evental finishes scanning for a new POD
189             event. It must be implemented by a subclass or it will raise an exception.
190              
191             =head2 handle_nonpod
192              
193             This method is called each time a non-POD segment is seen -- that is, lines
194             after C<=cut> and before another command.
195              
196             If unimplemented by a subclass, it does nothing by default.
197              
198             =head2 handle_blank
199              
200             This method is called at the end of a sequence of one or more blank lines.
201              
202             If unimplemented by a subclass, it does nothing by default.
203              
204             =head1 EVENTS
205              
206             There are four kinds of events that Pod::Eventual will produce. All are
207             represented as hash references.
208              
209             =head2 Command Events
210              
211             These events represent commands -- those things that start with an equals sign
212             in the first column. Here are some examples of POD and the event that would be
213             produced.
214              
215             A simple header:
216              
217             =head1 NAME
218              
219             { type => 'command', command => 'head1', content => "NAME\n", start_line => 4 }
220              
221             Notice that the content includes the trailing newline. That's to maintain
222             similarity with this possibly-surprising case:
223              
224             =for HTML
225             We're actually still in the command event, here.
226              
227             {
228             type => 'command',
229             command => 'for',
230             content => "HTML\nWe're actually still in the command event, here.\n",
231             start_line => 8,
232             }
233              
234             Pod::Eventual does not care what the command is. It doesn't keep track of what
235             it's seen or whether you've used a command that isn't defined. The only
236             special case is C<=cut>, which is never more than one line.
237              
238             =cut
239             We are no longer parsing POD when this line is read.
240              
241             {
242             type => 'command',
243             command => 'cut',
244             content => "\n",
245             start_line => 15,
246             }
247              
248             Waiving this special case may be an option in the future.
249              
250             =head2 Text Events
251              
252             A text event is just a paragraph of text, beginning after one or more empty
253             lines and running until the next empty line (or F<=cut>). In Perl 5's standard
254             usage of Pod, text content that begins with whitespace is a "verbatim"
255             paragraph, and text content that begins with non-whitespace is an "ordinary"
256             paragraph.
257              
258             Pod::Eventual doesn't care.
259              
260             Text events look like this:
261              
262             {
263             type => 'text',
264             content => "a string of text ending with a\n",
265             start_line => 16,
266             }
267              
268             =head2 Blank events
269              
270             These events represent blank lines (or many blank lines) within a Pod section.
271              
272             Blank events look like this:
273              
274             {
275             type => 'blank',
276             content => "\n\n\n\n",
277             start_line => 21,
278             }
279              
280             =head2 Non-Pod events
281              
282             These events represent non-Pod segments of the input.
283              
284             Non-Pod events look like this:
285              
286             {
287             type => 'nonpod',
288             content => "#!/usr/bin/perl\nuse strict;\n\nuse Acme::ProgressBar\n\n",
289             start_line => 1,
290             }
291              
292             =head1 AUTHOR
293              
294             Ricardo SIGNES <rjbs@cpan.org>
295              
296             =head1 COPYRIGHT AND LICENSE
297              
298             This software is copyright (c) 2013 by Ricardo SIGNES.
299              
300             This is free software; you can redistribute it and/or modify it under
301             the same terms as the Perl 5 programming language system itself.
302              
303             =cut