File Coverage

blib/lib/Pod/Eventual.pm
Criterion Covered Total %
statement 52 53 98.1
branch 26 30 86.6
condition 14 16 87.5
subroutine 5 8 62.5
pod 4 4 100.0
total 101 111 90.9


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