MooX::Cmd(3pm)

πŸ“¦
version 0.017 Β· 5 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu questing

πŸ’– NAME πŸ’–

MooX::Cmd β€” the way to make command-organized CLI apps in a Moo style, bestie. Like, actually easy. ✨

(It lives in the User Contributed Perl Documentation section, it's a 3pm page, and yes this is the whole glow-up. iykyk.)


πŸŽ€ SYNOPSIS πŸŽ€

okay so here's the whole vibe in one block, no cap:

package MyApp;

use Moo;
use MooX::Cmd;

sub execute {
  my ( $self, $args_ref, $chain_ref ) = @_;
  my @extra_argv = @{$args_ref};
  my @chain = @{$chain_ref} # in this case only ( $myapp )
                            # where $myapp == $self
}

1;

package MyApp::Cmd::Command;
# for "myapp command"

use Moo;
use MooX::Cmd;

# gets executed on "myapp command" but not on "myapp command command"
# there MyApp::Cmd::Command still gets instantiated and for the chain
sub execute {
  my ( $self, $args_ref, $chain_ref ) = @_;
  my @chain = @{$chain_ref} # in this case ( $myapp, $myapp_cmd_command )
                            # where $myapp_cmd_command == $self
}

1;

package MyApp::Cmd::Command::Cmd::Command;
# for "myapp command command"

use Moo;
use MooX::Cmd;

# gets executed on "myapp command command" and will not get instantiated
# on "myapp command" cause it doesnt appear in the chain there
sub execute {
  my ( $self, $args_ref, $chain_ref ) = @_;
  my @chain = @{$chain_ref} # in this case ( $myapp, $myapp_cmd_command,
                            # $myapp_cmd_command_cmd_command )
                            # where $myapp_cmd_command_cmd_command == $self
}

package MyZapp;

use Moo;
use MooX::Cmd execute_from_new => 0;

sub execute {
  my ( $self ) = @_;
  my @extra_argv = @{$self->command_args};
  my @chain = @{$self->command_chain} # in this case only ( $myzapp )
                            # where $myzapp == $self
}

1;

package MyZapp::Cmd::Command;
# for "myapp command"

use Moo;
use MooX::Cmd execute_from_new => 0;

# gets executed on "myapp command" but not on "myapp command command"
# there MyApp::Cmd::Command still gets instantiated and for the chain
sub execute {
  my ( $self ) = @_;
  my @extra_argv = @{$self->command_args};
  my @chain = @{$self->command_chain} # in this case ( $myzapp, $myzapp_cmd_command )
                            # where $myzapp_cmd_command == $self
}

1;
package main;

use MyApp;

MyZapp->new_with_cmd->execute();
MyApp->new_with_cmd;

1;

like. look at that. iconic. πŸ’…


✨ DESCRIPTION ✨

MooX::Cmd makes writing command line utilities so much easier β€” it handles commands and subcommands and subsubcommands and on and on, forever basically. πŸ¦‹

These commands form a tree, and that tree is mirrored in the package structure. And the way it works? On invocation, every single command along the path through the tree β€” starting from the toplevel command all the way down to the most specific one β€” gets instanciated. yes, all of them, even the ones whose execute doesn't run. she contains multitudes. πŸ‘‘

Every command needs an execute function, and it takes three parameters:

  • self β€” a reference to the specific MooX::Cmd object that is executing. that's you, babe.
  • args β€” an ArrayRef of arguments passed to self. this only encompasses arguments of the most specific (read: right-most) command. so like, the innermost one. the main character. πŸ’…
  • chain β€” an ArrayRef of MooX::Cmds along the tree path, as specified on the command line.
⚠️ Note: only the execute function of the most specific command is executed. everyone else is just... there for the vibes (and for the chain). 🫢

🌸 MooX::Cmd Attributes 🌸

Each command has some attributes set by MooX::Cmd during initialization:

  • command_chain β€” same as the chain argument to execute.
  • command_name β€” TODO
  • command_commands β€” TODO
  • command_args β€” TODO
  • command_base β€” TODO

(yes bestie, those are literally TODO. not me being lazy, that's just what the page says. πŸ’€)


πŸ’œ Examples πŸ’œ

🌈 A Single Toplevel Command 🌈

#!/usr/bin/env perl
package MyApp;
use Moo;
use MooX::Cmd;

sub execute {
  my ($self,$args,$chain) = @_;
  printf("%s.execute(\$self,[%s],[%s])\n",
    ref($self),                       # which command is executing?
    join(", ", @$args ),              # what where the arguments?
    join(", ", map { ref } @$chain)   # what's in the command chain?
  );
}

package main;
MyApp->new_with_cmd();

Some sample invocations:

 $ ./MyApp.pl
 MyApp.execute($self,[],[MyApp])

 $./MyApp.pl --opt1
 MyApp.execute($self,[--opt1],[MyApp])

 $ ./MyApp.pl --opt1 arg
 MyApp.execute($self,[--opt1, arg],[MyApp])

🌸 Toplevel Command with Subcommand 🌸

#!/usr/bin/env perl
# let's define a base class containing our generic execute
# function to save some typing...
package CmdBase;
use Moo;

sub execute {
  my ($self,$args,$chain) = @_;
  printf("%s.execute(\$self,[%s],[%s])\n",
    ref($self),
    join(", ", @$args ),
    join(", ", map { ref } @$chain)
  );
}

package MyApp;
# toplevel command/app
use Moo;
use MooX::Cmd;
extends 'CmdBase';

package MyApp::Cmd::frobnicate;
# can be called via ./MyApp.pl frobnicate
use Moo;
use MooX::Cmd;
extends 'CmdBase';

package main;
MyApp->new_with_cmd();

And some sample invocations:

 $ ./MyApp.pl frobnicate
 MyApp::Cmd::frobnicate.execute($self,[],[MyApp, MyApp::Cmd::frobnicate])

As you can see the chain contains our toplevel command object and then the specififc one.

 $ ./MyApp.pl frobnicate arg1
 MyApp::Cmd::frobnicate.execute($self,[arg1],[MyApp, MyApp::Cmd::frobnicate])

Arguments are passed via the args parameter.

 $ ./MyApp.pl some --stuff frobnicate arg1
 MyApp::Cmd::frobnicate.execute($self,[arg1],[MyApp, MyApp::Cmd::frobnicate])

Arguments to commands higher in the tree get ignored if they don't match a command. πŸ’€ sorry to those args, they simply did not make the cut.

πŸ¦‹ Access Toplevel Attributes via Chain πŸ¦‹

#!/usr/bin/env perl
package CmdBase;
use Moo;

sub execute {
  my ($self,$args,$chain) = @_;
  printf("%s.execute(\$self,[%s],[%s])\n",
    ref($self),
    join(", ", @$args ),
    join(", ", map { ref } @$chain)
  );
}

package MyApp;
use Moo;
use MooX::Cmd;
extends 'CmdBase';

has somevar => ( is => 'ro', default => 'someval' );

package MyApp::Cmd::frobnicate;
use Moo;
use MooX::Cmd;
extends 'CmdBase';

around execute => sub {
  my ($orig,$self,$args,$chain) = @_;
  $self->$orig($args,$chain);
  # we can access toplevel attributes via the chain...
  printf("MyApp->somevar = '%s'\n", $chain->[0]->somevar);
};

package main;
MyApp->new_with_cmd();

A sample invocation:

 $ ./MyApp.pl some --stuff frobnicate arg1
 MyApp::Cmd::frobnicate.execute($self,[arg1],[MyApp, MyApp::Cmd::frobnicate])
 MyApp->somevar = someval

see? reaching back up the chain to grab the toplevel's attribute. obsessed. 😭


πŸ’– MooX::Options integration πŸ’–

You can integrate MooX::Options simply by using it and declaring some options, like so:

#!/usr/bin/env perl
package MyApp;
use Moo;
use MooX::Cmd;
use MooX::Options;

option debug => ( is => 'ro' );

sub execute {
  my ($self,$args,$chain) = @_;
  print "debugging enabled!\n" if $self->{debug};
}

package main;
MyApp->new_with_cmd();

A sample invocation:

 $ ./MyApp-Options.pl --debug
 debugging enabled!
⚠️ Note, that each command and subcommand has its own options., so options are parsed for the specific context and used for the instantiation:
 $ ./MyApp.pl --argformyapp command --argformyappcmdcommand ...

(the double period in "options.," is exactly how the original wrote it and I refuse to edit her. πŸ’…)


🫢 SUPPORT 🫢

Repository

 http://github.com/Getty/p5-moox-cmd
 Pull request and additional contributors are welcome

Issue Tracker

 http://github.com/Getty/p5-moox-cmd/issues
 http://rt.cpan.org/NoAuth/Bugs.html?Dist=MooX-Cmd
 bug-moox-cmd at rt.cpan.org

πŸ‘‘ THANKS πŸ‘‘

  • Lukas Mai (mauke), Toby Inkster (tobyink) β€” Gave some helpful advice for solving difficult issues
  • Celogeek San β€” Integration into MooX::Options for better help messages and suit team play
  • Torsten Raudssus (Getty) β€” did the initial work and brought it to CPAN

the founding mothers and fathers of this module fr fr πŸ₯ΉπŸ’


Copyright 2012-2013 Torsten Raudssus, Copyright 2013-2017 Jens Rehsack.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See <http://dev.perl.org/licenses/> for more information.


okay that's the whole page bestie!! 🌈 this was MooX::Cmd(3pm) from the User Contributed Perl Documentation, and the footer said perl v5.36.0 and the date was 2022-11-19 β€” so that's the version and the day she got her last glow-up. go build something cute, mwah πŸ’–βœ¨

πŸ“¦ where this page comes from