App::Rad(3pm)

๐Ÿ“ฆ
version 1.05 ยท 5 versions
, in ubuntu noble, ubuntu resolute, ubuntu stonking

๐Ÿ’– App::Rad ๐Ÿ’–

Rapid (and easy!) creation of command line applications. That's the whole vibe of the App::Rad(3pm) page โ€” a User Contributed Perl Documentation situation, and bestie, it's a good one. โœจ


โœจ VERSION โœจ

Version 1.04 ๐ŸŽ€


๐ŸŒธ SYNOPSIS ๐ŸŒธ

okay so. This right here is your smallest working application (let's call it myapp.pl):

use App::Rad;
App::Rad->run();

That's it. That's the whole thing. Your program already works and you can use it directly via the command line (try it, fr fr!)

[user@host]$ ./myapp.pl
Usage: myapp.pl command [arguments]

Available Commands:
    help    show syntax and available commands

Next, start creating your own functions (e.g.) inside myapp.pl:

sub hello {
    return "Hello, World!";
}

And now your simple command line program myapp.pl has a hello command! slay ๐Ÿ’…

[user@host]$ ./myapp.pl
Usage: myapp.pl command [arguments]

Available Commands:
    hello
    help    show syntax and available commands

[user@host]$ ./myapp.pl hello
Hello, World!

You could easily add a customized help message for your command through the Help() attribute:

sub hello
:Help(give a nice compliment)
{
    return "Hello, World!";
}

And then, as expected:

[user@host]$ ./myapp.pl
Usage: myapp.pl command [arguments]

Available Commands:
    hello   give a nice compliment
    help    show syntax and available commands

App::Rad also lets you expand your applications, providing a lot of flexibility for every command, with embedded help, argument and options parsing, configuration file, default behavior, and much more:

use App::Rad;
App::Rad->run();

sub setup {
    my $c = shift;

    $c->register_commands( {
            foo => 'expand your foo!',
            bar => 'have a drink! arguments: --drink=DRINK',
        });
}

sub foo {
    my $c = shift;
    $c->load_config('myapp.conf');

    return 'foo expanded to ' . baz() * $c->config->{'myfoo'};
}

# note that 'baz' was not registered as a command,
# so it can't be called from the outside.
sub baz { rand(10) }

sub bar {
    my $c = shift;
    if ( $c->options->{'drink'} ) {
        return 'you asked for a ' . $c->options->{'drink'};
    }
    else {
        return 'you need to ask for a drink';
    }
}

You can try on the command line:

[user@host]$ ./myapp.pl
Usage: myapp.pl command [arguments]

Available Commands:
    bar     have a drink! arguments: --drink=DRINK
    foo     expand your foo!
    help    show syntax and available commands

[user@host]$ ./myapp.pl bar --drink=martini
you asked for a martini

like?? the range ๐Ÿ˜ญ it's giving main character energy and it's just a perl module.


โš ๏ธ WARNING โš ๏ธ

This module is very young, likely to change in strange ways and to have some bugs (please report if you find any!). I will try to keep the API stable, but even that is subject to change (let me know if you find anything annoying or have a wishlist). You have been warned! ๐Ÿ’€

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

App::Rad aims to be a simple yet powerful framework for developing your command-line applications. It can easily transform your Perl one-liners into reusable subroutines than can be called directly by the user of your program.

It also tries to provide a handy interface for your common command-line tasks. If you have a feature request to easen out your tasks even more, please drop me an email or a RT feature request. ๐Ÿซถ

๐Ÿงš Extending App::Rad - Plugins! ๐Ÿงš

App::Rad plugins can be loaded by naming them as arguments to the use App::Rad statement. Just ommit the App::Rad::Plugin prefix from the plugin name. For example:

use App::Rad  qw(My::Module);

will load the App::Rad::Plugin::My::Module plugin for you! no cap โœจ

Developers are strongly encouraged to publish their App::Rad plugins under the App::Rad::Plugin namespace. But, if your plugin start with a name other than that, you can fully qualify the name by using an unary plus sign:

use App::Rad  qw(
        My::Module
        +Fully::Qualified::Plugin::Name
);

Note that plugins are loaded in the order in which they appear. ๐Ÿ‘‘

Please refer to the actual plugin documentation for specific usage. And check out App::Rad::Plugin if you want to create your own plugins.


๐ŸŒˆ INSTANTIATION ๐ŸŒˆ

These are the main execution calls for the application. In your App::Rad programs, the ONLY thing your script needs to actually (and actively) call is one of the instantiation (or dispatcher) methods. Leave all the rest to your subs. Currently, the only available dispatcher is run():

โญ run() โญ

You'll be able to access all of your program's commands directly through the command line, as shown in the synopsis. easy as that bestie ๐Ÿ’…


๐ŸŽ€ BUILT-IN COMMANDS ๐ŸŽ€

This module comes with the following default commands. You are free to override them as you see fit.

๐Ÿ’œ help

Shows help information for your program. This built-in function displays the program name and all available commands (including the ones you added yourself) if a user types the 'help' command, or no command at all, or any command that does not exist (as they'd fall into the 'default' control function which (by default) calls 'help').

You can also display specific embedded help for your commands, either explicitly registering them with $c->register() or $c->register_commands() inside $c->setup() (see respective sections below) or with the Help() attribute:

use App::Rad;
App::Rad->run();

sub mycmd
:Help(display a nice welcome message)
{
    return "Welcome!";
}

the associated help text would go like this:

[user@host]$ ./myapp.pl
Usage: myapp.pl command [arguments]

Available Commands:
    help    show syntax and available commands
    mycmd   display a nice welcome message

๐ŸŒŸ OTHER BUILT IN COMMANDS (OPT-IN) ๐ŸŒŸ

The 'include' and 'exclude' commands below let the user include and exclude commands to your program and, as this might be dangerous when the user is not yourself, you have to opt-in on them:

use App::Rad qw(include);  # add the 'include' command
use App::Rad qw(exclude);  # add the 'exclude' command

though you'll probably want to set them both:

use App::Rad qw(include exclude);

๐Ÿ“ include [command_name] -perl_params 'your subroutine code'

Includes the given subroutine into your program on-the-fly, just as you would writing it directly into your program.

Let's say you have your simple 'myapp.pl' program that uses App::Rad sitting on your system quietly. One day, perhaps during your sysadmin's tasks, you create a really amazing one-liner to solve a really hairy problem, and want to keep it for posterity (reusability is always a good thing!). ๐Ÿซถ

For instance, to change a CSV file in place, adding a column on position #2 containing the line number, you might do something like this (this is merely illustrative, it's not actually the best way to do it):

$ perl -i -paF, -le 'splice @F,1,0,$.; $_=join ",",@F' somesheet.csv

And you just found out that you might use this other times. What do you do? App::Rad to the rescue! ๐ŸŒˆ

In the one-liner above, just switch 'perl' to 'myapp.pl include SUBNAME' and remove the trailing parameters (somesheet.csv):

$ myapp.pl include addcsvcol -i -paF, -le 'splice @F,1,0,$.; $_=join ",",@F'

That's it! Now myapp.pl has the 'addcsvcol' command (granted, not the best name) and you can call it directly whenever you want:

$ myapp.pl addcsvcol somesheet.csv

App::Rad not only transforms and adjusts your one-liner so it can be used inside your program, but also automatically formats it with Perl::Tidy (if you have it). This is what the one-liner above would look like inside your program:

sub addcsvcol {
    my $c = shift;

    local ($^I) = "";
    local ($/)  = "\n";
    local ($\)  = "\n";
  LINE: while ( defined( $_ = <ARGV> ) ) {
        chomp $_;
        our (@F) = split( /,/, $_, 0 );
        splice @F, 1, 0, $.;
        $_ = join( ',', @F );
    }
    continue {
        die "-p destination: $!\n" unless print $_;
    }
}

With so many arguments (-i, -p, -a -F,, -l -e), this is about as bad as it gets. And still one might find this way easier to document and mantain than a crude one-liner stored in your ~/.bash_history or similar.

Note: If you don't supply a name for your command, App::Rad will make one up for you (cmd1, cmd2, ...). But don't do that, as you'll have a hard time figuring out what that specific command does. ๐Ÿ˜ญ

Another Note: App::Rad tries to adjust the command to its interface, but please keep in mind this module is still in its early stages so it's not guaranteed to work every time. PLEASE let me know via email or RT bug request if your one-liner was not correctly translated into an App::Rad command. Thanks! ๐Ÿ’–

๐ŸงŠ exclude command_name

Removes the requested function from your program. Note that this will delete the actual code from your program, so be extra careful. It is strongly recommended that you do not use this command and either remove the subroutine yourself or add the function to your excluded list inside setup().

Note that built-in commands such as 'help' cannot be removed via exclude. They have to be added to your excluded list inside setup().


okay bestie hang on, we're only like a quarter of the way through this page โ€” more sections are literally incoming, don't go anywhere ๐ŸŒธ

๐ŸŽ€ ROLLING YOUR OWN COMMANDS ๐ŸŽ€

okay bestie, this is the fun part ๐Ÿ’– making a command is literally just... writing a sub in your program. that's it. that's the whole thing. but heads up: some names are reserved for special purposes โ€” uh, do not name your command setup, default, invalid, pre_process, post_process or teardown, because those belong to the special stuff (check the Control Functions section of this doc for the tea โ˜•). For literally everything else, App::Rad hooks you up with a cute lil interface for reading command line input and writing formatted output โœจ


๐Ÿ’… The Controller

Every command (sub) you create receives the controller object $c as an argument (other projects be calling it $self, same vibe, different name ๐Ÿซถ). The controller is the main interface to App::Rad and it's stacked with methods that make your command manipulation and execution tasks so much easier. main character energy fr.


๐ŸŒธ Reading arguments

When someone types in a command, she may pass some arguments to it. Those arguments can be accessed in four different ways, depending on what you want โ€” so it's fully up to you to control which and how many arguments (if at all) you wanna receive and/or use. Here they are, bestie:

@ARGV

Perl's @ARGV array has all the arguments passed to your command, without the command name (use $c->cmd for that) and without any processing (even if you explicitly use $c->getopt, which will change $c->argv instead, see below ๐Ÿ‘‡). And since the command itself won't be in the @ARGV parameters, you can use it in each command as if they were stand-alone programs. iconic.

$c->options

App::Rad lets you automatically retrieve any POSIX syntax command line options (getopt-style) passed to your command via the $c->options method. This method returns a hash reference with keys as given parameters and values as... well... values ๐Ÿ’โ€โ™€๏ธ The 'options' method automatically supports two simple argument structures:

  • Extended (long) option. Translates --parameter or --parameter=value into $c->options->{parameter}. If no value is supplied, it will be set to 1.
  • Single-letter option. Translates -p into $c->options->{p}.

Single-letter options can be nested together, so -abc will be parsed into $c->options->{a}, $c->options->{b} and $c->options{c}, while --abc will be parsed into $c->options->{abc}. We could, for instance, create a dice-rolling command like this ๐ŸŽฒ:

    sub roll {
        my $c = shift;

        my $value = 0;
        for ( 1..$c->options->{'times'} ) {
            $value += ( int(rand ($c->options->{'faces'}) + 1));
        }
        return $value;
    }

And now you can call your 'roll' command like:

    [user@host]$ ./myapp.pl roll --faces=6 --times=2

Note that App::Rad does not control which arguments can or cannot be passed: they are all parsed into $c->options and it's up to you to use whichever you want. For a more advanced use and control, see the $c->getopt method below ๐Ÿ‘€

Also note that single-letter options will be set to 1. However, if a user types them more than once, the value will be incremented accordingly. For example, if a user calls your program like so:

   [user@host]$ ./myapp.pl some_command -vvv

or

   [user@host]$ ./myapp.pl some_command -v -v -v

then, in both cases, $c->options->{v} will be set to 3. This will let you easily keep track of how many times any given option was chosen, and still let you just check for definedness if you don't care about that. lowkey genius ๐Ÿง โœจ

$c->argv

The array reference $c->argv contains every argument passed to your command that have not been parsed into $c->options. This is usually a list of every provided argument that didn't start with a dash (-), unless you've called $c->getopt and used something like param=s (again, see below ๐Ÿ™ˆ).

$c->getopt (Advanced Getopt usage)

App::Rad is also smoothly integrated with Getopt::Long, so you can have even more flexibility and power while parsing your command's arguments, such as aliases and types. Call the $c->getopt() method anytime inside your commands (or just once in your pre_process function to always have the same interface) passing a simple array with your options, and refer back to $c->options to see them. For instance:

    sub roll {
        my $c = shift;

        $c->getopt( 'faces|f=i', 'times|t=i' )
            or $c->execute('usage') and return undef;

        # and now you have $c->options->{'faces'}
        # and $c->options->{'times'} just like above.
    }

This becomes very handy for complex or feature-rich commands. Please refer to the Getopt::Long module for more usage examples ๐Ÿ’–

So, in order to manipulate and use any arguments, remember:

  • The given command name does not appear in the argument list;
  • All given arguments are in @ARGV
  • Automatically processed arguments are in $c->options
  • Non-processed arguments (the ones $c->options didn't catch) are in $c->argv
  • You can use $c->getopt to have Getopt::Long parse your arguments (it will not change @ARGV)

๐ŸŒˆ Sharing Data: $c->stash

The stash is a universal hash for storing data among your Commands, slay:

    $c->stash->{foo} = 'bar';
    $c->stash->{herculoids} = [ qw(igoo tundro zok gloop gleep) ];
    $c->stash->{application} = { name => 'My Application' };

You can use it for more granularity and control over your program. For instance, you can email the output of a command if (and only if) something happened:

    sub command {
        my $c = shift;
        my $ret = do_something();

        if ( $ret =~ /critical error/ ) {
            $c->stash->{mail} = 1;
        }
        return $ret;
    }

    sub post_process {
        my $c = shift;

        if ( $c->stash->{mail} ) {
            # send email alert...
        }
        else {
            print $c->output . "\n";
        }
    }

โญ Returning output

Once you're through, return whatever you want to give as output for your command:

    my $ret = "Here's the list: ";
    $ret .= join ', ', 1..5;
    return $ret;

    # this prints "Here's the list: 1, 2, 3, 4, 5"

App::Rad lets you post-process the returned value of every command, so refrain from printing to STDOUT directly whenever possible as it will give much more power to your programs ๐Ÿ˜ค๐Ÿ’ช See the post_process() control function further below in this document.


๐Ÿ’– HELPER METHODS ๐Ÿ’–

Okay so App::Rad's controller comes absolutely stacked with methods to help you run your app without losing your whole entire mind. If you think of some other useful command that isn't here? Please, bestie, drop me a line or send an RT request. We love a collab. ๐Ÿ’…


$c->execute( COMMAND_NAME )

Runs the command you hand it. If you hand it nothing, it runs whatever's chillin' in $c->cmd. And if that command doesn't even exist? No stress, it runs the 'default' command instead. Iconic behavior tbh.

Oh and heads up: every single execute() call also invokes pre_process and post_process, so you can easily manipulate the income and the outcome of every command. Chef's kiss. ๐Ÿซถ

$c->cmd

Returns a string with the name of the command โ€” that's literally the first argument of your program โ€” the one that gets called right after pre_process.

$c->command

Alias for $c->cmd. This longer form is discouraged and may be removed in future versions, because you could lowkey confuse it with the $c->commands() method (explained right below, bestie). You have been warned. โš ๏ธ

$c->commands()

Returns a list of all the available commands (functions) inside your program. The whole girl squad, listed out. โœจ

$c->is_command ( COMMAND_NAME )

Returns 1 (true) if the COMMAND_NAME you passed is available, 0 (false) otherwise. A little vibe check for your commands.

$c->create_command_name()

Returns a valid name for a command โ€” like, a name slot that hasn't been used by your program yet. It comes out looking like 'cmd1', 'cmd2', etc., so don't use this unless you absolutely have to. App::Rad itself uses it whenever you try to include (see below) a new command but don't supply a name for it. So like, it's the emergency name generator. ๐Ÿ™ˆ

$c->load_config( FILE (FILE2, FILE3, ...) )

This one lets you easily load one or more configuration files into your program. The config files look like this:

# comments and blank lines are discarded
key1 value1
key2:value2
key3=value3
key5           # stand-alone attribute (and inline-comment)

Yes bestie, comments and blank lines are discarded, and yes you can do all those funky separators. It's giving flexible. ๐ŸŒธ

$c->config

Returns a hash reference holding any config values you loaded (see $c->load_config() up there โ˜๏ธ).

$c->register ( NAME, CODEREF [, INLINE_HELP ])

Registers a coderef as a callable command. Now listen โ€” you do NOT have to call this just to register a sub inside your program as a command, run() will already do that for you. And if you don't want certain subroutines showing up as commands, you can always use $c->register_commands() (note the plural) inside setup(). This method is just an interface to dynamically include commands in your programs. It returns the command name on success, undef otherwise.

It's also super useful for making aliases for your commands:

sub setup {
    my $c = shift;
    $c->register_commands();

    $c->register('myalias', \&command);
}

sub command { return "Hi!" }

and then, on the command line:

[user@host]$ ./myapp.pl command
Hi!

[user@host]@ ./myapp.pl myalias
Hi!

Look at that. Two commands, one function. Main character energy fr fr. ๐Ÿ‘‘

The last parameter is optional and lets you add inline help to your command:

$c->register('cmd_name', \&cmd_func, 'display secret of life');

$c->register_command ( NAME, CODEREF [, INLINE_HELP ] )

Longer alias for $c->register(). Its use is discouraged because you might confuse it with register_commands (note the plural, bestie) down below. Plus, it's just more typing :) As such, this method may be removed in future versions. You have been warned! โš ๏ธ

$c->register_commands()

This method โ€” usually called during setup() โ€” tells App::Rad to register subroutines as valid commands. If you call it without any parameters, it registers all subroutines in your main program as valid commands. And note that App::Rad's default behavior is to ignore subroutines starting with an underscore '_'. You can easily change this behavior using some of the options below. Let's get into it ๐Ÿ’…

Adding single commands

$c->register_commands( qw/foo bar baz/ );

The code above registers only the subs "foo", "bar" and "baz" as commands. Every other subroutine won't be a valid command, so you can use them as internal subs for your program. You can change this behavior with the bundled options โ€” see 'Adding several commands' and 'Putting it all together' below.

Adding single commands (with inline help)

$c->register_commands(
        {
            dos2unix => 'convert text files from DOS to Unix format',
            unix2dos => 'convert text files from Unix to DOS format',
        }
);

You can pass a hash reference with commands as the keys and a small help string as the values. The code above registers only the subs "dos2unix" and "unix2dos", and the default help for your program becomes something like this:

[user@host]$ ./myapp.pl
Usage: myapp.pl command [arguments]

Available Commands:
    dos2unix    convert text files from DOS to Unix format
    help        show syntax and available commands
    unix2dos    convert text files from Unix to DOS format

Look at that gorgeous auto-generated help. We love to see it. ๐ŸŒˆ

Adding several commands

You can pass a hash reference as an argument to choose which subroutines get added as commands. The following keys may be used (and yes, note the dash in front of each key, bestie):

  • -ignore_prefix: subroutine names starting with the given string won't be added as commands
  • -ignore_suffix: subroutine names ending with the given string won't be added as commands
  • -ignore_regexp: subroutine names matching the given regular expression (as a string) won't be added as commands

For example:

use App::Rad;
App::Rad->run();

sub setup {
    my $c = shift;
    $c->register_commands( { -ignore_prefix => '_' } );
}

sub foo  {}  # will become a command
sub bar  {}  # will become a command
sub _baz {}  # will *NOT* become a command

This way you can easily segregate between commands and helper functions, making your code even more reusable without jeopardizing the command line interface. And yes โ€” as of version 1.04, ignoring commands with underscore '_' prefixes is also the default App::Rad behavior. Slay. ๐Ÿ’…

Putting it all together

You can combine some of the options above for even more flexibility:

$c->register_commands(
        'foo',
        { -ignore_suffix => 'foo' },
        { bar => 'all your command line are belong to us' },
);

The code above registers as commands all subs with names not ending in 'foo', but it will register the 'foo' sub as well. It will also give the 'bar' command its help string. This behavior is handy for registering several commands while keeping a few exceptions, or for adding your commands and only having inline help for a few of them (as you see fit).

And you don't have to worry about the order of the elements you pass โ€” App::Rad will figure them out for you in a DWIM fashion. It just knows. ๐Ÿ”ฎ

# this does the same as the code above
$c->register_commands(
        { bar => 'all your command line are belong to us' },
        'foo',
        { -ignore_suffix => 'foo' },
);

You can even bundle the hash reference to include your "cmd => help" pairs and the special keys:

# this behaves the same way as the code above:
$c->register_commands(
    'foo',
    {
        -ignore_suffix => 'foo',
        bar => 'all your command line are belong to us',
    }
);

Obsessed. One hash, all the chaos, fully under control. ๐ŸŽ€

$c->unregister_command ( NAME )

Longer alias for $c->unregister(). The use of the shorter form is encouraged, and this alias may be removed in future versions. You have been warned. โš ๏ธ

$c->unregister ( NAME )

Unregisters a given command name so it's not available anymore. Note that the subroutine will still be there to be called from inside your program โ€” it just won't be accessible via the command line anymore. So like, it's not gone-gone, it's just off the menu. ๐Ÿ“

$c->debug( MESSAGE )

Will print the given message on screen only if the debug flag is enabled:

use App::Rad  qw( debug );

And note: if debug is enabled, App::Rad itself will print several debug messages stating its current flow, so you can easily find out where everything is happening. It's basically your app narrating its own life. ๐Ÿ˜ญ

$c->plugins()

Returns a list of all loaded plugins, in the order in which they were loaded. First in, first listed, periodt.

$c->load_plugin( PLUGIN NAME )

This method dynamically loads the given plugin. The plugin needs to live under the "App::Rad::Plugin" namespace, and the name should be relative to that path. So like, $c->load_plugin('MyPlugin') will try to load 'App::Rad::Plugin::MyPlugin'. And if you want to load a plugin by its fully qualified name, you need to prepend a plus sign to the name: '+Fully::Qualified::Plugin::Name'.

Real talk though: this is an internal method and you really should refrain from using it. Instead, plugins should be loaded as parameters to the use App::Rad statement, as explained above. Don't be out here doing internal-method things, bestie. ๐Ÿ™ˆ

๐ŸŽ›๏ธ CONTROL FUNCTIONS (to possibly override)

okay bestie so here's the tea ๐Ÿ‘‘ App::Rad has some control functions that are literally expecting you to override them by writing your own versions in your program. They're like little hooks with main character energy. Here they all are:


setup()

this one is the get-ready era, fr. It's in charge of setting up what your program can and cannot do, plus literally everything you need to sort out before you actually run any command โ€” connecting to a database or a host, checking and validating stuff, downloading a document, whatever your little heart desires. โœจ

๐Ÿ’– PSA: if you override setup(), you must call $c->register_commands() or at minimum $c->register() so your subs get classified as valid commands. No skipping this one, iykyk. (Peep $c->register_commands() above for the full deets.)

Another super cute thing you can do with setup is mess with the command list itself. Like, say you want the include and exclude commands to exist but not be available to everyone. So instead of writing:

use App::Rad qw(include exclude);
App::Rad->run();

you can write something like this:

use App::Rad;
App::Rad->run();

sub setup {
    my $c = shift;
    $c->register_commands();

    # EUID is 'root'
    if ( $> == 0 ) {
        $c->register('include', \&App::Rad::include);
        $c->register('exclude', \&App::Rad::exclude);
    }
}

and get something like this:

[user@host]$ myapp.pl help
Usage: myapp.pl command [arguments]

Available Commands:
   help

[user@host]$ sudo myapp.pl help
Usage: myapp.pl command [arguments]

Available Commands:
   exclude
   help
   include

like??? iconic. security through vibes. ๐Ÿ’…


default()

if no command gets handed to your app, it just falls in here. ๐Ÿ“ And heads up โ€” an invalid (non-existant) command also lands here, but you can switch that up with the invalid() function right below (though honestly, usually you do not want to).

Default's default (lol, grin ๐Ÿ˜ญ) is just an alias for the help command.

sub default {
    my $c = shift;

    # will fall here if the given
    # command isn't valid.
}

You are free โ€” and lowkey encouraged โ€” to change the default behavior to whatever you want. This is super useful when your program only does one thing and therefore receives only parameters instead of command names. In those cases, use the ""default()"" sub as your main program's sub and parse the parameters with $c->argv and $c->getopt exactly like you would in any other command. ๐Ÿซถ


invalid()

This is a special little function for giving you even MORE flexibility while you build your command line apps. It gets called when the user requests a command that doesn't exist. The built-in invalid() just redirects itself to default() (see above), so usually the only time you even think about this is when you want to tell apart "no command given" (with or without getopt-like arguments) and "invalid command given" (with or without getopt-like arguments). ๐ŸŒˆ


teardown()

if you implement it, this one gets called automatically after your application runs. It's your clean-up era: removing temporary files, disconnecting a database connection you made in the setup function, logging, sending data over a network, or even storing state info via Storable or whatever you're into. ๐Ÿงš


pre_process()

if you implement it, this gets called automatically right before the actual wanted command is called. So it's your optional pre-run hook โœจ โ€” perfect for adding functionality like stopping certain commands from running under a specific uid (e.g. root):

sub pre_process {
    my $c = shift;

    if ( $c->cmd eq 'some_command' and $> != 0 ) {
        $c->cmd = 'default'; # or some standard error message
    }
}

post_process()

if you implement it, this gets called automatically right after the requested function returned. It receives the Controller object right after a given command has been executed (and hopefully with some output returned ๐Ÿ’–), so you can manipulate it however you like. In fact, the default post_process function goes like this:

sub post_process {
    my $c = shift;

    if ( $c->output() ) {
        print $c->output() . "\n";
    }
}

You can override this to slap a default header/footer on your programs (either a label or maybe a "Content-type: " string), parse the output however you see fit (CPAN is your friend, as usual), etc. ๐ŸŒธ


๐Ÿšจ IMPORTANT NOTE ON PRINTING INSIDE YOUR COMMANDS

okay so this is the part where I grab you by the shoulders. The post_process() function up there is the reason your application should NEVER print to STDOUT. Using print (or say, in 5.10) to send output to STDOUT is exclusively the domain of the post_process() function. Breaking this rule is a common source of errors, no cap. ๐Ÿ’€

if you want your functions to be interactive (for instance) and print everything themselves, you should disable post-processing in setup(), or create an empty post_process function, or make your functions return undef (so post_process() will only add a blank line to the output). Either way โ€” pick one, bestie. ๐Ÿ™ˆ


๐Ÿฉบ DIAGNOSTICS

if you see a '1' printed on the screen after a command is issued, it's probably because that command is returning a "true" value instead of an output string. ๐Ÿ˜ญ So if you don't want to return the command output for post processing (you'll loose some nice features, though), you can return undef or make post_process() empty. Crisis averted. โœจ


โš™๏ธ CONFIGURATION AND ENVIRONMENT

App::Rad requires no configuration files or environment variables. Clean. Minimal. Breathable. ๐ŸŒฟ


๐Ÿ“ฆ DEPENDENCIES

App::Rad depends only on 5.8 core modules (Carp for errors, Getopt::Long for $c->getopt, Attribute::Handlers for help and O/B::Deparse for the include command).

If you have Perl::Tidy installed, the include command will tidy up your code before inclusion. ๐Ÿ’…

The test suite depends on Test::More, FindBin and File::Temp, also core modules.


๐Ÿšซ INCOMPATIBILITIES

None reported. slay. ๐ŸŽ€


๐Ÿ› BUGS AND LIMITATIONS

Please report any bugs or feature requests to bug-app-easy at rt.cpan.org, or through the web interface at http://rt.cpan.org/garu/ReportBug.html?Queue=App-Rad. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. ๐Ÿซถ


๐Ÿ’ฌ SUPPORT

You can find documentation for this module with the perldoc command.

perldoc App::Rad

Although this Module comes without any warraties whatsoever (see DISCLAIMER below), I try really hard to provide some quality assurance for the users. This means I not only try to close all reported bugs in the minimum amount of time but I also try to find some on my own. ๐Ÿ’ช

This version of App::Rad comes with 183 tests and I keep my eye constantly on CPAN Testers http://www.cpantesters.org/show/App-Rad.html to ensure it passes all of them, in all platforms. You can send me your own App::Rad tests if you feel I'm missing something and I'll hapilly add them to the distribution. ๐Ÿงช

Since I take user's feedback very seriously, I really hope you send me any wishlist/TODO you'd like App::Rad to have (please try to send them via RT so other people can give their own suggestions). โœจ

You can also look for information at:

  • RT: CPAN's request tracker

http://rt.cpan.org/garu/Bugs.html?Dist=App-Rad

  • AnnoCPAN: Annotated CPAN documentation

http://annocpan.org/dist/App-Rad

  • CPAN Ratings

http://cpanratings.perl.org/d/App-Rad

  • Search CPAN

http://search.cpan.org/dist/App-Rad

IRC

#app-rad on irc.perl.org ๐ŸŒˆ


๐Ÿ“ TODO

this is a small list of features I plan to add in the near future (in no particular order). Feel free to contribute with your wishlist and comentaries! ๐Ÿ’œ

  • Shell-like environment
  • Loadable commands (in an external container file)
  • Modularized commands (similar to App::Cmd::Commands ?)
  • app-starter
  • command inclusion by prefix, suffix and regexp (feature request by fco)
  • command inclusion and exclusion also by attributes
  • some extra integration, maybe IPC::Cmd and IO::Prompt

๐Ÿ‘‘ AUTHOR

Breno G. de Oliveira, "<garu at cpan.org>"


๐ŸŒŸ CONTRIBUTORS

(in alphabetical order)

Ben Hengst

Fernando Correa

Flavio Glock

Thanks to everyone for contributing! Please let me know if I've skipped your name by accident. ๐Ÿฅบ


๐Ÿ’– ACKNOWLEDGEMENTS

This module was inspired by Kenichi Ishigaki's presentation "Web is not the only one that requires frameworks" during YAPC::Asia::2008 and the modules it exposed (mainly App::Cmd and App::CLI). ๐Ÿฆ‹

Also, many thanks to CGI::App(now Titanium)'s Mark Stosberg and all the Catalyst developers, as some of App::Rad's functionality was taken from those (web) frameworks. ๐ŸŒธ


Copyright 2008 Breno G. de Oliveira "<garu at cpan.org>". All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.


โš ๏ธ DISCLAIMER OF WARRANTY

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.


and that's a wrap on the whole page, bestie ๐ŸŽ€ this edition was whipped up from perl v5.36.0, dated 2022-10-13, for App::Rad(3pm). go build something iconic with it. periodt. ๐Ÿ’…โœจ

๐Ÿ“ฆ where this page comes from