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 23     23   150 use strictures 2;
  23         189  
  23         989  
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.103";
20              
21 23     23   5482 use Getopt::Long::Descriptive;
  23         56  
  23         141  
22 23     23   6643 use Module::Runtime qw(use_module);
  23         49  
  23         186  
23 23     23   1081 use Scalar::Util qw/blessed/;
  23         43  
  23         1223  
24 23     23   6837 use Text::LineFold ();
  23         507068  
  23         620  
25              
26 23     23   1207 use Moo;
  23         6699  
  23         186  
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 18     18   191 my $self = shift;
35 18         68 +{ '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   26 my $self = shift;
50 3         10 +{ '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 94     94 1 243 my ($self) = @_;
88 94         204 my $sub_commands = [];
89 94 50 33     745 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 94 50       1946 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 84     84 1 215 my ($self) = @_;
111             my %options_data
112 84 50       2182 = defined $self->{target} ? $self->{target}->_options_data : ();
113             my %options_config
114             = defined $self->{target}
115             ? $self->{target}->_options_config
116 84 50       2672 : ( spacer => " " );
117 84         1325 my $getopt_options = $self->options;
118              
119 84         233 my $lf = _get_line_fold();
120              
121 84         53495 my @to_fold;
122 84         163 my $max_spec_length = 0;
123 84         226 for my $opt (@$getopt_options) {
124 553 100       1179 if ( $opt->{desc} eq 'spacer' ) {
125 86         172 push @to_fold, '';
126             push @to_fold,
127 86         275 $options_config{spacer} x ( $lf->config('ColMax') - 4 );
128 86         3368 next;
129             }
130 467         2281 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
131 467         731 my $format_doc_str;
132 467 100       2025 $format_doc_str = $self->format_doc->{$format} if defined $format;
133             $format_doc_str = 'JSON'
134 467 100       1842 if defined $options_data{ $opt->{name} }{json};
135              
136             my $spec
137             = ( defined $short ? "-" . $short . " " : "" ) . "-"
138             . ( length( $opt->{name} ) > 1 ? "-" : "" )
139             . $opt->{name}
140 467 100       1675 . ( defined $format_doc_str ? "=" . $format_doc_str : "" );
    100          
    100          
141              
142 467 100       867 $max_spec_length = length($spec) if $max_spec_length < length($spec);
143              
144 467         1007 push @to_fold, $spec, $opt->{desc};
145             }
146              
147 84         141 my @message;
148 84         238 while (@to_fold) {
149 553         342132 my $spec = shift @to_fold;
150 553         862 my $desc = shift @to_fold;
151 553 100       1095 if ( length($spec) ) {
152 467         2801 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 86         233 push @message, $desc, "\n";
164             }
165             }
166              
167 84         69820 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 94   100 94   457 || eval {
176             use_module("Term::Size::Any");
177             [ Term::Size::Any::chars() ]->[0];
178             } || 80;
179              
180 94         2324 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 29 my ($self) = @_;
191             my %options_data
192 10 50       268 = defined $self->{target} ? $self->{target}->_options_data : ();
193             my %options_config
194             = defined $self->{target}
195             ? $self->{target}->_options_config
196 10 50       315 : ( spacer => " " );
197 10         173 my $getopt_options = $self->options;
198 10         22 my @message;
199 10         30 my $lf = _get_line_fold();
200 10         5974 for my $opt (@$getopt_options) {
201 64 100       26708 if ( $opt->{desc} eq 'spacer' ) {
202             push @message,
203 12         45 $options_config{spacer} x ( $lf->config('ColMax') - 4 );
204 12         426 push @message, "";
205 12         29 next;
206             }
207 52         320 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
208 52         96 my $format_doc_str;
209 52 100       178 $format_doc_str = $self->format_doc->{$format} if defined $format;
210             $format_doc_str = 'JSON'
211 52 100       198 if defined $options_data{ $opt->{name} }{json};
212             push @message,
213             ( defined $short ? "-" . $short . " " : "" ) . "-"
214             . ( length( $opt->{name} ) > 1 ? "-" : "" )
215 52 50       272 . $opt->{name} . ":"
    100          
    100          
216             . ( defined $format_doc_str ? " " . $format_doc_str : "" );
217              
218 52         106 my $opt_data = $options_data{ $opt->{name} };
219 52 50       100 $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       203 );
228             }
229              
230 10         4919 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 7 my ($self) = @_;
243              
244             my %options_data
245 3 50       77 = defined $self->{target} ? $self->{target}->_options_data : ();
246             my %options_config
247             = defined $self->{target}
248             ? $self->{target}->_options_config
249 3 50       90 : ( spacer => " " );
250              
251 3         43 my $prog_name = $self->{prog_name};
252 3 50       12 $prog_name = Getopt::Long::Descriptive::prog_name if !defined $prog_name;
253              
254 3         6 my $sub_commands = [];
255 3 50 33     17 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         10 my @man = ( "=encoding UTF-8", "=head1 NAME", $prog_name, );
266              
267 3 50       9 if ( defined( my $description = $options_config{description} ) ) {
268 0         0 push @man, "=head1 DESCRIPTION", $description;
269             }
270              
271 3         14 push @man,
272             (
273             "=head1 SYNOPSIS",
274             $prog_name . " [-h] [" . $self->__("long options ...") . "]"
275             );
276              
277 3 50       19 if ( defined( my $synopsis = $options_config{synopsis} ) ) {
278 0         0 push @man, $synopsis;
279             }
280              
281 3         6 push @man, ( "=head1 OPTIONS", "=over" );
282              
283 3         9 my $spacer_escape = "E<" . ord( $options_config{spacer} ) . ">";
284 3         8 for my $opt ( @{ $self->options } ) {
  3         16  
285 18 100       49 if ( $opt->{desc} eq 'spacer' ) {
286 3         5 push @man, "=back";
287 3         13 push @man, $spacer_escape x 40;
288 3         5 push @man, "=over";
289 3         5 next;
290             }
291 15         67 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
292 15         21 my $format_doc_str;
293 15 100       76 $format_doc_str = $self->format_doc_long->{$format}
294             if defined $format;
295             $format_doc_str = 'JSON'
296 15 100       89 if defined $options_data{ $opt->{name} }{json};
297              
298             my $opt_long_name
299 15 100       40 = "-" . ( length( $opt->{name} ) > 1 ? "-" : "" ) . $opt->{name};
300 15 50       37 my $opt_name
    100          
301             = ( defined $short ? "-" . $short . " " : "" )
302             . $opt_long_name . ":"
303             . ( defined $format_doc_str ? " " . $format_doc_str : "" );
304              
305 15         28 push @man, "=item B<" . $opt_name . ">";
306              
307 15         20 my $opt_data = $options_data{ $opt->{name} };
308 15 50       27 $opt_data = {} if !defined $opt_data;
309             push @man, defined $opt_data->{long_doc}
310             ? $opt_data->{long_doc}
311 15 50       32 : $opt->{desc};
312             }
313 3         6 push @man, "=back";
314              
315 3 50       8 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       10 if ( defined( my $authors = $options_config{authors} ) ) {
343 3 50 33     12 if ( !ref $authors && length($authors) ) {
344 0         0 $authors = [$authors];
345             }
346 3 50       7 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         35 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 5 my ($self) = @_;
364             my %options_data
365 2 50       50 = defined $self->{target} ? $self->{target}->_options_data : ();
366 2         32 my $getopt_options = $self->options;
367              
368 2         6 my $prog_name = $self->{prog_name};
369 2 50       6 $prog_name = Getopt::Long::Descriptive::prog_name if !defined $prog_name;
370              
371 2         4 my @message;
372 2         7 for my $opt (@$getopt_options) {
373 14 100       28 if ( $opt->{desc} eq 'spacer' ) {
374 2         3 push @message, '';
375 2         4 next;
376             }
377 12         54 my ($format) = $opt->{spec} =~ /(?:\|\w)?(?:=(.*?))?$/x;
378 12         21 my $format_doc_str;
379 12 100       73 $format_doc_str = $self->format_doc->{$format} if defined $format;
380             $format_doc_str = 'JSON'
381 12 50       63 if defined $options_data{ $opt->{name} }{json};
382             push @message,
383             "-"
384             . ( length( $opt->{name} ) > 1 ? "-" : "" )
385             . $opt->{name}
386 12 100       47 . ( defined $format_doc_str ? "=" . $format_doc_str : "" );
    100          
387             }
388             return
389 2 100       6 join( " ", $prog_name, map { $_ eq '' ? " | " : "[ $_ ]" } @message );
  14         41  
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 10     10 1 10550 my ($self) = @_;
408 10         39 $self->{should_die} = 1;
409 10         25 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 23     23   50518 );
  23         50  
  23         242  
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;