File Coverage

blib/lib/MetaPOD/Extractor.pm
Criterion Covered Total %
statement 95 105 90.4
branch 22 34 64.7
condition 2 5 40.0
subroutine 27 28 96.4
pod 11 11 100.0
total 157 183 85.7


line stmt bran cond sub pod time code
1 3     3   26435 use 5.006; # our
  3         8  
2 3     3   10 use strict;
  3         3  
  3         52  
3 3     3   9 use warnings;
  3         3  
  3         216  
4              
5             package MetaPOD::Extractor;
6              
7             our $VERSION = 'v0.4.0';
8              
9             # ABSTRACT: Extract MetaPOD declarations from a file.
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 3     3   851 use Moo qw( extends has );
  3         22846  
  3         17  
14             extends 'Pod::Eventual';
15              
16              
17              
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28             ## no critic (Bangs::ProhibitDebuggingModule)
29 3     3   4448 use Data::Dump qw(pp);
  3         13799  
  3         269  
30 3     3   21 use Carp qw(croak);
  3         3  
  3         3516  
31              
32             has formatter_regexp => (
33             is => ro =>,
34             lazy => 1,
35             builder => sub {
36              
37             # _Pulp__5010_qr_m_propagate_properly
38             ## no critic (Compatibility::PerlMinimumVersionAndWhy)
39 5     5   45 return qr/MetaPOD::([^[:space:]]+)/sxm;
40             },
41             );
42              
43             has version_regexp => (
44             is => ro =>,
45             lazy => 1,
46             builder => sub {
47              
48             # _Pulp__5010_qr_m_propagate_properly
49             ## no critic (Compatibility::PerlMinimumVersionAndWhy)
50 5     5   42 return qr/(v[[:digit:].]+)/sxm;
51             },
52             );
53              
54             has regexp_begin_with_version => (
55             is => ro =>,
56             lazy => 1,
57             builder => sub {
58 3     3   71 my $formatter_regexp = $_[0]->formatter_regexp;
59 3         58 my $version_regexp = $_[0]->version_regexp;
60              
61             # _Pulp__5010_qr_m_propagate_properly
62             ## no critic (Compatibility::PerlMinimumVersionAndWhy)
63 3         102 return qr{ ^ ${formatter_regexp} \s+ ${version_regexp} \s* $ }smx;
64             },
65             );
66              
67             has regexp_begin => (
68             is => ro =>,
69             lazy => 1,
70             builder => sub {
71 2     2   65 my $formatter_regexp = $_[0]->formatter_regexp;
72              
73             # _Pulp__5010_qr_m_propagate_properly
74             ## no critic (Compatibility::PerlMinimumVersionAndWhy)
75 2         60 return qr{ ^ ${formatter_regexp} \s* $ }smx;
76             },
77             );
78              
79             has regexp_for_with_version => (
80             is => ro =>,
81             lazy => 1,
82             builder => sub {
83 2     2   53 my $formatter_regexp = $_[0]->formatter_regexp;
84 2         33 my $version_regexp = $_[0]->version_regexp;
85              
86             # _Pulp__5010_qr_m_propagate_properly
87             ## no critic (Compatibility::PerlMinimumVersionAndWhy)
88 2         100 return qr{ ^ ${formatter_regexp} \s+ ${version_regexp} \s+ ( .*$ ) }smx;
89             },
90             );
91              
92             has regexp_for => (
93             is => ro =>,
94             lazy => 1,
95             builder => sub {
96 1     1   27 my $formatter_regexp = $_[0]->formatter_regexp;
97              
98             # _Pulp__5010_qr_m_propagate_properly
99             ## no critic (Compatibility::PerlMinimumVersionAndWhy)
100 1         47 return qr{ ^ ${formatter_regexp} \s+ ( .* $ ) $ }smx;
101             },
102             );
103              
104             has end_segment_callback => (
105             is => ro =>,
106             lazy => 1,
107             builder => sub {
108 4     4   53 return sub { };
        4      
109             },
110             );
111              
112              
113              
114              
115              
116              
117              
118             has segment_cache => (
119             is => ro =>,
120             lazy => 1,
121             writer => 'set_segment_cache',
122 0     0   0 builder => sub { {} },
123             );
124              
125              
126              
127              
128              
129              
130              
131             has segments => (
132             is => ro =>,
133             lazy => 1,
134             writer => 'set_segments',
135 5     5   48 builder => sub { [] },
136             );
137              
138              
139              
140              
141              
142              
143              
144              
145              
146              
147              
148              
149              
150             has in_segment => (
151             is => ro =>,
152             lazy => 1,
153             writer => 'set_in_segment',
154             clearer => 'unset_in_segment',
155 5     5   46 builder => sub { undef },
156             );
157              
158              
159              
160              
161              
162              
163              
164             sub begin_segment {
165 3     3 1 7 my ( $self, $format, $version, $start_line ) = @_;
166 3 100       20 $self->set_segment_cache(
167             {
168             format => $format,
169             start_line => $start_line,
170             ( defined $version ? ( version => $version ) : () ),
171             },
172             );
173 3         15 $self->set_in_segment(1);
174 3         8 return $self;
175             }
176              
177              
178              
179              
180              
181              
182              
183             sub end_segment {
184 3     3 1 4 my ($self) = @_;
185 3         36 my $segment = $self->segment_cache;
186 3         11 push @{ $self->segments }, $segment;
  3         58  
187 3         10 $self->set_segment_cache( {} );
188 3         71 $self->unset_in_segment();
189 3         68 my $cb = $self->end_segment_callback;
190 3         13 $cb->($segment);
191 3         8 return $self;
192             }
193              
194              
195              
196              
197              
198              
199              
200             sub append_segment_data {
201 3     3 1 6 my ( $self, $segment_data ) = @_;
202 3   50     51 $self->segment_cache->{data} ||= q{};
203 3         69 $self->segment_cache->{data} .= $segment_data;
204 3         17 return $self;
205             }
206              
207              
208              
209              
210              
211              
212              
213             sub add_segment {
214 2     2 1 9 my ( $self, $format, $version, $section_data, $start_line ) = @_;
215 2         5 my $segment = {};
216 2         4 $segment->{format} = $format;
217 2 100       6 $segment->{version} = $version if defined $version;
218 2         3 $segment->{data} = $section_data;
219 2 50       6 $segment->{start_line} = $start_line if defined $start_line;
220              
221 2         3 push @{ $self->segments }, $segment;
  2         42  
222 2         63 my $cb = $self->end_segment_callback;
223 2         3 $cb->($segment);
224              
225 2         4 return $self;
226             }
227              
228              
229              
230              
231              
232              
233              
234             sub handle_begin {
235 3     3 1 5 my ( $self, $event ) = @_;
236 3 50       44 if ( $self->in_segment ) {
237 0         0 croak '=begin MetaPOD:: cannot occur inside =begin MetaPOD:: at line ' . $event->{start_line};
238             }
239 3 100       52 if ( $event->{content} =~ $self->regexp_begin_with_version ) {
240 1         5 return $self->begin_segment( $1, $2, $event->{start_line} );
241             }
242 2 50       45 if ( $event->{content} =~ $self->regexp_begin ) {
243 2         9 return $self->begin_segment( $1, undef, $event->{start_line} );
244             }
245 0         0 return $self->handle_ignored($event);
246             }
247              
248              
249              
250              
251              
252              
253              
254             sub handle_end {
255 3     3 1 5 my ( $self, $event ) = @_;
256 3         9 chomp $event->{content};
257 3         12 my $statement = q{=} . $event->{command} . q{ } . $event->{content};
258              
259 3 0 33     57 if ( not $self->in_segment and not $event->{content} =~ $self->regexp_begin ) {
260 0         0 return $self->handle_ignored($event);
261             }
262              
263 3 50       146 if ( $self->in_segment ) {
264 3         53 my $expected_end = '=end MetaPOD::' . $self->segment_cache->{format};
265 3 50       20 if ( $statement ne $expected_end ) {
266 0         0 croak "$statement seen but expected $expected_end at line " . $event->{start_line};
267             }
268 3         7 return $self->end_segment();
269             }
270 0 0       0 if ( $event->{content} =~ $self->regexp_begin ) {
271 0         0 croak "unexpected $statement without =begin MetaPOD::$1 at line" . $event->{start_line};
272             }
273 0         0 return $self->handle_ignored($event);
274             }
275              
276              
277              
278              
279              
280              
281              
282             sub handle_for {
283 2     2 1 3 my ( $self, $event ) = @_;
284 2 100       44 if ( $event->{content} =~ $self->regexp_for_with_version ) {
285 1         5 return $self->add_segment( $1, $2, $3, $event->{start_line} );
286             }
287 1 50       21 if ( $event->{content} =~ $self->regexp_for ) {
288 1         4 return $self->add_segment( $1, undef, $2, $event->{start_line} );
289             }
290 0         0 return $self->handle_ignored($event);
291             }
292              
293              
294              
295              
296              
297              
298              
299             sub handle_cut {
300 2     2 1 3 my ( $self, $element ) = @_;
301 2         4 return $self->handle_ignored($element);
302             }
303              
304              
305              
306              
307              
308              
309              
310             sub handle_text {
311 3     3 1 5 my ( $self, $element ) = @_;
312 3 50       56 return $self->handle_ignored($element) unless $self->in_segment;
313 3         25 return $self->append_segment_data( $element->{content} );
314             }
315              
316              
317              
318              
319              
320              
321              
322             sub handle_ignored {
323 4     4 1 3 my ( $self, $element ) = @_;
324 4 50       139 if ( $self->in_segment ) {
325 0         0 croak 'Unexpected type ' . $element->{type} . ' inside segment ' . pp($element) . ' at line' . $element->{start_line};
326             }
327             }
328              
329              
330              
331              
332              
333              
334              
335             sub handle_event {
336 15     15 1 8610 my ( $self, $event ) = @_;
337 15         25 for my $command (qw( begin end for cut )) {
338 28 100       56 last unless 'command' eq $event->{type};
339 23 100       101 next unless $event->{command} eq $command;
340 10         51 my $method = $self->can( 'handle_' . $command );
341 10         24 return $self->$method($event);
342             }
343 5 100       20 if ( 'text' eq $event->{type} ) {
344 3         10 return $self->handle_text($event);
345             }
346 2         6 return $self->handle_ignored($event);
347              
348             }
349              
350             1;
351              
352             __END__