Actually, App::CLI is a really powerful module having central idea similar to the dispatcher in many web application frameworks such as Rails. When we are trying to create yet another handy CLI tool for our daily working, here is a recommended architecture to use App::CLI:
MyApp
-> The kernel of our new tool
MyApp::*
-> All other module forming the mechanism
-> of our new tool, e.g. MyApp::Config
-> for loading configuration
MyApp::Command
-> The dispatcher serving the invoking commands
MyApp::Command::*
-> All subcommands invoked by MyApp::Command
MyApp::Command::*::*
-> All subsubcommands invoked by specific subcommand
MyApp::Command::*::*::*
-> subsubsubcommands *god*
After deciding what command we need to implement, we only have two things should be done. The first is making
MyApp::Command
be able to dispatch. What we need to do is only add use base qw(App::CLI);
into it. Now, in MyApp.pm or our script, where we want to invoke command, we can just say
MyApp::Command->dispatch();
MyApp::Command
would automatically use @ARGV to determine what (sub)command it should invoke and find its package to *require*. For example, if we type $ myapp list user --sort age
in terminal, the @ARGV
would be qw(list user --sort age)
, and MyApp::Command
would require MyApp::Command::List::User
, create its instance as $cmd
, assign $cmd->{sort}
as 'age', finally invoke $cmd->run_command()
.So, The second thing, most of we need to effort, is to implement our (sub)commands. it's a bit like writing Controllers of Rails. If the URL matches /foo/bar/:id, the Action
#list()
of Controller Foo::Bar
would handle the request, and @id
would be assign value. If users type $ myapp list nickname --name /mark/
MyApp::Command::List::Nickname
would handle the command and $instance->{name}
would be assign string /mark/. For this case, we can write as below.
package MyApp::Command::List;
use base qw(App::CLI::Command);
use constant subcommands => qw(User Nickname);
use constant options => (
"h|help" => "help",
);
sub run {
my ($self, @args) = @_;
if ($self->{help}){
# output PODs when $ myapp list --help
}
# do something when user type like
# $ myapp list arg1 arg2 --opt1 --opt2 opt_arg2
# and arg1 doesn't equal to User or Nickname
}
package MyApp::Command::List::Nickname;
use base qw(App::CLI::Command);
use constant options => (
"name=s" => "name",
);
sub run {
my ($self, @args) = @_;
$name = $self->{name} #=> /mark/
# query the data base via condition /mark/
}
It completes. Note the we can specify all possible subcommands in constant subcommands of and give all possible options in constant options to all (sub)commands and just implement &run() the (sub)command would be done. And finally, note we can cascading subcommands infinitely. That is the core power of this module starting from v0.2.
Enjoy.
No comments:
Post a Comment