File Coverage

blib/lib/App/Spec/Pod.pm
Criterion Covered Total %
statement 139 145 95.8
branch 37 42 88.1
condition 19 21 90.4
subroutine 9 10 90.0
pod 6 7 85.7
total 210 225 93.3


line stmt bran cond sub pod time code
1             # ASTRACT: Generates Pod from App::Spec objects
2 2     2   237973 use strict;
  2         5  
  2         74  
3 2     2   8 use warnings;
  2         2  
  2         206  
4             package App::Spec::Pod;
5              
6             our $VERSION = 'v0.15.0'; # VERSION
7              
8 2     2   504 use Moo;
  2         9525  
  2         13  
9              
10             has spec => ( is => 'ro' );
11              
12             sub generate {
13 4     4 1 2228 my ($self) = @_;
14 4         18 my $spec = $self->spec;
15 4         97 my $appname = $spec->name;
16 4         19 my $title = $spec->title;
17 4   100     24 my $abstract = $spec->abstract // '';
18 4   100     24 my $description = $spec->description // '';
19 4         15 my $subcmds = $spec->subcommands;
20 4         13 my $global_options = $spec->options;
21              
22 4         20 $self->markup(text => \$abstract);
23 4         18 $self->markup(text => \$description);
24              
25 4         17 my @subcmd_pod = $self->subcommand_pod(
26             commands => $subcmds,
27             );
28 4         8 my $option_string = '';
29 4 50       13 if (@$global_options) {
30 4         13 $option_string = "=head2 GLOBAL OPTIONS\n\n" . $self->options2pod(
31             options => $global_options,
32             );
33             }
34              
35 4         12 my $pod = <<"EOM";
36             \=head1 NAME
37              
38             $appname - $title
39              
40             \=head1 ABSTRACT
41              
42             $abstract
43              
44             \=head1 DESCRIPTION
45              
46             $description
47              
48             $option_string
49              
50             \=head2 SUBCOMMANDS
51              
52 4         58 @{[ join '', @subcmd_pod ]}
53             EOM
54              
55             }
56              
57             sub subcommand_pod {
58 11     11 1 35 my ($self, %args) = @_;
59 11         26 my $spec = $self->spec;
60 11         27 my $appname = $spec->name;
61 11         25 my $commands = $args{commands};
62 11   100     40 my $previous = $args{previous} || [];
63              
64 11         24 my @pod;
65             my %keys;
66 11         54 @keys{ keys %$commands } = ();
67 11         26 my @keys;
68 11 100       30 if (@$previous) {
69 7         31 @keys = sort keys %keys;
70             }
71             else {
72 4         9 for my $key (qw/ help _meta /) {
73 8 100       42 if (exists $keys{ $key }) {
74 5         9 push @keys, $key;
75 5         12 delete $keys{ $key };
76             }
77             }
78 4         28 unshift @keys, sort keys %keys;
79             }
80 11         28 for my $name (@keys) {
81 29         61 my $cmd_spec = $commands->{ $name };
82 29         101 my $name = $cmd_spec->name;
83 29         67 my $summary = $cmd_spec->summary;
84 29         61 my $description = $cmd_spec->description;
85 29         60 my $subcmds = $cmd_spec->subcommands;
86 29         91 my $parameters = $cmd_spec->parameters;
87 29         60 my $options = $cmd_spec->options;
88              
89 29         81 $self->markup(text => \$summary);
90 29         68 $self->markup(text => \$description);
91              
92 29         49 my $desc = '';
93 29 50       63 if (length $summary) {
94 29         86 $desc .= "$summary\n\n";
95             }
96 29 100       62 if (length $description) {
97 1         3 $desc .= "$description\n\n";
98             }
99              
100 29         68 my $usage = "$appname @$previous $name";
101 29 100       78 if (keys %$subcmds) {
102 10         16 $usage .= " ";
103             }
104              
105 29         54 my $option_string = '';
106 29 100       83 if (@$options) {
107 14         27 $usage .= " [options]";
108 14         35 $option_string = "Options:\n\n" . $self->options2pod(
109             options => $options,
110             );
111             }
112              
113 29 100       82 if (length $option_string) {
114 14         25 $desc .= "$option_string\n";
115             }
116              
117 29         92 my $param_string = '';
118 29 100       67 if (@$parameters) {
119 9         26 $param_string = "Parameters:\n\n" . $self->params2pod(
120             parameters => $parameters,
121             );
122 9         23 for my $param (@$parameters) {
123 14         35 my $name = $param->name;
124 14         27 my $required = $param->required;
125 14         52 $usage .= " " . $param->to_usage_header;
126             }
127             }
128 29 100       65 if (length $param_string) {
129 9         20 $desc .= $param_string;
130             }
131              
132 29         85 my $pod = <<"EOM";
133             \=head3 @$previous $name
134              
135             $usage
136              
137             $desc
138             EOM
139 29 100 100     94 if (keys %$subcmds and $name ne "help") {
140 7         35 my @sub = $self->subcommand_pod(
141             previous => [@$previous, $name],
142             commands => $subcmds,
143             );
144 7         31 $pod .= join '', @sub;
145             }
146 29         113 push @pod, $pod;
147             }
148 11         50 return @pod;
149             }
150              
151             sub params2pod {
152 9     9 1 28 my ($self, %args) = @_;
153 9         24 my $params = $args{parameters};
154 9         20 my @rows;
155 9         20 for my $param (@$params) {
156 14 100       47 my $required = $param->required ? '*' : '';
157 14         38 my $summary = $param->summary;
158 14         28 my $multi = '';
159 14 50       60 if ($param->mapping) {
    100          
160 0         0 $multi = '{}';
161             }
162             elsif ($param->multiple) {
163 2         5 $multi = '[]';
164             }
165 14         46 my $flags = $self->spec->_param_flags_string($param);
166 14         44 my @lines = split m/\n/, $summary;
167 14   100     105 push @rows, [" " . $param->name, " " . $required, $multi, ($lines[0] // '') . $flags];
168 14         50 push @rows, [" " , " ", '', $_] for map {s/^ +//; $_ } @lines[1 .. $#lines];
  2         6  
  2         11  
169             }
170 9         23 my $test = $self->simple_table(\@rows);
171 9         38 return $test;
172             }
173              
174             sub simple_table {
175 27     27 0 58 my ($self, $rows) = @_;
176 27         64 my @widths;
177              
178 27         49 for my $row (@$rows) {
179 61         141 for my $i (0 .. $#$row) {
180 244         397 my $col = $row->[ $i ];
181 244   100     732 $widths[ $i ] ||= 0;
182 244 100       597 if ( $widths[ $i ] < length $col) {
183 98         229 $widths[ $i ] = length $col;
184             }
185             }
186             }
187 27   100     53 my $format = join ' ', map { "%-" . ($_ || 0) . "s" } @widths;
  108         359  
188 27         51 my @lines;
189 27         50 for my $row (@$rows) {
190 61   50     114 my $string = sprintf "$format\n", map { $_ // '' } @$row;
  244         769  
191 61         172 push @lines, $string;
192             }
193 27         122 return join '', @lines;
194              
195             }
196              
197             sub options2pod {
198 18     18 1 48 my ($self, %args) = @_;
199 18         40 my $options = $args{options};
200 18         26 my @rows;
201 18         42 for my $opt (@$options) {
202 41         99 my $name = $opt->name;
203 41         85 my $aliases = $opt->aliases;
204 41         95 my $summary = $opt->summary;
205 41 50       99 my $required = $opt->required ? '*' : '';
206 41         63 my $multi = '';
207 41 100       170 if ($opt->mapping) {
    100          
208 1         3 $multi = '{}';
209             }
210             elsif ($opt->multiple) {
211 3         6 $multi = '[]';
212             }
213             my @names = map {
214 41 100       87 length $_ > 1 ? "--$_" : "-$_"
  56         200  
215             } ($name, @$aliases);
216 41         159 my $flags = $self->spec->_param_flags_string($opt);
217 41         118 my @lines = split m/\n/, $summary;
218 41   100     236 push @rows, [" @names", " " . $required, $multi, ($lines[0] // '') . $flags];
219 41         158 push @rows, [" ", " " , '', $_ ] for map {s/^ +//; $_ } @lines[1 .. $#lines];
  4         14  
  4         48  
220             }
221 18         57 my $test = $self->simple_table(\@rows);
222 18         85 return $test;
223             }
224              
225             sub markup {
226 66     66 1 157 my ($self, %args) = @_;
227 66         117 my $text = $args{text};
228 66 100       155 return unless defined $$text;
229 38   50     128 my $markup = $self->spec->markup // '';
230 38 50       111 if ($markup eq "swim") {
231 0           $$text = $self->swim2pod($$text);
232             }
233             }
234             sub swim2pod {
235 0     0 1   my ($self, $text) = @_;
236 0           require Swim;
237 0           my $swim = Swim->new(text => $text);
238 0           my $pod = $swim->to_pod;
239             }
240              
241             1;
242              
243             __END__