File Coverage

blib/lib/MooX/Options/Descriptive/Usage.pm
Criterion Covered Total %
statement 134 157 85.3
branch 71 100 71.0
condition 5 14 35.7
subroutine 16 18 88.8
pod 7 7 100.0
total 233 296 78.7


line stmt bran cond sub pod time code
1             package MooX::Options::Descriptive::Usage;
2              
3 22     22   172 use strictures 2;
  22         223  
  22         1148  
4              
5             =head1 NAME
6              
7             MooX::Options::Descriptive::Usage - Usage class
8              
9             =head1 DESCRIPTION
10              
11             Usage class to display the error message.
12              
13             This class use the full size of your terminal
14              
15             =cut
16              
17             ## no critic (ProhibitExcessComplexity)
18              
19             our $VERSION = "4.101";
20              
21 22     22   6334 use Getopt::Long::Descriptive;
  22         64  
  22         144  
22 22     22   7653 use Module::Runtime qw(use_module);
  22         63  
  22         207  
23 22     22   1348 use Scalar::Util qw/blessed/;
  22         59  
  22         1628  
24 22     22   11218 use Text::LineFold ();
  22         668784  
  22         748  
25              
26 22     22   1824 use Moo;
  22         7684  
  22         232  
27             with "MooX::Locale::Passthrough";
28              
29             has format_doc => ( is => "lazy" );
30              
31             ## no critic (Subroutines::RequireFinalReturn, Subroutines::ProhibitUnusedPrivateSubroutines)
32              
33             sub _build_format_doc {
34 17     17   242 my $self = shift;
35 17         78 +{ 's' => $self->__("String"),
36             's@' => $self->__("[Strings]"),
37             'i' => $self->__("Int"),
38             'i@' => $self->__("[Ints]"),
39             'o' => $self->__("Ext. Int"),
40             'o@' => $self->__("[Ext. Ints]"),
41             'f' => $self->__("Real"),
42             'f@' => $self->__("[Reals]"),
43             };
44             }
45              
46             has format_doc_long => ( is => "lazy" );
47              
48             sub _build_format_doc_long {
49 3     3   39 my $self = shift;
50 3         11 +{ 's' => $self->__("String"),
51             's@' => $self->__("Array of Strings"),
52             'i' => $self->__("Integer"),
53             'i@' => $self->__("Array of Integers"),
54             'o' => $self->__("Extended Integer"),
55             'o@' => $self->__("Array of extended integers"),
56             'f' => $self->__("Real number"),
57             'f@' => $self->__("Array of real numbers"),
58             };
59             }
60              
61             =head1 ATTRIBUTES
62              
63             Following attributes are present and behave as GLD::Usage describe them.
64              
65             =head2 leader_text
66              
67             Text that appear on top of your message
68              
69             =head2 options
70              
71             The options spec of your message
72              
73             =cut
74              
75             has leader_text => ( is => "ro" );
76             has options => ( is => "ro" );
77              
78             =head1 METHODS
79              
80             =head2 sub_commands_text
81              
82             Return the list of sub commands if available.
83              
84             =cut
85              
86             sub sub_commands_text {
87 91     91 1 342 my ($self) = @_;
88 91         246 my $sub_commands = [];
89 91 50 33     1052 if (defined $self->{target}
90             && defined(
91             my $sub_commands_options = $self->{target}->_options_sub_commands
92             )
93             )
94             {
95 0         0 $sub_commands = $sub_commands_options;
96             }
97 91 50       2767 return if !@$sub_commands;
98             return "",
99             $self->__("SUB COMMANDS AVAILABLE: ")
100 0         0 . join( ', ', map { $_->{name} } @$sub_commands ), "";
  0         0  
101             }
102              
103             =head2 text
104              
105             Return a compact help message.
106              
107             =cut
108              
109             sub text {
110 81     81 1 339 my ($self) = @_;
111             my %options_data
112 81 50       2805 = defined $self->{target} ? $self->{target}->_options_data : ();
113             my %options_config
114             = defined $self->{target}
115             ? $self->{target}->_options_config
116 81 50       3671 : ( spacer => " " );
117 81         1868 my $getopt_options = $self->options;
118              
119 81         412 my $lf = _get_line_fold();
120              
121 81         72747 my @to_fold;
122 81         205 my $max_spec_length = 0;
123 81         280 for my $opt (@$getopt_options) {
124 529 100       1682 if ( $opt->{desc} eq 'spacer' ) {
125 83         218 push @to_fold, '';
126             push @to_fold,
127 83         399 $options_config{spacer} x ( $lf->config('ColMax') - 4 );
128 83         4393 next;
129             }
130 446         2952 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
131 446         1032 my $format_doc_str;
132 446 100       2538 $format_doc_str = $self->format_doc->{$format} if defined $format;
133             $format_doc_str = 'JSON'
134 446 100       2598 if defined $options_data{ $opt->{name} }{json};
135              
136             my $spec
137             = ( defined $short ? "-" . $short . " " : "" ) . "-"
138             . ( length( $opt->{name} ) > 1 ? "-" : "" )
139             . $opt->{name}
140 446 100       2280 . ( defined $format_doc_str ? "=" . $format_doc_str : "" );
    100          
    100          
141              
142 446 100       1052 $max_spec_length = length($spec) if $max_spec_length < length($spec);
143              
144 446         1357 push @to_fold, $spec, $opt->{desc};
145             }
146              
147 81         188 my @message;
148 81         280 while (@to_fold) {
149 529         497223 my $spec = shift @to_fold;
150 529         1129 my $desc = shift @to_fold;
151 529 100       1483 if ( length($spec) ) {
152 446         3479 push @message,
153             $lf->fold(
154             " ",
155             " " x ( 6 + $max_spec_length ),
156             sprintf(
157             "%-" . ( $max_spec_length + 1 ) . "s %s",
158             $spec, $desc
159             )
160             );
161             }
162             else {
163 83         333 push @message, $desc, "\n";
164             }
165             }
166              
167 81         97270 return join( "\n",
168             $self->leader_text, "", join( "", @message ),
169             $self->sub_commands_text );
170             }
171              
172             # set the column size of your terminal into the wrapper
173             sub _get_line_fold {
174             my $columns = $ENV{TEST_FORCE_COLUMN_SIZE}
175 91   100 91   609 || eval {
176             use_module("Term::Size::Any");
177             [ Term::Size::Any::chars() ]->[0];
178             } || 80;
179              
180 91         3517 return Text::LineFold->new( ColMax => $columns - 4 );
181             }
182              
183             =head2 option_help
184              
185             Return the help message for your options
186              
187             =cut
188              
189             sub option_help {
190 10     10 1 33 my ($self) = @_;
191             my %options_data
192 10 50       324 = defined $self->{target} ? $self->{target}->_options_data : ();
193             my %options_config
194             = defined $self->{target}
195             ? $self->{target}->_options_config
196 10 50       463 : ( spacer => " " );
197 10         226 my $getopt_options = $self->options;
198 10         106 my @message;
199 10         50 my $lf = _get_line_fold();
200 10         7867 for my $opt (@$getopt_options) {
201 64 100       34634 if ( $opt->{desc} eq 'spacer' ) {
202             push @message,
203 12         120 $options_config{spacer} x ( $lf->config('ColMax') - 4 );
204 12         604 push @message, "";
205 12         39 next;
206             }
207 52         380 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
208 52         119 my $format_doc_str;
209 52 100       326 $format_doc_str = $self->format_doc->{$format} if defined $format;
210             $format_doc_str = 'JSON'
211 52 100       259 if defined $options_data{ $opt->{name} }{json};
212             push @message,
213             ( defined $short ? "-" . $short . " " : "" ) . "-"
214             . ( length( $opt->{name} ) > 1 ? "-" : "" )
215 52 50       336 . $opt->{name} . ":"
    100          
    100          
216             . ( defined $format_doc_str ? " " . $format_doc_str : "" );
217              
218 52         129 my $opt_data = $options_data{ $opt->{name} };
219 52 50       138 $opt_data = {} if !defined $opt_data;
220             push @message,
221             $lf->fold(
222             " ",
223             " ",
224             defined $opt_data->{long_doc}
225             ? $self->__( $opt_data->{long_doc} )
226             : $self->__( $opt->{desc} )
227 52 50       247 );
228             }
229              
230 10         6403 return join( "\n",
231             $self->leader_text, join( "\n ", "", @message ),
232             $self->sub_commands_text );
233             }
234              
235             =head2 option_pod
236              
237             Return the usage message in pod format
238              
239             =cut
240              
241             sub option_pod {
242 3     3 1 11 my ($self) = @_;
243              
244             my %options_data
245 3 50       114 = defined $self->{target} ? $self->{target}->_options_data : ();
246             my %options_config
247             = defined $self->{target}
248             ? $self->{target}->_options_config
249 3 50       134 : ( spacer => " " );
250              
251 3         66 my $prog_name = $self->{prog_name};
252 3 50       14 $prog_name = Getopt::Long::Descriptive::prog_name if !defined $prog_name;
253              
254 3         9 my $sub_commands = [];
255 3 50 33     24 if (defined $self->{target}
256             && defined(
257             my $sub_commands_options
258             = $self->{target}->_options_sub_commands()
259             )
260             )
261             {
262 0         0 $sub_commands = $sub_commands_options;
263             }
264              
265 3         12 my @man = ( "=encoding UTF-8", "=head1 NAME", $prog_name, );
266              
267 3 50       14 if ( defined( my $description = $options_config{description} ) ) {
268 0         0 push @man, "=head1 DESCRIPTION", $description;
269             }
270              
271 3         18 push @man,
272             (
273             "=head1 SYNOPSIS",
274             $prog_name . " [-h] [" . $self->__("long options ...") . "]"
275             );
276              
277 3 50       24 if ( defined( my $synopsis = $options_config{synopsis} ) ) {
278 0         0 push @man, $synopsis;
279             }
280              
281 3         10 push @man, ( "=head1 OPTIONS", "=over" );
282              
283 3         14 my $spacer_escape = "E<" . ord( $options_config{spacer} ) . ">";
284 3         6 for my $opt ( @{ $self->options } ) {
  3         22  
285 18 100       49 if ( $opt->{desc} eq 'spacer' ) {
286 3         9 push @man, "=back";
287 3         13 push @man, $spacer_escape x 40;
288 3         9 push @man, "=over";
289 3         7 next;
290             }
291 15         97 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
292 15         35 my $format_doc_str;
293 15 100       233 $format_doc_str = $self->format_doc_long->{$format}
294             if defined $format;
295             $format_doc_str = 'JSON'
296 15 100       129 if defined $options_data{ $opt->{name} }{json};
297              
298             my $opt_long_name
299 15 100       52 = "-" . ( length( $opt->{name} ) > 1 ? "-" : "" ) . $opt->{name};
300 15 50       59 my $opt_name
    100          
301             = ( defined $short ? "-" . $short . " " : "" )
302             . $opt_long_name . ":"
303             . ( defined $format_doc_str ? " " . $format_doc_str : "" );
304              
305 15         43 push @man, "=item B<" . $opt_name . ">";
306              
307 15         33 my $opt_data = $options_data{ $opt->{name} };
308 15 50       40 $opt_data = {} if !defined $opt_data;
309             push @man, defined $opt_data->{long_doc}
310             ? $opt_data->{long_doc}
311 15 50       52 : $opt->{desc};
312             }
313 3         12 push @man, "=back";
314              
315 3 50       13 if (@$sub_commands) {
316 0         0 push @man, "=head1 AVAILABLE SUB COMMANDS";
317 0         0 push @man, "=over";
318 0         0 for my $sub_command (@$sub_commands) {
319 0 0 0     0 if ($sub_command->{command}->can("_options_config")
320             && defined(
321             my $desc
322             = { $sub_command->{command}->_options_config }
323             ->{description}
324             )
325             )
326             {
327 0         0 push @man, "=item B<" . $sub_command->{name} . "> : " . $desc;
328             }
329             else {
330 0         0 push @man, "=item B<" . $sub_command->{name} . "> :";
331             }
332              
333             push @man,
334             $prog_name . " "
335             . $sub_command->{name}
336 0         0 . " [-h] ["
337             . $self->__("long options ...") . "]";
338             }
339 0         0 push @man, "=back";
340             }
341              
342 3 50       15 if ( defined( my $authors = $options_config{authors} ) ) {
343 3 50 33     19 if ( !ref $authors && length($authors) ) {
344 0         0 $authors = [$authors];
345             }
346 3 50       14 if (@$authors) {
347 0         0 push @man, ( "=head1 AUTHORS", "=over" );
348 0         0 push @man, map { "=item B<" . $_ . ">" } @$authors;
  0         0  
349 0         0 push @man, "=back";
350             }
351             }
352              
353 3         61 return join( "\n\n", @man );
354             }
355              
356             =head2 option_short_usage
357              
358             All options message without help
359              
360             =cut
361              
362             sub option_short_usage {
363 2     2 1 9 my ($self) = @_;
364             my %options_data
365 2 50       83 = defined $self->{target} ? $self->{target}->_options_data : ();
366 2         55 my $getopt_options = $self->options;
367              
368 2         10 my $prog_name = $self->{prog_name};
369 2 50       10 $prog_name = Getopt::Long::Descriptive::prog_name if !defined $prog_name;
370              
371 2         6 my @message;
372 2         11 for my $opt (@$getopt_options) {
373 14 100       49 if ( $opt->{desc} eq 'spacer' ) {
374 2         9 push @message, '';
375 2         7 next;
376             }
377 12         94 my ($format) = $opt->{spec} =~ /(?:\|\w)?(?:=(.*?))?$/x;
378 12         44 my $format_doc_str;
379 12 100       212 $format_doc_str = $self->format_doc->{$format} if defined $format;
380             $format_doc_str = 'JSON'
381 12 50       123 if defined $options_data{ $opt->{name} }{json};
382             push @message,
383             "-"
384             . ( length( $opt->{name} ) > 1 ? "-" : "" )
385             . $opt->{name}
386 12 100       75 . ( defined $format_doc_str ? "=" . $format_doc_str : "" );
    100          
387             }
388             return
389 2 100       9 join( " ", $prog_name, map { $_ eq '' ? " | " : "[ $_ ]" } @message );
  14         74  
390             }
391              
392             =head2 warn
393              
394             Warn your options help message
395              
396             =cut
397              
398 0     0 1 0 sub warn { return CORE::warn shift->text }
399              
400             =head2 die
401              
402             Croak your options help message
403              
404             =cut
405              
406             sub die {
407 9     9 1 15020 my ($self) = @_;
408 9         37 $self->{should_die} = 1;
409 9         27 return;
410             }
411              
412             use overload (
413             q{""} => "text",
414             '&{}' => sub {
415             return
416 0 0   0   0 sub { my ($self) = @_; return $self ? $self->text : $self->warn; };
  0         0  
  0         0  
417             }
418 22     22   64397 );
  22         65  
  22         296  
419              
420             =head1 SUPPORT
421              
422             You can find documentation for this module with the perldoc command.
423              
424             perldoc MooX::Options
425              
426             You can also look for information at:
427              
428             =over 4
429              
430             =item * RT: CPAN's request tracker (report bugs here)
431              
432             L
433              
434             =item * AnnoCPAN: Annotated CPAN documentation
435              
436             L
437              
438             =item * CPAN Ratings
439              
440             L
441              
442             =item * Search CPAN
443              
444             L
445              
446             =back
447              
448             =head1 AUTHOR
449              
450             celogeek
451              
452             =head1 COPYRIGHT AND LICENSE
453              
454             This software is copyright (c) 2013 by celogeek .
455              
456             This software is copyright (c) 2017 by Jens Rehsack.
457              
458             This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
459              
460             =cut
461              
462             1;