NAME Zydeco::Lite::App - use Zydeco::Lite to quickly develop command-line apps SYNOPSIS In `consumer.pl`: #! perl use strict; use warnings; use Zydeco::Lite::App; use Types::Standard -types; app 'MyApp' => sub { command 'Eat' => sub { constant documentation => 'Consume some food.'; arg 'foods' => ( type => ArrayRef[Str], documentation => 'A list of foods.', ); run { my ( $self, $foods ) = ( shift, @_ ); $self->info( "Eating $_." ) for @$foods; return 0; }; }; command 'Drink' => sub { constant documentation => 'Consume some drinks.'; arg 'drinks' => ( type => ArrayRef[Str], documentation => 'A list of drinks.', ); run { my ( $self, $drinks ) = ( shift, @_ ); $self->info( "Drinking $_." ) for @$drinks; return 0; }; }; }; 'MyApp'->execute( @ARGV ); At the command line: $ ./consumer.pl help eat usage: consumer.pl eat [...] Consume some food. Flags: --help Show context-sensitive help. Args: [] A list of foods. $ ./consumer.pl eat pizza chocolate Eating pizza. Eating chocolate. DESCRIPTION Zydeco::Lite::App extends Zydeco::Lite to redefine the `app` keyword to build command-line apps, and add `command`, `arg`, `flag`, and `run` keywords. It assumes your command-line app will have a single level of subcommands, like many version control and package management tools often do. (You type `git add filename.pl`, not `git filename.pl`. The `add` part is the subcommand.) It will handle @ARGV processing, loading config files, and IO for you. `app` The `app` keyword exported by Zydeco::Lite::App is a wrapper for the `app` keyword provided by Zydeco::Lite which performs additional processing for the `command` keyword to associate commands with applications, and adds the Zydeco::Lite::App::Trait::Application role (a.k.a. the App trait) to the package it defines. # Named application: app "Local::MyApp", sub { ...; # definition of app } "Local::MyApp"->execute( @ARGV ); # Anonymous application: my $app = app sub { ...; # definition of app }; $app->execute( @ARGV ); An anonymous application will actually have a package name, but it will be an automatically generated string of numbers, letters, and punctuation which you shouldn't rely on being the same from one run to another. Within the coderef passed to `app`, you can define roles, classes, and commands. The package defined by `app` will do the App trait. The App Trait `commands` The `commands` method lists the app's subcommands. Subcommands will each be a package, typically with a package name that uses the app's package name as a prefix. So your "add" subcommand might have a package name "Local::MyApp::Add" and your "add-recursive" subcommand might be called "Local::MyApp::Add::Recursive". The `commands` method will return these packages minus the prefix, so calling `'Local::MyApp'->commands` would return a list of strings including "Add" and "Add::Recursive". The App trait requires your app package to implement this method, but the `app` keyword will provide this method for you, so you don't typially need to worry about implementing it yourself. `execute` The `execute` method is the powerhouse of your app. It takes a list of command-line parameters, processes them, loads any config files, figures out which subcommand to run, dispatches to that, and exits. The App trait implements this method for you and you should probably not override it. `execute_no_subcommand` In the case where `execute` cannot figure out what subcommand to dispatch to, `execute_no_subcommand` is called. The App trait implements this method for you. The default behaviour is to call `execute` again, passing it "--help". You can override this behaviour though, if some other behaviour would be more useful. `stdio` Most of the methods in the App trait are okay to be called as either class methods or instance methods. "Local::MyApp"->execute( @ARGV ); bless( {}, "Local::MyApp" )->execute( @ARGV ); `stdio` is for calling on an instance though, and will return an instance if you call it as a class method. The arguments set the filehandles used by the app for input, output, and error messages. my $app = "Local::MyApp"->stdio( $in_fh, $out_fh, $err_fh ); $app->execite( @ARGV ); `stdin`, `stdout`, `stderr` Accessors which return the handles set by `stdio`. If no filehandles have been given, or called as a class method, return STDIN, STDOUT, and STDERR. `readline` A method for reading input. `$app->readline()` is a shortcut for `$app->stdin->readline()` but also calls `chomp` on the result. `print`, `debug`, `usage`, `info`, `warn`, `error`, `fatal`, `success` Methods for printing output. All off them automatically append new lines. `print` writes lines to `$app->stdout`. `debug` writes lines to `$app->stderr` but only if `$app->debug_mode` returns true. `usage` writes lines to `$app->stderr` and then exits with exit code 1. `info` writes lines in blue text to `$app->stderr`. `warn` writes lines in yellow text to `$app->stderr`. `error` writes lines in red text to `$app->stderr`. `fatal` writes lines in red text to `$app->stderr` and then exits with exit code 254. `success` writes lines in green text to `$app->stderr`. Any of these methods can be overridden in your app if you prefer different colours or different behaviour. `debug_mode` This method returns false by default. You can override it to return true, or do something like this: app "Local::MyApp" => sub { ...; method "debug_mode" => sub { return $ENV{MYAPP_DEBUG} || 0; }; }; `config_file` Returns the empty list by default. If you override it to return a list of filenames (not full path names, just simple filenames like "myapp.json"), your app will use these filenames to find configuration settings. `find_config` If `config_file` returns a non-empty list, this method will check the current working directory, a user-specific config directory (`~/.config/` on Linux/Unix, another operating systems will vary), and a system-wide config directory (`/etc/` on Linux/Unix), and return a list of config files found in those directories as Path::Tiny objects. `read_config` If given a list of Path::Tiny objects, will read each file as a config file and attempt to merge the results into a single hashref, which it will return. If an empty list is given, will call `find_config` to get a list of Path::Tiny objects. This allows your system-wide config in `/etc/myapp.json` to be overridden by user-specific `~/.config/myapp.json` and a local `./myapp.json`. You should rarely need to call this manually. (The `execute` method will call it as needed and pass any relevant configuration to the subcommand that it dispatches to.) It may sometimes be useful to override it if you need to support a different way of merging configuration data from multiple files, or if you need to be able to read configuration data from a non-file source. `read_single_config` Helper method called by `read_config`. Determines config file type by the last part of the filename. Understands JSON, INI, YAML, and TOML, and will assume TOML if the file type cannot be determined from its name. Config::Tiny and YAML::XS or YAML::PP are required for reading those file types, but are not included in Zydeco::Lite::App's list of dependencies. TOML is the generally recommended file format for apps created with this module. This method may be useful to override if you need to be able to handle other file types. `kingpin` Returns a Getopt::Kingpin object populated with everything necessary to perform command-line processing for this app. You will rarely need to call this manually or override it. `exit` Passed an integer, exits with that exit code. You may want to override this if you wish to perform some cleanup on exit. `command` The `command` keyword is used to define a subcommand for your app. An app should have one or more subcommands. It is a wrapper for the `class` keyword exported by Zydeco::Lite. The `command` keyword adds the Zydeco::Lite::App::Trait::Command role (a.k.a. the Command trait) to the class it defines. Commands may have zero or more args and flags. Args are (roughly speaking) positional parameters, passed to the command's `execute` method, while flags are named arguments passed the the command's constructor. The Command Trait `command_name` The Command trait requires your class to implement the `command_name` method. However, the `command` keyword will provide a default implementation for you if you have not. The default implementation uses the class name of the command (minus its app prefix), lowercases it, and replaces "::" with "-". So given the example: app "MyApp::Local", sub { command "Add::Recursive", sub { run { ... }; }; }; The package name of the command will be "MyApp::Local::Add::Recursive", and the command name will be "add-recursive". `documentation` This method is called to get a brief one-line description of the command. app "MyApp::Local", sub { command "Add::Recursive", sub { method "documentation" => sub { return "Adds a directory recursively."; }; run { ... }; }; }; You may prefer to use `constant` to define this method in your command class. app "MyApp::Local", sub { command "Add::Recursive", sub { constant "documentation" => "Adds a directory recursively."; run { ... }; }; }; See Zydeco::Lite for more information on the `method` and `constant` keywords. `execute` Each subcommand is required to implement an `execute` method. app "MyApp::Local", sub { command "Add::Recursive", sub { method "execute" => sub { ...; }; }; }; The subcommand's `execute` method is called by the app's `execute` method. It is passed the subcommand object ($self) followed by any command-line arguments that were given, which may have been coerced. (See "arg".) It should return the application's exit code; usually 0 for a successful execution, and an integer from 1 to 255 if unsuccessful. The `run` keyword provides a helpful shortcut for defining the `execute` method. (See "run".) `app` Returns the app as an object or package name. app "MyApp::Local", sub { command "Add::Recursive", sub { method "execute" => sub { my ( $self, @args ) = ( shift, @_ ); ...; $self->app->success( "Done!" ); $self->app->exit( 0 ); }; }; }; The `print`, `debug`, `info`, `warn`, `error`, `fatal`, `usage`, `success`, and `readline` methods are delegated to `app`, so `$self->app->success(...)` can just be written as `$self->success(...)`. `config` Returns the config section as a hashref for this subcommand only. So for example, if myapp.json had: { "globals": { "foo": 1, "bar": 2 }, "bumpf": { "bar": 3, "bat": 999 }, "quuux": { "bar": 4, "baz": 5 } } Then the Quuux command would see the following config: { "foo" => 1, "bar" => 4, "baz" => 5, } The `globals` section in a config is special and gets copied to all commands. `kingpin` Utility method used by the app's `kingpin` method to add a Getopt::Kingpin::Command object for processing this subcommand's arguments. You are unlikely to need to override this method or call it directly. `arg` Defines a command-line argument for a subcommand. use Zydeco::Lite::App; use Types::Path::Tiny -types; app "Local::MyApp" => sub { command "Add" => sub { arg 'filename' => ( type => File, required => 1 ); run { my ( $self, $file ) = ( shift, @_ ); ...; }; }; }; Arguments are ordered and are passed on the command line like follows: $ ./myapp.pl add myfile.txt The `arg` keyword acts a lot like Zydeco::Lite's `has` keyword. It supports the following options for an argument: `type` The type constraint for the argument. The following types (from Types::Standard and Types::Path::Tiny) are supported: Int, Num, Str, File, Dir, Path, ArrayRef[Int], ArrayRef[Num], ArrayRef[Str], ArrayRef[File], ArrayRef[Dir], ArrayRef[Path], HashRef[Int], HashRef[Num], HashRef[Str], HashRef[File], HashRef[Dir], HashRef[Path], as well as any custom type constraint which can be coerced from strings. HashRef types are passed on the command line like: ./myapp.pl somecommand key1=value1 key2=value2 `kingpin_type` In cases where `type` is a custom type constraint and Zydeco::Lite::App cannot figure out what to do with it, you can set `kingpin_type` to be one of the above supported types to act as a hint about how to process it. `required` A boolean indicating whether the argument is required. (Optional otherwise.) Optional arguments may be better as a "flag". `documentation` A one-line description of the argument. `placeholder` A string to use as a placeholder value for the argument in help text. `default` A non-reference default value for the argument, or a coderef that when called will generate a default value (which may be a reference). `env` An environment variable which will override the default value if it is given. Arguments don't need to be defined directly within a command. It is possible for a command to "inherit" arguments from a role or parent class, but this is usually undesirable as it may lead to their order being hard to predict. `flag` Flags are command-line options which are passed as `--someopt` on the command line. use Zydeco::Lite::App; use Types::Path::Tiny -types; app "Local::MyApp" => sub { command "Add" => sub { arg 'filename' => ( type => File, required => 1 ); flag 'logfile' => ( init_arg => 'log', type => File, handles => { 'append_log' => 'append' }, default => sub { Path::Tiny::path('log.txt') }, ); run { my ( $self, $file ) = ( shift, @_ ); $self->append_log( "Starting work...\n" ); ...; }; }; }; This would be called as: ./myapp.pl add --log=log2.txt filename.txt The `flag` keyword is a wrapper around the `has` keyword, so supports all the options supported by `has` such as `predicate`, `handles`, etc. It also supports all the options described for "arg" such as `env` and `placeholder`. Additionally there is a `short` option, allowing for short, single-letter flag aliases: flag 'logfile' => ( init_arg => 'log', type => File, short => 'L', ); Instead of being initialized using command-line arguments, flags can also be initialized in the application's config file. Flags given on the command line override flags in the config files; flags given in config files override those given by environment variables; environment variables override defaults. Like args, flags can be defined in a parent class or a role. It can be helpful to define common flags in a role. `run` The `run` keyword just defines a method called "execute". The following are equivalent: run { ... }; method 'execute' => sub { ... }; BUGS Please report any bugs to . SEE ALSO This module extends Zydeco::Lite to add support for rapid development of command-line apps. Z::App is a shortcut for importing this module plus a collection of others that might be useful to you, including type constraint libraries, strict, warnings, etc. Getopt::Kingpin is used for processing command-line arguments. AUTHOR Toby Inkster . COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Toby Inkster. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. DISCLAIMER OF WARRANTIES THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.