File Coverage

blib/lib/Test/Auto/Parser.pm
Criterion Covered Total %
statement 362 409 88.5
branch 104 178 58.4
condition 7 24 29.1
subroutine 62 62 100.0
pod 0 58 0.0
total 535 731 73.1


line stmt bran cond sub pod time code
1             package Test::Auto::Parser;
2              
3 6     6   46 use strict;
  6         162  
  6         201  
4 6     6   59 use warnings;
  6         15  
  6         163  
5              
6 6     6   30 use Moo;
  6         28  
  6         29  
7 6     6   2163 use Test::Auto::Types ();
  6         105  
  6         32493  
8              
9             require Carp;
10              
11             our $VERSION = '0.12'; # VERSION
12              
13             # ATTRIBUTES
14              
15             has name => (
16             is => 'ro',
17             isa => Test::Auto::Types::Strings(),
18             required => 0
19             );
20              
21             has tagline => (
22             is => 'ro',
23             isa => Test::Auto::Types::Strings(),
24             required => 0
25             );
26              
27             has abstract => (
28             is => 'ro',
29             isa => Test::Auto::Types::Strings(),
30             required => 0
31             );
32              
33             has synopsis => (
34             is => 'ro',
35             isa => Test::Auto::Types::Strings(),
36             required => 0
37             );
38              
39             has includes => (
40             is => 'ro',
41             isa => Test::Auto::Types::Strings(),
42             required => 0
43             );
44              
45             has description => (
46             is => 'ro',
47             isa => Test::Auto::Types::Strings(),
48             required => 0
49             );
50              
51             has inherits => (
52             is => 'ro',
53             isa => Test::Auto::Types::Strings(),
54             required => 0
55             );
56              
57             has integrates => (
58             is => 'ro',
59             isa => Test::Auto::Types::Strings(),
60             required => 0
61             );
62              
63             has attributes => (
64             is => 'ro',
65             isa => Test::Auto::Types::Strings(),
66             required => 0
67             );
68              
69             has libraries => (
70             is => 'ro',
71             isa => Test::Auto::Types::Strings(),
72             required => 0
73             );
74              
75             has headers => (
76             is => 'ro',
77             isa => Test::Auto::Types::Strings(),
78             required => 0
79             );
80              
81             has footers => (
82             is => 'ro',
83             isa => Test::Auto::Types::Strings(),
84             required => 0
85             );
86              
87             has source => (
88             is => 'ro',
89             isa => Test::Auto::Types::Source(),
90             required => 1
91             );
92              
93             # BUILD
94              
95             sub BUILD {
96 39     39 0 44523 my ($self, $args) = @_;
97              
98 39 50       141 $self->{'$stash'} = {} if !$self->{'$stash'};
99              
100 39         147 my $source = $self->source->data;
101              
102 39         130 $self->build_name;
103 39         114 $self->build_tagline;
104 39         156 $self->build_abstract;
105 39         137 $self->build_synopsis;
106 39         119 $self->build_includes;
107 39         134 $self->build_description;
108 39         132 $self->build_inherits;
109 39         123 $self->build_integrates;
110 39         113 $self->build_attributes;
111 39         107 $self->build_libraries;
112 39         114 $self->build_headers;
113 39         155 $self->build_footers;
114 39         109 $self->build_scenarios;
115 39         110 $self->build_methods;
116 39         127 $self->build_functions;
117 39         113 $self->build_routines;
118 39         111 $self->build_types;
119              
120 39         1032 return $self;
121             }
122              
123             # METHODS
124              
125             sub build_name {
126 39     39 0 76 my ($self) = @_;
127              
128 39         103 $self->parse_name;
129 39 50       96 $self->check_name or Carp::confess 'build name failed';
130              
131 39         80 return $self->name;
132             }
133              
134             sub parse_name {
135 39     39 0 77 my ($self) = @_;
136              
137 39         83 my $source = $self->source->data;
138              
139 39         138 return $self->{name} = $source->content('name');
140             }
141              
142             sub check_name {
143 39     39 0 73 my ($self) = @_;
144              
145 39         87 my $name = $self->name;
146              
147 39 50       112 return 1 if !$name;
148              
149 39 50       166 return 0 if $name->[0] =~ /[^\w\'\:']/;
150              
151 39         106 return 1;
152             }
153              
154             sub build_tagline {
155 39     39 0 114 my ($self) = @_;
156              
157 39         116 $self->parse_tagline;
158              
159 39         123 return $self->tagline;
160             }
161              
162             sub parse_tagline {
163 39     39 0 68 my ($self) = @_;
164              
165 39         99 my $source = $self->source->data;
166              
167 39         105 return $self->{tagline} = $source->content('tagline');
168             }
169              
170             sub build_abstract {
171 39     39 0 88 my ($self) = @_;
172              
173 39         119 $self->parse_abstract;
174 39 50       121 $self->check_abstract or Carp::confess 'build abstract failed';
175              
176 39         86 return $self->abstract;
177             }
178              
179             sub parse_abstract {
180 39     39 0 75 my ($self) = @_;
181              
182 39         93 my $source = $self->source->data;
183              
184 39         92 return $self->{abstract} = $source->content('abstract');
185             }
186              
187             sub check_abstract {
188 39     39 0 81 my ($self) = @_;
189              
190 39         106 my $abstract = $self->abstract;
191              
192 39 50       116 return 1 if !$abstract;
193              
194 39 50       177 return 0 if $abstract->[0] !~ /\w/;
195              
196 39         94 return 1;
197             }
198              
199             sub build_synopsis {
200 39     39 0 77 my ($self) = @_;
201              
202 39         148 $self->parse_synopsis;
203 39 50       220 $self->check_synopsis or Carp::confess 'build synopsis failed';
204              
205 39         82 return $self->synopsis;
206             }
207              
208             sub parse_synopsis {
209 39     39 0 79 my ($self) = @_;
210              
211 39         99 my $source = $self->source->data;
212              
213 39         115 return $self->{synopsis} = $source->content('synopsis');
214             }
215              
216             sub check_synopsis {
217 39     39 0 101 my ($self) = @_;
218              
219 39         156 my $synopsis = $self->synopsis;
220              
221 39 50       103 return 1 if !$synopsis;
222              
223 39 50       149 return 0 if $synopsis->[0] !~ /\w/;
224              
225 39         96 return 1;
226             }
227              
228             sub build_includes {
229 39     39 0 71 my ($self) = @_;
230              
231 39         124 $self->parse_includes;
232 39 50       113 $self->check_includes or Carp::confess 'build includes failed';
233              
234 39         100 return $self->includes;
235             }
236              
237             sub parse_includes {
238 39     39 0 61 my ($self) = @_;
239              
240 39         104 my $source = $self->source->data;
241              
242 39         91 return $self->{includes} = $source->content('includes');
243             }
244              
245             sub check_includes {
246 39     39 0 82 my ($self) = @_;
247              
248 39         86 my $includes = $self->includes;
249              
250 39 100       114 return 1 if !$includes;
251              
252 37         84 for my $include (map { [split /\s*:\s*/] } grep {length} @$includes) {
  379         1525  
  393         539  
253 379 100       652 next if $include->[0] eq 'function';
254 365 50       570 next if $include->[0] eq 'routine';
255 365 50       656 next if $include->[0] eq 'method';
256              
257 0         0 return 0;
258             }
259              
260 37         187 return 1;
261             }
262              
263             sub build_description {
264 39     39 0 68 my ($self) = @_;
265              
266 39         107 $self->parse_description;
267 39 50       166 $self->check_description or Carp::confess 'build description failed';
268              
269 39         108 return $self->description;
270             }
271              
272             sub parse_description {
273 39     39 0 87 my ($self) = @_;
274              
275 39         116 my $source = $self->source->data;
276              
277 39         95 return $self->{description} = $source->content('description');
278             }
279              
280             sub check_description {
281 39     39 0 85 my ($self) = @_;
282              
283 39         87 my $description = $self->description;
284              
285 39 50       91 return 0 if !$description;
286 39         141 return 1;
287             }
288              
289             sub build_inherits {
290 39     39 0 70 my ($self) = @_;
291              
292 39         115 $self->parse_inherits;
293 39 50       112 $self->check_inherits or Carp::confess 'build inherits failed';
294              
295 39         90 return $self->inherits;
296             }
297              
298             sub parse_inherits {
299 39     39 0 72 my ($self) = @_;
300              
301 39         93 my $source = $self->source->data;
302              
303 39         128 return $self->{inherits} = $source->content('inherits');
304             }
305              
306             sub check_inherits {
307 39     39 0 87 my ($self) = @_;
308              
309 39         93 my $inherits = $self->inherits;
310              
311 39 100       158 return 1 if !$inherits;
312              
313 1         2 for my $inherit (@$inherits) {
314 1 50       5 return 0 if $inherit =~ /[^\w\'\:']/;
315             }
316              
317 1         3 return 1;
318             }
319              
320             sub build_integrates {
321 39     39 0 81 my ($self) = @_;
322              
323 39         121 $self->parse_integrates;
324 39 50       153 $self->check_integrates or Carp::confess 'build integrates failed';
325              
326 39         99 return $self->integrates;
327             }
328              
329             sub parse_integrates {
330 39     39 0 71 my ($self) = @_;
331              
332 39         89 my $source = $self->source->data;
333              
334 39         124 return $self->{integrates} = $source->content('integrates');
335             }
336              
337             sub check_integrates {
338 39     39 0 94 my ($self) = @_;
339              
340 39         92 my $integrates = $self->integrates;
341              
342 39 50       142 return 1 if !$integrates;
343              
344 0         0 for my $integrate (@$integrates) {
345 0 0       0 return 0 if $integrate =~ /[^\w\'\:']/;
346             }
347              
348 0         0 return 1;
349             }
350              
351             sub build_attributes {
352 39     39 0 71 my ($self) = @_;
353              
354 39         121 $self->parse_attributes;
355 39 50       112 $self->check_attributes or Carp::confess 'build attributes failed';
356              
357 39         82 return $self->attributes;
358             }
359              
360             sub parse_attributes {
361 39     39 0 68 my ($self) = @_;
362              
363 39         88 my $source = $self->source->data;
364 39         90 my $lines = $source->content('attributes');
365              
366 39         120 my $attributes = {};
367              
368 39         93 for my $line (@$lines) {
369 65         380 my ($name, $is, $presence, $type) = map {split /,\s*/}
  130         543  
370             ($line =~ /(\w+)\s*\:\s*(.*)/);
371              
372 65         425 $attributes->{$name} = {is => $is, presence => $presence, type => $type};
373             }
374              
375 39         132 $self->stash(attributes => $attributes);
376              
377 39         80 return $self->{attributes} = $lines;
378             }
379              
380             sub check_attributes {
381 39     39 0 73 my ($self) = @_;
382              
383 39         94 my $attributes = $self->attributes;
384              
385 39 100       136 return 1 if !@$attributes;
386              
387 38         79 my $stashed = $self->stash('attributes');
388              
389 38         144 for my $name (keys %$stashed) {
390 65 50       149 return 0 if !$stashed->{$name}{is};
391             return 0 if !(
392             $stashed->{$name}{is} eq 'ro' ||
393 65 50 33     213 $stashed->{$name}{is} eq 'rw'
394             );
395 65 50       130 return 0 if !$stashed->{$name}{presence};
396             return 0 if !(
397             $stashed->{$name}{presence} eq 'req' ||
398 65 50 66     215 $stashed->{$name}{presence} eq 'opt'
399             );
400 65 50       155 return 0 if !$stashed->{$name}{type};
401             }
402              
403 38         120 return 1;
404             }
405              
406             sub build_libraries {
407 39     39 0 86 my ($self) = @_;
408              
409 39         116 $self->parse_libraries;
410 39 50       121 $self->check_libraries or Carp::confess 'build libraries failed';
411              
412 39         101 return $self->libraries;
413             }
414              
415             sub parse_libraries {
416 39     39 0 69 my ($self) = @_;
417              
418 39         111 my $source = $self->source->data;
419              
420 39         118 return $self->{libraries} = $source->content('libraries');
421             }
422              
423             sub check_libraries {
424 39     39 0 79 my ($self) = @_;
425              
426 39         108 my $libraries = $self->libraries;
427              
428 39 100       98 return 1 if !$libraries;
429              
430 38         75 for my $library (@$libraries) {
431 38 50       188 return 0 if $library =~ /[^\w\'\:']/;
432             }
433              
434 38         94 return 1;
435             }
436              
437             sub build_headers {
438 39     39 0 94 my ($self) = @_;
439              
440 39         135 $self->parse_headers;
441 39 50       186 $self->check_headers or Carp::confess 'build headers failed';
442              
443 39         97 return $self->headers;
444             }
445              
446             sub parse_headers {
447 39     39 0 136 my ($self) = @_;
448              
449 39         88 my $source = $self->source->data;
450              
451 39         110 return $self->{headers} = $source->content('headers');
452             }
453              
454             sub check_headers {
455 39     39 0 85 my ($self) = @_;
456              
457 39         97 my $headers = $self->headers;
458              
459 39 100       131 return 1 if !$headers;
460              
461 14 50       38 return 0 unless scalar @$headers;
462              
463 14         44 return 1;
464             }
465              
466             sub build_footers {
467 39     39 0 88 my ($self) = @_;
468              
469 39         119 $self->parse_footers;
470 39 50       132 $self->check_footers or Carp::confess 'build footers failed';
471              
472 39         91 return $self->footers;
473             }
474              
475             sub parse_footers {
476 39     39 0 72 my ($self) = @_;
477              
478 39         116 my $source = $self->source->data;
479              
480 39         96 return $self->{footers} = $source->content('footers');
481             }
482              
483             sub check_footers {
484 39     39 0 93 my ($self) = @_;
485              
486 39         124 my $footers = $self->footers;
487              
488 39 100       127 return 1 if !$footers;
489              
490 14 50       33 return 0 unless scalar @$footers;
491              
492 14         31 return 1;
493             }
494              
495             sub build_scenarios {
496 39     39 0 79 my ($self) = @_;
497              
498 39         118 $self->parse_scenarios;
499 39 50       114 $self->check_scenarios or Carp::confess 'build scenarios failed';
500              
501 39         84 return $self->stash('scenarios');
502             }
503              
504             sub parse_scenarios {
505 39     39 0 76 my ($self) = @_;
506              
507 39         93 my $source = $self->source->data;
508              
509 39         74 my $scenarios = {};
510              
511 39         70 for my $metadata (@{$source->list('scenario')}) {
  39         134  
512 14 50       60 if (my $content = $source->contents("example", $metadata->{name})) {
513 14 50       49 next if !@$content;
514              
515 14         50 my $usage = $metadata->{data};
516             $scenarios->{$metadata->{name}} = {
517 14         62 usage => $usage,
518             example => $content
519             };
520             }
521             }
522              
523 39         123 return $self->stash(scenarios => $scenarios);
524             }
525              
526             sub check_scenarios {
527 39     39 0 73 my ($self) = @_;
528              
529 39         76 my $scenarios = $self->stash('scenarios');
530              
531 39 100       168 return 1 if !%$scenarios;
532              
533 14         76 while (my($key, $val) = each(%$scenarios)) {
534 14 50       47 return 0 unless $val->{usage};
535 14 50       61 return 0 unless $val->{example};
536             }
537              
538 14         36 return 1;
539             }
540              
541             sub build_methods {
542 39     39 0 90 my ($self) = @_;
543              
544 39 100       108 return if !$self->includes;
545              
546 37         113 $self->parse_methods;
547 37 50       285 $self->check_methods or Carp::confess 'build methods failed';
548              
549 37         97 return $self->stash('methods');
550             }
551              
552             sub parse_methods {
553 37     37 0 74 my ($self) = @_;
554              
555 37         95 my $source = $self->source->data;
556              
557 37         58 my $methods = {};
558              
559 37         63 for my $include (map { [split /\s*:\s*/] } grep {length} @{$self->includes}) {
  379         1470  
  393         528  
  37         95  
560 379 100       866 next if $include->[0] ne 'method';
561              
562 365         532 my $item = {};
563 365         569 my $name = $include->[1];
564              
565 365         884 $item->{usage} = $source->contents('method', $name);
566 365         906 $item->{signature} = $source->contents('signature', $name);
567              
568 365         589 my $index = 1;
569 365         1259 while (my $content = $source->contents("example-$index", $name)) {
570 752 100       1563 last if !@$content;
571              
572 387         987 $item->{examples}{$index} = $content;
573 387         1356 $index++;
574             }
575              
576 365         1052 $methods->{$name} = $item;
577             }
578              
579 37         290 return $self->stash(methods => $methods);
580             }
581              
582             sub check_methods {
583 37     37 0 73 my ($self) = @_;
584              
585 37         84 my $methods = $self->stash('methods');
586              
587 37 50       107 return 1 if !%$methods;
588              
589 37         186 while (my($key, $val) = each(%$methods)) {
590 365 50 33     758 unless ($val->{usage} && @{$val->{usage}}) {
  365         832  
591 0         0 warn "Missing: $key (usage)";
592 0         0 return 0;
593             }
594 365 50 33     684 unless ($val->{signature} && @{$val->{signature}}) {
  365         810  
595 0         0 warn "Missing: $key (signature)";
596 0         0 return 0;
597             }
598 365 50       1035 unless ($val->{examples}) {
599 0         0 warn "Missing: $key (examples)";
600 0         0 return 0;
601             }
602             }
603              
604 37         103 return 1;
605             }
606              
607             sub build_functions {
608 39     39 0 70 my ($self) = @_;
609              
610 39 100       129 return if !$self->includes;
611              
612 37         103 $self->parse_functions;
613 37 50       159 $self->check_functions or Carp::confess 'build functions failed';
614              
615 37         83 return $self->stash('functions');
616             }
617              
618             sub parse_functions {
619 37     37 0 69 my ($self) = @_;
620              
621 37         104 my $source = $self->source->data;
622              
623 37         80 my $functions = {};
624              
625 37         61 for my $include (map { [split /\s*:\s*/] } grep {length} @{$self->includes}) {
  379         1837  
  393         523  
  37         90  
626 379 100       696 next if $include->[0] ne 'function';
627              
628 14         32 my $item = {};
629 14         34 my $name = $include->[1];
630              
631 14         47 $item->{usage} = $source->contents('function', $name);
632 14         51 $item->{signature} = $source->contents('signature', $name);
633              
634 14         36 my $index = 1;
635 14         55 while (my $content = $source->contents("example-$index", $name)) {
636 28 100       84 last if !@$content;
637              
638 14         36 $item->{examples}{$index} = $content;
639 14         54 $index++;
640             }
641              
642 14         45 $functions->{$name} = $item;
643             }
644              
645 37         176 return $self->stash(functions => $functions);
646             }
647              
648             sub check_functions {
649 37     37 0 82 my ($self) = @_;
650              
651 37         95 my $functions = $self->stash('functions');
652              
653 37 100       149 return 1 if !%$functions;
654              
655 14         62 while (my($key, $val) = each(%$functions)) {
656 14 50 33     52 unless ($val->{usage} && @{$val->{usage}}) {
  14         49  
657 0         0 warn "Missing: $key (usage)";
658 0         0 return 0;
659             }
660 14 50 33     53 unless ($val->{signature} && @{$val->{signature}}) {
  14         41  
661 0         0 warn "Missing: $key (signature)";
662 0         0 return 0;
663             }
664 14 50       58 unless ($val->{examples}) {
665 0         0 warn "Missing: $key (examples)";
666 0         0 return 0;
667             }
668             }
669              
670 14         36 return 1;
671             }
672              
673             sub build_routines {
674 39     39 0 97 my ($self) = @_;
675              
676 39 100       130 return if !$self->includes;
677              
678 37         106 $self->parse_routines;
679 37 50       95 $self->check_routines or Carp::confess 'build routines failed';
680              
681 37         74 return $self->stash('routines');
682             }
683              
684             sub parse_routines {
685 37     37 0 63 my ($self) = @_;
686              
687 37         111 my $source = $self->source->data;
688              
689 37         61 my $routines = {};
690              
691 37         80 for my $include (map { [split /\s*:\s*/] } grep {length} @{$self->includes}) {
  379         1322  
  393         516  
  37         112  
692 379 50       704 next if $include->[0] ne 'routine';
693              
694 0         0 my $item = {};
695 0         0 my $name = $include->[1];
696              
697 0         0 $item->{usage} = $source->contents('routine', $name);
698 0         0 $item->{signature} = $source->contents('signature', $name);
699              
700 0         0 my $index = 1;
701 0         0 while (my $content = $source->contents("example-$index", $name)) {
702 0 0       0 last if !@$content;
703              
704 0         0 $item->{examples}{$index} = $content;
705 0         0 $index++;
706             }
707              
708 0         0 $routines->{$name} = $item;
709             }
710              
711 37         147 return $self->stash(routines => $routines);
712             }
713              
714             sub check_routines {
715 37     37 0 69 my ($self) = @_;
716              
717 37         73 my $routines = $self->stash('routines');
718              
719 37 50       129 return 1 if !%$routines;
720              
721 0         0 while (my($key, $val) = each(%$routines)) {
722 0 0 0     0 unless ($val->{usage} && @{$val->{usage}}) {
  0         0  
723 0         0 warn "Missing: $key (usage)";
724 0         0 return 0;
725             }
726 0 0 0     0 unless ($val->{signature} && @{$val->{signature}}) {
  0         0  
727 0         0 warn "Missing: $key (signature)";
728 0         0 return 0;
729             }
730 0 0       0 unless ($val->{examples}) {
731 0         0 warn "Missing: $key (examples)";
732 0         0 return 0;
733             }
734             }
735              
736 0         0 return 1;
737             }
738              
739             sub build_types {
740 39     39 0 70 my ($self) = @_;
741              
742 39         133 $self->parse_types;
743 39 50       110 $self->check_types or Carp::confess 'build types failed';
744              
745 39         90 return $self->stash('types');
746             }
747              
748             sub parse_types {
749 39     39 0 87 my ($self) = @_;
750              
751 39         94 my $source = $self->source->data;
752              
753 39         75 my $types = {};
754 39         130 my $listings = $source->list('type');
755              
756 39         162 for my $name (map {$$_{name}} @{$listings}) {
  4         10  
  39         116  
757 4         8 my $item = {};
758              
759 4         10 $item->{usage} = $source->contents('type', $name);
760 4         12 $item->{library} = $source->contents('type-library', $name);
761 4         11 $item->{parent} = $source->contents('type-parent', $name);
762 4         9 $item->{composite} = $source->contents('type-composite', $name);
763              
764 4         6 my $index;
765              
766 4         6 $index = 1;
767 4         14 while (my $content = $source->contents("type-coercion-$index", $name)) {
768 4 50       11 last if !@$content;
769              
770 0         0 $item->{coercions}{$index} = $content;
771 0         0 $index++;
772             }
773              
774 4         9 $index = 1;
775 4         20 while (my $content = $source->contents("type-example-$index", $name)) {
776 8 100       22 last if !@$content;
777              
778 4         12 $item->{examples}{$index} = $content;
779 4         14 $index++;
780             }
781              
782 4         10 $types->{$name} = $item;
783             }
784              
785 39         116 return $self->stash(types => $types);
786             }
787              
788             sub check_types {
789 39     39 0 85 my ($self) = @_;
790              
791 39         87 my $types = $self->stash('types');
792              
793 39 100       134 return 1 if !%$types;
794              
795 1         6 while (my($key, $val) = each(%$types)) {
796 4 50       19 return 0 unless $val->{usage};
797 4 50       9 return 0 unless $val->{library};
798 4 50       84 return 0 unless $val->{examples};
799             }
800              
801 1         4 return 1;
802             }
803              
804             sub scenarios {
805 14     14 0 42 my ($self, $name, $attr) = @_;
806              
807 14         37 my $scenarios = $self->stash('scenarios');
808              
809 14 100       47 return $scenarios if !$name;
810              
811 2         8 my $result = $scenarios->{$name};
812              
813 2 50       7 return $result if !$attr;
814              
815 2         8 return $result->{$attr};
816             }
817              
818             sub methods {
819 82     82 0 176 my ($self, $name, $attr) = @_;
820              
821 82         184 my $methods = $self->stash('methods');
822              
823 82 100       223 return $methods if !$name;
824              
825 50         110 my $result = $methods->{$name};
826              
827 50 50       120 return $result if !$attr;
828              
829 50         147 return $result->{$attr};
830             }
831              
832             sub functions {
833 22     22 0 51 my ($self, $name, $attr) = @_;
834              
835 22         56 my $functions = $self->stash('functions');
836              
837 22 100       88 return $functions if !$name;
838              
839 2         3 my $result = $functions->{$name};
840              
841 2 50       6 return $result if !$attr;
842              
843 2         6 return $result->{$attr};
844             }
845              
846             sub routines {
847 14     14 0 58 my ($self, $name, $attr) = @_;
848              
849 14         39 my $routines = $self->stash('routines');
850              
851 14 50       54 return $routines if !$name;
852              
853 0         0 my $result = $routines->{$name};
854              
855 0 0       0 return $result if !$attr;
856              
857 0         0 return $result->{$attr};
858             }
859              
860             sub types {
861 13     13 0 68 my ($self, $name, $attr) = @_;
862              
863 13         43 my $types = $self->stash('types');
864              
865 13 50       1148 return $types if !$name;
866              
867 0         0 my $result = $types->{$name};
868              
869 0 0       0 return $result if !$attr;
870              
871 0         0 return $result->{$attr};
872             }
873              
874             sub render {
875 115     115 0 16762 my ($self, $method, @args) = @_;
876              
877 115         218 my $newline = "\n";
878              
879 115 50       531 my $results = $self->$method(@args) or return "";
880              
881 115         3081 return join $newline, @$results;
882             }
883              
884             sub stash {
885 815     815 0 1389 my ($self, $key, $value) = @_;
886              
887 815 50       1500 return $self->{'$stash'} if !exists $_[1];
888              
889 815 100       1885 return $self->{'$stash'}->{$key} if !exists $_[2];
890              
891 228         457 $self->{'$stash'}->{$key} = $value;
892              
893 228         367 return $value;
894             }
895              
896             1;
897              
898             =encoding utf8
899              
900             =head1 NAME
901              
902             Test::Auto::Parser
903              
904             =cut
905              
906             =head1 ABSTRACT
907              
908             Specification Parser
909              
910             =cut
911              
912             =head1 SYNOPSIS
913              
914             package main;
915              
916             use Test::Auto;
917             use Test::Auto::Parser;
918              
919             my $test = Test::Auto->new(
920             't/Test_Auto_Parser.t'
921             );
922              
923             my $parser = Test::Auto::Parser->new(
924             source => $test
925             );
926              
927             =cut
928              
929             =head1 DESCRIPTION
930              
931             This package parses files containing POD blocks which adhere to the
932             specification as defined in L<Test::Auto/SPECIFICATION>, and provides methods
933             for accessing the data.
934              
935             =cut
936              
937             =head1 LIBRARIES
938              
939             This package uses type constraints from:
940              
941             L<Test::Auto::Types>
942              
943             =cut
944              
945             =head1 ATTRIBUTES
946              
947             This package has the following attributes:
948              
949             =cut
950              
951             =head2 abstract
952              
953             abstract(ArrayRef[Str])
954              
955             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
956              
957             =cut
958              
959             =head2 attributes
960              
961             attributes(ArrayRef[Str])
962              
963             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
964              
965             =cut
966              
967             =head2 description
968              
969             description(ArrayRef[Str])
970              
971             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
972              
973             =cut
974              
975             =head2 footers
976              
977             footers(ArrayRef[Str])
978              
979             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
980              
981             =cut
982              
983             =head2 headers
984              
985             headers(ArrayRef[Str])
986              
987             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
988              
989             =cut
990              
991             =head2 includes
992              
993             includes(ArrayRef[Str])
994              
995             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
996              
997             =cut
998              
999             =head2 inherits
1000              
1001             inherits(ArrayRef[Str])
1002              
1003             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
1004              
1005             =cut
1006              
1007             =head2 integrates
1008              
1009             integrates(ArrayRef[Str])
1010              
1011             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
1012              
1013             =cut
1014              
1015             =head2 libraries
1016              
1017             libraries(ArrayRef[Str])
1018              
1019             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
1020              
1021             =cut
1022              
1023             =head2 name
1024              
1025             name(ArrayRef[Str])
1026              
1027             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
1028              
1029             =cut
1030              
1031             =head2 source
1032              
1033             source(Source)
1034              
1035             This attribute is read-only, accepts C<(Source)> values, and is required.
1036              
1037             =cut
1038              
1039             =head2 synopsis
1040              
1041             synopsis(ArrayRef[Str])
1042              
1043             This attribute is read-only, accepts C<(ArrayRef[Str])> values, and is optional.
1044              
1045             =cut
1046              
1047             =head1 AUTHOR
1048              
1049             Al Newkirk, C<awncorp@cpan.org>
1050              
1051             =head1 LICENSE
1052              
1053             Copyright (C) 2011-2019, Al Newkirk, et al.
1054              
1055             This is free software; you can redistribute it and/or modify it under the terms
1056             of the The Apache License, Version 2.0, as elucidated in the
1057             L<"license file"|https://github.com/iamalnewkirk/test-auto/blob/master/LICENSE>.
1058              
1059             =head1 PROJECT
1060              
1061             L<Wiki|https://github.com/iamalnewkirk/test-auto/wiki>
1062              
1063             L<Project|https://github.com/iamalnewkirk/test-auto>
1064              
1065             L<Initiatives|https://github.com/iamalnewkirk/test-auto/projects>
1066              
1067             L<Milestones|https://github.com/iamalnewkirk/test-auto/milestones>
1068              
1069             L<Issues|https://github.com/iamalnewkirk/test-auto/issues>
1070              
1071             =cut