File Coverage

blib/lib/App/CLI/Extension.pm
Criterion Covered Total %
statement 60 61 98.3
branch 5 8 62.5
condition n/a
subroutine 11 11 100.0
pod 2 3 66.6
total 78 83 93.9


line stmt bran cond sub pod time code
1             package App::CLI::Extension;
2              
3             =pod
4              
5             =head1 NAME
6              
7             App::CLI::Extension - for App::CLI extension module
8              
9             =head1 VERSION
10              
11             1.421
12              
13             =head1 SYNOPSIS
14              
15             # MyApp.pm
16             package MyApp;
17            
18             use strict;
19             use base qw(App::CLI::Extension);
20            
21             # extension method
22             # load App::CLI::Plugin::Foo, MyApp::Plugin::Bar
23             __PACKAGE__->load_plugins(qw(Foo +MyApp::Plugin::Bar));
24            
25             # extension method
26             __PACKAGE__->config( name => "kurt" );
27            
28             1;
29            
30             # MyApp/Hello.pm
31             package MyApp::Hello;
32            
33             use strict;
34             use base qw(App::CLI::Command);
35             use constant options => ("age=i" => "age");
36            
37             sub run {
38            
39             my($self, @args) = @_;
40             # config - App::CLI::Extension extension method(App::CLI::Extension::Component::Config)
41             print "Hello! my name is " . $self->config->{name} . "\n";
42             print "age is " . "$self->{age}\n";
43             }
44            
45             # myapp
46             #!/usr/bin/perl
47            
48             use strict;
49             use MyApp;
50            
51             MyApp->dispatch;
52            
53             # execute
54             [kurt@localhost ~] myapp hello --age=27
55             Hello! my name is kurt
56             age is 27
57              
58             =head1 DESCRIPTION
59              
60             The expansion module which added plug in, initial setting mechanism to App::CLI
61              
62             App::CLI::Extension::Component::* modules is automatic, and it is done require
63              
64             (It is now Config and Stash is automatic, and it is done require)
65              
66             =head2 RUN PHASE
67              
68             +----------------------+
69             | ** run_method ** |
70             | +----------------+ |
71             | | setup phase | |
72             | +----------------+ |
73             | || |
74             | +----------------+ |
75             | | prerun phase | |
76             | +----------------+ |
77             | || |
78             | +----------------+ | if anything error... +----------------+
79             | | run phase | | ======================> | fail phase |
80             | +----------------+ | +----------------+
81             | || | set exit_value(default: 255)
82             | +----------------+ | |
83             | | postrun phase | | |
84             | +----------------+ | |
85             +----------------------+ |
86             | |
87             | |
88             +----------------+ |
89             | finish phase | <================================== +
90             +----------------+
91             |
92             exit
93              
94             =head2 SETUP
95              
96             If you define initialization and initialization of each plug-in
97              
98             =head2 PRERUN
99              
100             If you want the process to run before you run something in the main processing
101              
102             =head2 RUN
103              
104             Process to define the main(require). however, $self->finished non-zero if not executed
105              
106             =head2 POSTRUN
107              
108             After the run method to execute. however, $self->finished non-zero if not executed
109              
110             =head2 FINISH
111              
112             At the end of all processing
113              
114             =head2 FAIL
115              
116             setup/prerun/run/postrun/finish processing to be executed if an exception occurs somewhere in the phase error
117              
118             $self->e is the App::CLI::Extension::Exception or Error::Simple instance is set
119              
120             =cut
121              
122 18     18   528403 use strict;
  18         50  
  18         582  
123 18     18   96 use warnings;
  18         36  
  18         595  
124 18     18   86 use base qw(App::CLI Class::Accessor::Grouped);
  18         42  
  18         22074  
125 18     18   706515 use 5.008000;
  18         70  
  18         1174  
126 18     18   19570 use UNIVERSAL::require;
  18         33644  
  18         198  
127              
128             our $VERSION = '1.421';
129             our @COMPONENTS = qw(
130             Config
131             ErrorHandler
132             InstallCallback
133             OriginalArgv
134             Stash
135             RunCommand
136             );
137              
138             __PACKAGE__->mk_group_accessors(inherited => "_config", "_components", "_orig_argv", "_plugins");
139             __PACKAGE__->_config({});
140             __PACKAGE__->_plugins([]);
141              
142             =pod
143              
144             =head1 METHOD
145              
146             =cut
147              
148             sub import {
149              
150 18     18   1166 my $class = shift;
151 18         39 my @loaded_components;
152 18         45 foreach my $component (@COMPONENTS) {
153 108         455 $component = sprintf "%s::Component::%s", __PACKAGE__, $component;
154 108 50       636 $component->require or die "load component error: $UNIVERSAL::require::ERROR";
155 108         2095 $component->import;
156 108         573 push @loaded_components, $component;
157             }
158 18         645 $class->_components(\@loaded_components);
159             }
160              
161             sub dispatch {
162              
163 16     16 0 2827 my $class = shift;
164             # save original argv
165 16         227 my @argv = @ARGV;
166 16         1414 $class->_orig_argv(\@argv);
167 16         1171 my $cmd = $class->prepare(@_);
168 16         137549 $cmd->subcommand;
169             {
170 18     18   4422 no strict "refs"; ## no critic
  18         42  
  18         631  
  16         156  
171 18     18   110 no warnings "uninitialized"; ## adhoc
  18         34  
  18         11752  
172 16         50 my $pkg = ref($cmd);
173             # component and plugin set value
174 16         38 unshift @{"$pkg\::ISA"}, @{$class->_components};
  16         83  
  16         746  
175 16 100       2619 if (scalar(@{$class->_plugins}) != 0) {
  16         540  
176 12         253 unshift @{"$pkg\::ISA"}, @{$class->_plugins};
  12         52  
  12         345  
177             }
178 16         1520 $cmd->config($class->_config);
179 16         1567 $cmd->orig_argv($class->_orig_argv);
180             }
181 16         501 $cmd->run_command(@ARGV);
182             }
183              
184              
185             =pod
186              
187             =head2 load_plugins
188              
189             auto load and require plugin modules
190              
191             Example
192              
193             # MyApp.pm
194             # MyApp::Plugin::GoodMorning and App::CLI::Plugin::Config::YAML::Syck require
195             __PACKAGE__->load_plugins(qw(+MyApp::Plugin::GoodMorning Config::YAML::Syck));
196            
197             # MyApp/Plugin/GoodMorning.pm
198             package MyApp::Plugin::GoodMorning;
199            
200             use strict;
201            
202             sub good_morning {
203            
204             my $self = shift;
205             print "Good monring!\n";
206             }
207            
208             # MyApp/Hello.pm
209             package MyApp::Hello;
210            
211             use strict;
212             use base qw(App::CLI::Command);
213            
214             sub run {
215            
216             my($self, @args) = @_;
217             $self->good_morning;
218             }
219            
220             # myapp
221             #!/usr/bin/perl
222            
223             use strict;
224             use MyApp;
225            
226             MyApp->dispatch;
227            
228             # execute
229             [kurt@localhost ~] myapp hello
230             Good morning!
231              
232             =cut
233              
234             sub load_plugins {
235              
236 13     13 1 1832 my($class, @load_plugins) = @_;
237              
238 13         28 my @loaded_plugins = @{$class->_plugins};
  13         412  
239 13         693 foreach my $plugin(@load_plugins){
240              
241 20 50       108 if ($plugin =~ /^\+/) {
242 20         79 $plugin =~ s/^\+//;
243             } else {
244 0         0 $plugin = "App::CLI::Plugin::$plugin";
245             }
246 20 50       125 $plugin->require or die "plugin load error: $UNIVERSAL::require::ERROR";
247 20         2790 $plugin->import;
248 20         112 push @loaded_plugins, $plugin;
249             }
250              
251 13         412 $class->_plugins(\@loaded_plugins);
252             }
253              
254             =pod
255              
256             =head2 config
257              
258             configuration method
259              
260             Example
261              
262             # MyApp.pm
263             __PACKAGE__->config(
264             name => "kurt",
265             favorite_group => "nirvana",
266             favorite_song => ["Lounge Act", "Negative Creep", "Radio Friendly Unit Shifter", "You Know You're Right"]
267             );
268            
269             # MyApp/Hello.pm
270             package MyApp::Hello;
271            
272             use strict;
273             use base qw(App::CLI::Command);
274            
275             sub run {
276            
277             my($self, @args) = @_;
278             print "My name is " . $self->config->{name} . "\n";
279             print "My favorite group is " . $self->config->{favorite_group} . "\n";
280             print "My favorite song is " . join(",", @{$self->config->{favorite_song}});
281             print " and Smells Like Teen Spirit\n"
282             }
283            
284             # myapp
285             #!/usr/bin/perl
286            
287             use strict;
288             use MyApp;
289            
290             MyApp->dispatch;
291            
292             # execute
293             [kurt@localhost ~] myapp hello
294             My name is kurt
295             My favorite group is nirvana
296             My favorite song is Lounge Act,Negative Creep,Radio Friendly Unit Shifter,You Know You're Right and Smells Like Teen Spirit
297              
298             =cut
299              
300             sub config {
301              
302 7     7 1 172 my($class, %config) = @_;
303 7         196 $class->_config(\%config);
304 7         243 return $class->_config;
305             }
306              
307             =head1 COMPONENT METHOD
308              
309             =head2 argv0
310              
311             my script name
312              
313             Example:
314              
315             # MyApp/Hello.pm
316             package MyApp::Hello;
317             use strict;
318             use feature ":5.10.0";
319             use base qw(App::CLI::Command);
320            
321             sub run {
322            
323             my($self, @args) = @_;
324             say "my script name is " . $self->argv0;
325             }
326            
327             1;
328              
329             # execute
330             [kurt@localhost ~] myapp hello
331             my script name is myapp
332              
333             =head2 full_argv0
334              
335             my script fullname
336              
337             Example:
338              
339             # MyApp/Hello.pm
340             package MyApp::Hello;
341             use strict;
342             use feature ":5.10.0";
343             use base qw(App::CLI::Command);
344            
345             sub run {
346            
347             my($self, @args) = @_;
348             say "my script full name is " . $self->full_argv0;
349             }
350            
351             1;
352            
353             # execute
354             [kurt@localhost ~] myapp hello
355             my script name is /home/kurt/myapp
356              
357             =head2 cmdline
358              
359             my execute cmdline string
360              
361             Example:
362              
363             # MyApp/Hello.pm
364             package MyApp::Hello;
365             use strict;
366             use feature ":5.10.0";
367             use base qw(App::CLI::Command);
368            
369             sub run {
370            
371             my($self, @args) = @_;
372             say "my script cmdline is [" . $self->cmdline . "]";
373             }
374            
375             1;
376            
377             # execute
378             [kurt@localhost ~] myapp hello --verbose --num=10
379             my script cmdline is [/home/kurt/myapp hello --verbose --num=10]
380              
381             =head2 orig_argv
382              
383             my execute script original argv
384              
385             Example:
386              
387             # MyApp/Hello.pm
388             package MyApp::Hello;
389             use strict;
390             use feature ":5.10.0";
391             use base qw(App::CLI::Command);
392            
393             sub run {
394            
395             my($self, @args) = @_;
396             say "my script original argv is [" join(", ", @{$self->orig_argv}) . "]";
397             }
398            
399             1;
400            
401             # execute
402             [kurt@localhost ~] myapp hello --verbose --num=10
403             my script original argv is [hello,--verbose, --num=10]
404              
405             =head2 stash
406              
407             like global variable in Command package
408              
409             Example:
410            
411             # MyApp/Hello.pm
412             package MyApp::Hello;
413             use strict;
414             use feature ":5.10.0";
415             use base qw(App::CLI::Command);
416            
417             sub run {
418            
419             my($self, @args) = @_;
420             $self->stash->{name} = "kurt";
421             say "stash value: " . $self->stash->{name};
422             }
423            
424             1;
425              
426             =head2 new_callback
427              
428             install new callback phase
429              
430             Example:
431              
432             $self->new_callback("some_phase");
433             # registered callback argument pattern
434             $self->new_callback("some_phase", sub { $self = shift; "anything to do..." });
435              
436             =head2 add_callback
437              
438             install callback
439              
440             Example:
441              
442             $self->add_callback("some_phase", sub { my $self = shift; say "some_phase method No.1" });
443             $self->add_callback("some_phase", sub { my $self = shift; say "some_phase method No.1" });
444             $self->add_callback("any_phase", sub {
445             my($self, @args) = @_;
446             say "any_phase args: @args";
447             });
448              
449             =cut
450              
451             =head2 exec_callback
452              
453             execute callback
454              
455             Example:
456              
457             $self->execute_callback("some_phase");
458             # some_phase method method No.1
459             # some_phase method method No.2
460            
461             $self->execute_callback("any_phase", qw(one two three));
462             # any_phase args: one two three
463              
464             =head2 exists_callback
465              
466             exists callback check
467              
468             Example:
469              
470             if ($self->exists_callback("some_phase")) {
471             $self->exec_callback("some_phase");
472             } else {
473             die "some_phase is not exists callback phase";
474             }
475              
476             =head2 exit_value
477              
478             set exit value
479              
480             Example:
481              
482             # program exit value is 1(ex. echo $?)
483             $self->exit_value(1);
484              
485             =head2 finished
486              
487             setup or prepare phase and 1 set, run and postrun phase will not run. default 0
488              
489             Example:
490              
491             # MyApp/Hello.pm
492             package MyApp::Hello;
493            
494             use strict;
495             use base qw(App::CLI::Command);
496            
497             sub prerun {
498            
499             my($self, @args) = @_;
500             $self->finished(1);
501             }
502            
503             # non execute
504             sub run {
505            
506             my($self, @args) = @_;
507             print "hello\n";
508             }
509              
510             =head2 throw
511              
512             raises an exception, fail phase transitions
513              
514             Example:
515              
516             # MyApp/Hello.pm
517             package MyApp::Hello;
518            
519             use strict;
520             use base qw(App::CLI::Command);
521            
522             sub run {
523            
524             my($self, @args) = @_;
525             my $file = "/path/to/file";
526             open my $fh, "< $file" or $self->throw("can not open file:$file");
527             while ( my $line = <$fh> ) {
528             chomp $line;
529             print "$line\n";
530             }
531             close $fh;
532             }
533            
534             # transitions fail phase method
535             sub fail {
536            
537             my($self, @args) = @_;
538             # e is App:CLI::Extension::Exception instance
539             printf "ERROR: %s", $self->e;
540             printf "STACKTRACE: %s", $self->e->stacktrace;
541             }
542            
543             # myapp
544             #!/usr/bin/perl
545            
546             use strict;
547             use MyApp;
548            
549             MyApp->dispatch;
550            
551             # execute
552             [kurt@localhost ~] myapp hello
553             ERROR: can not open file:/path/to/file at lib/MyApp/Throw.pm line 10.
554             STACKTRACE: can not open file:/path/to/file at lib/MyApp/Throw.pm line 10
555             MyApp::Throw::run('MyApp::Throw=HASH(0x81bd6b4)') called at /usr/lib/perl5/site_perl/5.8.8/App/CLI/Extension/Component/RunCommand.pm line 36
556             App::CLI::Extension::Component::RunCommand::run_command('MyApp::Throw=HASH(0x81bd6b4)') called at /usr/lib/perl5/site_perl/5.8.8/App/CLI/Extension.pm line 177
557             App::CLI::Extension::dispatch('MyApp') called at ./myapp line 7
558              
559             when you run throw method, App::CLI::Extension::Exception instance that $self->e is set to.
560              
561             App::CLI::Extension::Exception is the Error::Simple is inherited. refer to the to documentation of C
562              
563             throw method without running CORE::die if you run the $self->e is the Error::Simple instance will be set
564              
565             =head2 e
566              
567             App::CLI::Extension::Exception or Error::Simple instance. There is a ready to use, fail phase only
568              
569             =head1 RUN PHASE METHOD
570              
571             =head2 setup
572              
573             =head2 prerun
574              
575             =head2 postrun
576              
577             =head2 finish
578              
579             program last phase. By default, the exit will be executed automatically, exit if you do not want the APPCLI_NON_EXIT environ valiable how do I set the (value is whatever)
580              
581             =head2 fail
582              
583             error phase. default exit value is 255. if you want to change exit_value, see exit_value manual
584              
585             =cut
586              
587             1;
588              
589             __END__