CLI-Startup
view release on metacpan or search on metacpan
lib/CLI/Startup.pm view on Meta::CPAN
package CLI::Startup;
use English qw( -no_match_vars );
use warnings;
use strict;
use Carp;
use Symbol;
use Pod::Text;
use Text::CSV;
use Class::Std;
use Config::Any;
use Data::Dumper;
use File::HomeDir;
use File::Basename;
use Clone qw{ clone };
use Hash::Merge qw{ merge };
use List::Util qw{ max reduce };
use Getopt::Long qw{
GetOptionsFromArray :config posix_default bundling require_order no_ignore_case
};
use Exporter 'import';
our @EXPORT_OK = qw/startup/;
our $VERSION = '0.29'; # Don't forget to update the manpage version, too!
use Readonly;
Readonly my $V_FOR_VERBOSE => 'ALIAS OF VERBOSE';
Readonly my $V_OPTSPEC => 'v+';
# Simple command-line processing with transparent
# support for config files.
sub startup
{
my $optspec = shift;
my $app = CLI::Startup->new($optspec);
$app->init;
return $app->get_options;
}
#<<< Leave this alone, perltidy
# Attributes of our inside-out objects.
my %config_of : ATTR();
my %initialized_of : ATTR( :get<initialized> );
my %options_of : ATTR();
my %optspec_of : ATTR( :initarg<optspec> );
my %raw_options_of : ATTR();
my %rcfile_of : ATTR( :get<rcfile> :initarg<rcfile> );
my %usage_of : ATTR( :get<usage> :initarg<usage> );
my %write_rcfile_of : ATTR( :get<write_rcfile> :initarg<write_rcfile> );
my %default_settings_of :
ATTR( :get<default_settings> :initarg<default_settings> );
#>>>
# Returns a clone of the config object.
sub get_config
{
my $self = shift;
$self->die('get_config() called before init()')
unless $self->get_initialized;
return clone( $config_of{ ident $self} );
}
# Set defaults for the command-line options. Can be done as much as
# desired until the app is initialized.
sub set_default_settings
{
my ( $self, $settings ) = @_;
$self->die('set_default_settings() requires a hashref')
unless defined $settings and ref $settings eq 'HASH';
$self->die('set_default_settings() called after init()')
if $self->get_initialized;
$default_settings_of{ ident $self} = clone($settings);
return; # Needed so we don't leak a reference to the data!
}
# Get the options provided on the command line. This, unlike most of
# the others, can ONLY be called after the app is initialized.
sub get_options
{
my $self = shift;
$self->die('get_options() called before init()')
unless $self->get_initialized;
return clone( $options_of{ ident $self} );
}
lib/CLI/Startup.pm view on Meta::CPAN
if $EVAL_ERROR;
my $json = JSON::MaybeXS->new();
open my $RCFILE, '>', $file
or $self->die("Couldn't open file \"$file\": $OS_ERROR");
print {$RCFILE} $json->encode( $self->get_options_as_defaults )
or $self->die("Couldn't write to file \"$file\": $OS_ERROR");
close $RCFILE
or $self->die("Couldn't close file \"$file\": $OS_ERROR");
return 1;
}
# Write the current settings to a YAML file.
sub _write_rcfile_yaml
{
my ( $self, $file ) = @_;
# Installing a YAML module is optional.
eval 'use YAML::Any qw{DumpFile}';
$self->die('Can\'t write rcfile: YAML::Any is not installed.')
if $EVAL_ERROR;
DumpFile( $file, $self->get_options_as_defaults );
return 1;
}
# Write the current settings to a Perl file.
sub _write_rcfile_perl
{
my ( $self, $file ) = @_;
local $Data::Dumper::Terse = 1;
open my $RCFILE, '>', $file
or $self->die("Couldn't open file \"$file\": $OS_ERROR");
print {$RCFILE} Dumper( $self->get_options_as_defaults )
or $self->die("Couldn't write to file \"$file\": $OS_ERROR");
close $RCFILE
or $self->die("Couldn't close file \"$file\": $OS_ERROR");
return 1;
}
1; # End of CLI::Startup
__END__
=head1 NAME
CLI::Startup - Simple initialization for command-line scripts
=head1 VERSION
Version 0.29
=head1 SYNOPSIS
C<CLI::Startup> can export a single method, C<startup()>, into the
caller's namespace. It transparently handles config files, defaults,
and command-line options.
use CLI::Startup 'startup';
# Returns the merged results of defaults, config file
# and command-line options.
my $options = startup({
'opt1=s' => 'Option taking a string',
'opt2:i' => 'Optional option taking an integer',
...
});
It also supports an object-oriented interface with much more scope
for customization. The basic usage looks like this:
use CLI::Startup;
# Parse command line and read config
$app = CLI::Startup->new({
usage => '[options] [other args ...]', # Optional
options => $optspec,
default_settings => $defaults,
});
$app->init;
# Combined command line, config file and default options. Almost
# always what you want.
$opts = $app->get_options;
# Information about the current invocation of the calling script:
$opts = $app->get_raw_options; # Actual command-line options
$conf = $app->get_config; # Options set in config file
$dflt = $app->get_default_settings; # Wired-in script defaults
Most scripts can then use C<$opts> for all their customization needs.
You can also hide extra data in the config file, and access it
through C<$app->get_config>. The app settings will be stored in
a section of the config file named "default," so the rest of the
file is yours to do with as you wish. See the example implemetation
of an C<rsync> wrapper, below, for one use of this.
=head1 DESCRIPTION
Good command-line scripts always support command-line options using
Getopt::Long, and I<should> support default configuration in a file
in a standard format like YAML, JSON, XML, INI, etc. At minimum
it should include a C<--help> option that explains the other
options. Supporting all this takes quite a bit of boilerplate code.
In my experience, doing it right takes several hundred lines of
code that are practically the same in every script.
C<CLI::Startup> is intended to factor away almost all of that
boilerplate. In the common case, all that's needed is a single
hashref listing the options (using C<Getopt::Long> syntax) as keys,
and a bit of help text as values. C<CLI::Startup> will automatically
generate the command-line parsing, reading of an optional config
file, merging of the two, and creation of a hash of the actual
settings to be used for the current invocation. It automatically
prints a usage message when it sees invalid options or the C<--help>
( run in 1.260 second using v1.01-cache-2.11-cpan-39bf76dae61 )