File Coverage

blib/lib/MooseX/App.pm
Criterion Covered Total %
statement 65 73 89.0
branch 16 18 88.8
condition 15 24 62.5
subroutine 15 17 88.2
pod 5 6 83.3
total 116 138 84.0


line stmt bran cond sub pod time code
1             # ============================================================================«
2             package MooseX::App;
3             # ============================================================================«
4              
5 10     10   2594786 use 5.010;
  10         30  
6 10     10   5336 use utf8;
  10         92  
  10         45  
7 10     10   286 use strict;
  10         22  
  10         200  
8 10     10   37 use warnings;
  10         10  
  10         527  
9              
10             our $AUTHORITY = 'cpan:MAROS';
11             our $VERSION = 1.37_01;
12              
13 10     10   4519 use MooseX::App::Meta::Role::Attribute::Option;
  10         24  
  10         523  
14 10     10   5130 use MooseX::App::Exporter qw(app_usage app_description app_base app_fuzzy app_strict app_prefer_commandline app_permute option parameter);
  10         31  
  10         84  
15 10     10   5361 use MooseX::App::Message::Envelope;
  10         48  
  10         409  
16 10     10   77 use Moose::Exporter;
  10         13  
  10         60  
17 10     10   531 use Scalar::Util qw(blessed);
  10         14  
  10         7831  
18              
19             my ($IMPORT,$UNIMPORT,$INIT_META) = Moose::Exporter->build_import_methods(
20             with_meta => [ qw(app_usage app_description app_namespace app_exclude app_base app_fuzzy app_command_name app_command_register app_strict app_prefer_commandline option parameter app_permute) ],
21             also => [ 'Moose' ],
22             as_is => [ 'new_with_command' ],
23             install => [ 'unimport','init_meta' ],
24             );
25              
26             sub import {
27 11     11   475 my ( $class, @plugins ) = @_;
28              
29             # Get caller
30 11         38 my ($caller_class) = caller();
31              
32             # Process plugins
33 11         223 MooseX::App::Exporter->process_plugins($caller_class,@plugins);
34              
35             # Call Moose-Exporter generated importer
36 11         131 return $class->$IMPORT( { into => $caller_class } );
37             }
38              
39             sub init_meta {
40 11     11 0 928 my ($class,%args) = @_;
41              
42             # Get required roles and metaroles
43 11         42 $args{roles} = ['MooseX::App::Role::Base'];
44             $args{metaroles} = {
45 11         61 class => [
46             'MooseX::App::Meta::Role::Class::Base',
47             'MooseX::App::Meta::Role::Class::Documentation'
48             ],
49             attribute => [
50             'MooseX::App::Meta::Role::Attribute::Option'
51             ],
52             };
53              
54 11         135 return MooseX::App::Exporter->process_init_meta(%args);
55             }
56              
57             sub app_command_name(&) {
58 3     3 1 49 my ( $meta, $namesub ) = @_;
59 3         116 return $meta->app_command_name($namesub);
60             }
61              
62             sub app_command_register(%) {
63 0     0 1 0 my ( $meta, %commands ) = @_;
64              
65 0         0 foreach my $command (keys %commands) {
66 0         0 $meta->command_register($command,$commands{$command});
67             }
68 0         0 return;
69             }
70              
71             sub app_namespace(@) {
72 0     0 1 0 my ( $meta, @namespaces ) = @_;
73 0         0 return $meta->app_namespace( \@namespaces );
74             }
75              
76             sub app_exclude(@) {
77 3     3 1 34 my ( $meta, @namespaces ) = @_;
78 3         113 return $meta->app_exclude( \@namespaces );
79             }
80              
81             sub new_with_command {
82 70     70 1 38189 my ($class,@args) = @_;
83              
84 70 100 66     571 Moose->throw_error('new_with_command is a class method')
85             if ! defined $class || blessed($class);
86              
87 69         418 my $meta = $class->meta;
88 69         1913 my $metameta = $meta->meta;
89              
90             # Sanity check
91 69 100 66     1118 Moose->throw_error('new_with_command may only be called from the application base package:'.$class)
92             if $metameta->does_role('MooseX::App::Meta::Role::Class::Command')
93             || ! $metameta->does_role('MooseX::App::Meta::Role::Class::Base');
94              
95             $meta->command_check()
96 68 50 33     40142 if $ENV{APP_DEVELOPER} || $ENV{HARNESS_ACTIVE};
97              
98             # Extra args
99 68         110 my %args;
100 68 50 33     441 if (scalar @args == 1
    100          
101             && ref($args[0]) eq 'HASH' ) {
102 0         0 %args = %{$args[0]};
  0         0  
103             } elsif (scalar @args % 2 == 0) {
104 67         183 %args = @args;
105             } else {
106 1         6 Moose->throw_error('new_with_command got invalid extra arguments');
107             }
108              
109             # Get ARGV
110 67         147 my $argv = delete $args{ARGV};
111 67         100 my $parsed_argv;
112 67 100       176 if (defined $argv) {
113 7         243 $parsed_argv = MooseX::App::ParsedArgv->new( argv => $argv );
114             } else {
115 60         320 $parsed_argv = MooseX::App::ParsedArgv->instance();
116             }
117              
118 67         268 my $first_argv = $parsed_argv->first_argv;
119              
120             # Requested help
121 67 100 100     1319 if (defined $first_argv
    100 66        
      66        
122             && lc($first_argv) =~ m/^(help|h|\?|usage|-h|--help|-\?|--usage)$/) {
123 2         14 return MooseX::App::Message::Envelope->new(
124             $meta->command_usage_global(),
125             );
126             # No args
127             } elsif (! defined $first_argv
128             || $first_argv =~ m/^\s*$/
129             || $first_argv =~ m/^-{1,2}\w/) {
130 2         10 return MooseX::App::Message::Envelope->new(
131             $meta->command_message(
132             header => "Missing command", # LOCALIZE
133             type => "error",
134             ),
135             $meta->command_usage_global(),
136             127, # exitcode
137             );
138             # Looks like a command
139             } else {
140 63         305 my $return = $meta->command_find();
141              
142             # Nothing found
143 63 100 66     325 if (blessed $return
144             && $return->isa('MooseX::App::Message::Block')) {
145 2         12 return MooseX::App::Message::Envelope->new(
146             $return,
147             $meta->command_usage_global(),
148             127, # exitcode
149             );
150             # One command found
151             } else {
152 61         2444 my $command_class = $meta->command_get($return);
153 61         384 return $class->initialize_command_class($command_class,%args);
154             }
155             }
156             }
157              
158              
159 10     10   62 no Moose;
  10         14  
  10         75  
160             1;
161              
162             __END__
163              
164             =encoding utf8
165              
166             =head1 NAME
167              
168             MooseX::App - Write user-friendly command line apps with even less suffering
169              
170             =head1 SYNOPSIS
171              
172             In your base class:
173              
174             package MyApp;
175             use MooseX::App qw(Color);
176            
177             option 'global_option' => (
178             is => 'rw',
179             isa => 'Bool',
180             documentation => q[Enable this to do fancy stuff],
181             ); # Global option
182            
183             has 'private' => (
184             is => 'rw',
185             ); # not exposed
186              
187             Write multiple command classes (If you have only a single command class
188             you should use L<MooseX::App::Simple> instead). Packackes in the namespace may be
189             deeply nested.
190              
191             package MyApp::SomeCommand;
192             use MooseX::App::Command; # important (also imports Moose)
193             extends qw(MyApp); # optional, only if you want to use global options from base class
194            
195             # Positional parameter
196             parameter 'some_parameter' => (
197             is => 'rw',
198             isa => 'Str',
199             required => 1,
200             documentation => q[Some parameter that you need to supply],
201             );
202            
203             option 'some_option' => (
204             is => 'rw',
205             isa => 'Int',
206             required => 1,
207             documentation => q[Very important option!],
208             ); # Option
209            
210             sub run {
211             my ($self) = @_;
212             # Do something
213             }
214              
215             And then you need a simple wrapper script (called eg. myapp):
216              
217             #!/usr/bin/env perl
218             use MyApp;
219             MyApp->new_with_command->run;
220              
221             On the command line:
222              
223             bash$ myapp help
224             usage:
225             myapp <command> [long options...]
226             myapp help
227            
228             global options:
229             --global_option Enable this to do fancy stuff [Flag]
230             --help --usage -? Prints this usage information. [Flag]
231            
232             available commands:
233             some_command Description of some command
234             another_command Description of another command
235             help Prints this usage information
236              
237             or
238              
239             bash$ myapp some_command --help
240             usage:
241             myapp some_command <SOME_PARAMETER> [long options...]
242             myapp help
243             myapp some_command --help
244            
245             parameters:
246             some_parameter Some parameter that you need to supply [Required]
247            
248             options:
249             --global_option Enable this to do fancy stuff [Flag]
250             --some_option Very important option! [Int,Required]
251             --help --usage -? Prints this usage information. [Flag]
252              
253             =head1 DESCRIPTION
254              
255             MooseX-App is a highly customisable helper to write user-friendly
256             command line applications without having to worry about most of the annoying
257             things usually involved. Just take any existing L<Moose> class, add a single
258             line (C<use MooseX-App qw(PluginA PluginB ...);>) and create one class
259             for each command in an underlying namespace. Options and positional parameters
260             can be defined as simple L<Moose> accessors using the C<option> and
261             C<parameter> keywords respectively.
262              
263             MooseX-App will then
264              
265             =over
266              
267             =item * Find, load and initialise the command classes (see
268             L<MooseX::App::Simple> for single class/command applications)
269              
270             =item * Create automated help and documentation from modules POD as well as
271             attributes metadata and type constraints
272              
273             =item * Read, encode and validate the command line options and positional
274             parameters entered by the user from @ARGV and %ENV (and possibly prompt
275             the user for additional parameters see L<MooseX::App::Plugin::Term>)
276              
277             =item * Provide helpful error messages if user input cannot be validated
278             (either missing or wrong attributes or Moose type constraints not satisfied)
279             or if the user requests help.
280              
281             =back
282              
283             Commandline options are defined using the 'option' keyword which accepts
284             the same attributes as Moose' 'has' keyword.
285              
286             option 'some_option' => (
287             is => 'rw',
288             isa => 'Str',
289             );
290              
291             This is equivalent to
292              
293             has 'some_option' => (
294             is => 'rw',
295             isa => 'Str',
296             traits => ['AppOption'], # Load extra metaclass
297             cmd_type => 'option', # Set attribute type
298             );
299              
300             Single letter options are treated as flags and may be combined with eachother.
301              
302             Positional parameters are defined with the 'parameter' keyword
303              
304             parameter 'some_option' => (
305             is => 'rw',
306             isa => 'Str',
307             );
308              
309             This is equivalent to
310              
311             has 'some_option' => (
312             is => 'rw',
313             isa => 'Str',
314             traits => ['AppOption'],
315             cmd_type => 'parameter',
316             );
317              
318             All keywords are imported by L<Moosex::App> (in the app base class) and
319             L<MooseX::App::Command> (in the command class) or L<MooseX::App::Simple>
320             (single class application).
321              
322             Furthermore, all options and parameters can also be supplied via %ENV
323              
324             option 'some_option' => (
325             is => 'rw',
326             isa => 'Str',
327             cmd_env => 'SOME_OPTION', # sets the env key
328             );
329              
330             Moose type constraints help MooseX::App to construct helpful error messages
331             and parse @ARGV in a meaningful way. The following type constraints are
332             supported:
333              
334             =over
335              
336             =item * ArrayRef: Specify multiple values ('--opt value1 --opt value2',
337             also see L<app_permute> and L<cmd_split>)
338              
339             =item * HashRef: Specify multiple key value pairs ('--opt key=value --opt
340             key2=value2', also see L<app_permute>)
341              
342             =item * Enum: Display all possibilities
343              
344             =item * Bool: Flags that do not require values
345              
346             =item * Int, Num: Used for proper error messages
347              
348             =back
349              
350             Read the L<Tutorial|MooseX::App::Tutorial> for getting started with a simple
351             MooseX::App command line application.
352              
353             =head1 METHODS
354              
355             =head2 new_with_command
356              
357             my $myapp_command = MyApp->new_with_command();
358              
359             This constructor reads the command line arguments and tries to create a
360             command class instance. If it fails it returns a
361             L<MooseX::App::Message::Envelope> object holding an error message.
362              
363             You can pass a hash of default/fallback params to new_with_command
364              
365             my $obj = MyApp->new_with_command(%default);
366              
367             Optionally you can pass a custom ARGV to this constructor
368              
369             my $obj = MyApp->new_with_command( ARGV => \@myARGV );
370              
371             However, if you do so you must take care of propper @ARGV encoding yourself.
372              
373             =head2 initialize_command_class
374              
375             my $obj = MyApp->initialize_command_class($command_name,%default);
376              
377             Helper method to instantiate the command class for the given command.
378              
379             =head1 GLOBAL OPTIONS
380              
381             These options may be used to alter the default behaviour of MooseX-App.
382              
383             =head2 app_base
384              
385             app_base 'my_script'; # Defaults to $0
386              
387             Usually MooseX::App will take the name of the calling wrapper script to
388             construct the program name in various help messages. This name can
389             be changed via the app_base function.
390              
391             =head2 app_fuzzy
392              
393             app_fuzzy 1; # default
394             OR
395             app_fuzzy 0;
396              
397             Enables fuzzy matching of commands and attributes. Is turned on by default.
398              
399             =head2 app_strict
400              
401             app_strict 0; # default
402             OR
403             app_strict 1;
404              
405             If strict is enabled the program will terminate with an error message if
406             superfluous/unknown positional parameters are supplied. If disabled all
407             extra parameters will be copied to the L<extra_argv> attribute. Unknown
408             options (with leading dashes) will always yield an error message.
409              
410             The command_strict config in the command classes allows one to set this option
411             individually for each command in the respective command class.
412              
413             =head2 app_prefer_commandline
414              
415             app_prefer_commandline 0; # default
416             or
417             app_prefer_commandline 1;
418              
419             Specifies if parameters/options supplied via @ARGV,%ENV should take precedence
420             over arguments passed directly to new_with_command.
421              
422             =head2 app_namespace
423              
424             app_namespace 'MyApp::Commands', 'YourApp::MoreCommands';
425             OR
426             app_namespace();
427              
428             Usually MooseX::App will take the package name of the base class as the
429             namespace for commands. This namespace can be changed and you can add
430             multiple extra namespaces.
431              
432             If app_namespace is called with no arguments then autoloading of command
433             classes will be disabled entirely.
434              
435             =head2 app_exclude
436              
437             app_exclude 'MyApp::Commands::Roles','MyApp::Commands::Utils';
438              
439             A sub namespace included via app_namespace (or the default behaviour) can
440             be excluded using app_exclude.
441              
442             =head2 app_command_name
443              
444             app_command_name {
445             my ($package_short,$package_full) = @_;
446             # munge package name;
447             return $command_name;
448             };
449              
450             This coderef can be used to control how autoloaded package names should be
451             translated to command names. If this command returns nothing the respective
452             command class will be skipped and not loaded.
453              
454             =head2 app_command_register
455              
456             app_command_register
457             do => 'MyApp::Commands::DoSomething',
458             undo => 'MyApp::Commands::UndoSomething';
459              
460             This keyword can be used to register additional commands. Especially
461             useful in conjunction with app_namespace and disabled autoloading.
462              
463             =head2 app_description
464              
465             app_description qq[Description text];
466              
467             Set the app description text. If not set this information will be taken from
468             the Pod DESCRIPTION or OVERVIEW sections. (see command_description to set
469             usage per command)
470              
471             =head2 app_usage
472              
473             app_usage qq[myapp --option ...];
474              
475             Set a custom usage text. If not set this will be taken from the Pod SYNOPSIS
476             or USAGE section. If both sections are not available, the usage information
477             will be autogenerated. (see command_usage to set usage per command)
478              
479             =head2 app_permute
480              
481             app_permute 0; # default
482             OR
483             app_permute 1;
484              
485             Allows one to specify multiple values with one key. So instead of writing
486             C<--list element1 --list element2 --list element3> one might write
487             C<--list element1 element2 element3> for ArrayRef elements. HashRef elements
488             may be expressed as <--hash key=value key2=value2>.
489              
490             =head1 GLOBAL ATTRIBUTES
491              
492             All MooseX::App classes will have two extra attributes
493              
494             =head2 extra_argv
495              
496             Carries all parameters from @ARGV that were not consumed (only if app_strict
497             is turned off, otherwise superfluous parameters will raise an exception).
498              
499             =head2 help_flag
500              
501             Help flag that is set when help was requested.
502              
503             =head1 ATTRIBUTE OPTIONS
504              
505             Options and parameters accept extra attributes for customisation:
506              
507             =over
508              
509             =item * cmd_tags - Extra tags (as used by the help)
510              
511             =item * cmd_flag - Override option/parameter name
512              
513             =item * cmd_aliases - Additional option/parameter name aliases
514              
515             =item * cmd_split - Split values into ArrayRefs on this token
516              
517             =item * cmd_position - Specify option/parameter order in help
518              
519             =item * cmd_env - Read options/parameters from %ENV
520              
521             =item * cmd_count - Value of option equals to number of occurrences in @ARGV
522              
523             =item * cmd_negate - Adds an option to negate boolean flags
524              
525             =back
526              
527             Refer to L<MooseX::App::Meta::Role::Attribute::Option> for detailed
528             documentation.
529              
530             =head1 METADATA
531              
532             MooseX::App will use your class metadata and POD to construct the commands and
533             helpful error- or usage-messages. These bits of information are utilised
534             and should be provided if possible:
535              
536             =over
537              
538             =item * Package names
539              
540             =item * L<required> options for Moose attributes
541              
542             =item * L<documentation> options for Moose attributes
543              
544             =item * Moose type constraints (Bool, ArrayRef, HashRef, Int, Num, and Enum)
545              
546             =item * Documentation set via app_description, app_usage,
547             command_short_description, command_long_description and command_usage
548              
549             =item * POD (NAME, ABSTRACT, DESCRIPTION, USAGE, SYNOPSIS, OVERVIEW,
550             COPYRIGHT, LICENSE, COPYRIGHT AND LICENSE, AUTHOR and AUTHORS sections)
551              
552             =item * Dzil ABSTRACT tag if no POD is available yet
553              
554             =back
555              
556             =head1 PLUGINS
557              
558             The behaviour of MooseX-App can be customised with plugins. To load a
559             plugin just pass a list of plugin names after the C<use MooseX-App> statement.
560             (Attention: order sometimes matters)
561              
562             use MooseX::App qw(PluginA PluginB);
563              
564             Currently the following plugins are shipped with MooseX::App
565              
566             =over
567              
568             =item * L<MooseX::App::Plugin::BashCompletion>
569              
570             Adds a command that generates a bash completion script for your application.
571             See third party L<MooseX::App::Plugin::ZshCompletion> for Z shell completion.
572              
573             =item * L<MooseX::App::Plugin::Color>
574              
575             Colorful output for your MooseX::App applications.
576              
577             =item * L<MooseX::App::Plugin::Config>
578              
579             Config files for MooseX::App applications.
580              
581             =item * L<MooseX::App::Plugin::ConfigHome>
582              
583             Try to find config files in users home directory.
584              
585             =item * L<MooseX::App::Plugin::Term>
586              
587             Prompt user for options and parameters that were not provided via options or
588             params. Prompt offers basic editing capabilities and non-persistent history.
589              
590             =item * L<MooseX::App::Plugin::Typo>
591              
592             Handle typos in command names and provide suggestions.
593              
594             =item * L<MooseX::App::Plugin::Version>
595              
596             Adds a command to display the version and license of your application.
597              
598             =item * L<MooseX::App::Plugin::Man>
599              
600             Display full manpage of application and commands.
601              
602             =item * L<MooseX::App::Plugin::MutexGroup>
603              
604             Allow for mutally exclusive options.
605              
606             =item * L<MooseX::App::Plugin::Depends>
607              
608             Adds dependent options.
609              
610             =back
611              
612             Refer to L<Writing MooseX-App Plugins|MooseX::App::WritingPlugins>
613             for documentation on how to create your own plugins.
614              
615             =head1 CAVEATS & KNOWN BUGS
616              
617             Startup time may be an issue - escpecially if you load many plugins. If you do
618             not require the functionality of plugins and ability for fine grained
619             customisation (or Moose for that matter) then you should probably
620             use L<MooX::Options> or L<MooX::Cmd>.
621              
622             In some cases - especially when using non-standard class inheritance - you may
623             end up with command classes lacking the help attribute. In this case you need
624             to include the following line in your base class or command classes.
625              
626             with qw(MooseX::App::Role::Common);
627              
628             When manually registering command classes (eg. via app_command_register) in
629             multiple base classes with different sets of plugins (why would you ever want
630             to do that?), then meta attributes may lack some attribute metaclasses. In
631             this case you need to load the missing attribute traits explicitly:
632              
633             option 'argument' => (
634             depends => 'otherargument',
635             trait => ['MooseX::App::Plugin::Depends::Meta::Attribute'], # load trait
636             );
637              
638             =head1 SEE ALSO
639              
640             Read the L<Tutorial|MooseX::App::Tutorial> for getting started with a simple
641             MooseX::App command line application.
642              
643             For alternatives you can check out
644              
645             L<MooseX::App::Cmd>, L<MooseX::Getopt>, L<MooX::Options>, L<MooX::Cmd>and L<App::Cmd>
646              
647             =head1 SUPPORT
648              
649             Please report any bugs or feature requests via
650             L<https://github.com/maros/MooseX-App/issues/new>. I will be notified, and
651             then you'll automatically be notified of progress on your report as I make
652             changes.
653              
654             =head1 AUTHOR
655              
656             MaroÅ¡ Kollár
657             CPAN ID: MAROS
658             maros [at] k-1.com
659            
660             http://www.k-1.com
661              
662             =head1 CONTRIBUTORS
663              
664             Special thanks to all contributors.
665              
666             In no particular order: Andrew Jones, George Hartzell, Steve Nolte,
667             Michael G, Thomas Klausner, Yanick Champoux, Edward Baudrez, David Golden,
668             J.R. Mash, Thilo Fester, Gregor Herrmann, Sergey Romanov, Sawyer X, Roman F.,
669             Hunter McMillen, Maik Hentsche, Alexander Stoddard, Marc Logghe, Tina Müller,
670             Lisa Hare
671              
672             You are more than welcome to contribute to MooseX-App. Please have a look
673             at the L<https://github.com/maros/MooseX-App/issues?q=is%3Aissue+is%3Aopen+label%3AWishlist>
674             list of open wishlist issues for ideas.
675              
676             =head1 COPYRIGHT
677              
678             MooseX::App is Copyright (c) 2012-17 MaroÅ¡ Kollár.
679              
680             This library is free software and may be distributed under the same terms as
681             perl itself. The full text of the licence can be found in the LICENCE file
682             included with this module.
683              
684             =cut