Activator
view release on metacpan or search on metacpan
lib/Activator/Options.pm view on Meta::CPAN
configuration files. All of the above files are optional and will be
ignored if they don't exist, however at least one configuration file
must exist for the C<get_opts()> function.
=head2 Configuration File Search Path
TODO: This functionality is not implemented yet. Currently, only
~/.activator.d/<project> is supported
The search path for configuration YAML files is listed below. The
first conf file for each level appearing in the following directories
will be utilized:
--conf_path=<paths_to_conf_dir> # colon separated list
--conf_files=<conf_files> # comma separated list
$ENV{ACT_OPT_conf_path}
$ENV{ACT_OPT_project_home}/.<$ENV{ACT_OPT_project}>.d/
$ENV{HOME}/.<$ENV{ACT_OPT_project}>.d/
/etc/<$ENV{ACT_OPT_project}>.d/
/etc/activator.d/ # useful for org.yml
It is up to the script to define what C<project> is by insuring that
C<$ENV{ACT_OPT_project}> is set. This module will throw
C<Activator::Exception::Option> it is not set by you or passed in as a
command line argument, so you could force the user to use the
C<--project> option if you like.
=head2 Realms
This module supports the concept of realms to allow multiple similar
configurations to override only the esential keys to "git 'er done".
=head2 Configuration Logic Summary
=over
=item *
All configuration files are read and merged together on a realm by
realm basis with higher precedence configuration files overriding
lower precedence. If duplicate level files exist in the Configuration
File Search Path, only the first discovered file is used.
=item *
All realms are then merged with the C<default> realm, I<realm> config
taking precedence.
=item *
All C<default> realm environment variables override all values for
each realm (excepting C<overrides> realm).
=item *
All specific realm environment variables override that realm's values
for the key.
=item *
The C<default> realm overrides section is used to override matching
keys in all realms.
=item *
The specific realm overrides section is used to override matching keys
in the requested realm.
=item *
Any command line options given override ALL matching keys for all
realms.
=item *
# TODO: NOT YET IMPLEMENTED
Perform variable substitution
=back
=head1 COMMAND LINE ARGUMENTS
This module allows long or short options using C<'-'> or C<'--'>
notation, allows barewords in any order, and recognizes the arguments
terminator C<'--'>. Also supported are multiple flag arguments:
#### turn on super verbosity. sets $opts->{v} = 2
myscript.pl -v -v
You can specify configured options at the command line for
override:
#### override the configuration file setting for 'foo'
myscript.pl --foo=bar
Command line overrides only apply to keys that exist in a
configuration file and any unrecognized options are ignored. C<@ARGV>
is modified to remove recognized options, leaving barewords and
unrecognized options in the order they were specified. This module has
no support for argument validation, and your script must handle
unrecognizd options and behave appropriately. There are numerous
modules on CPAN that can help you do that (L<Getopt::Long> for
example). If you do use another options module, make sure you call
C<get_opts()> BEFORE you call their processor, so that @ARGV will be
in an appropriate state.
Also, while YAML configuration (and this module) support deep
structures for options, you can only override top level keys that are
scalars using command line arguments and/or environment variables.
=head2 Special Arguments
There are a few special command line arguments that do not require
YAML existence:
--skip_env : ignore environment variables
--project=<> : use this value for <project> in config file search path.
--project_home=<> : useful for when utilizing <project_home>/.<project> config dir
--realm=<> : use <realm>.yml in config file processing and consider all
command line arguments to be in this realm
TODO: these are not implemented yet
Also supported are these variables which can be listed as many times
as necessary:
--conf_file=<> : include <> file for inclusion
--conf_path=<> : include <> path when looking for config files
=head1 ENVIRONMENT VARIABLES
Environment variables can be used to override any top level YAML
configuration key which is a scalar. The expected format is
C<ACT_OPT_[key]>. Note that YAML is case sensitive, so the environment
variables must match. Be especially wary of command shell senstive
characters in your YAML keys.
If you wish to override a key for only a particular realm, you
can insert the realm into the env variable wrapped by double
underscores:
ACT_OPT_foo - set 'foo' for default realm
ACT_OPT__bar__foo - set 'foo' only for 'bar' realm
The special command line arguments listed in the COMMAND LINE
ARGUMENTS section also have corresponding environment variables with
minor differences in use:
ACT_OPT_skip_env : set to 1 to skip, or 0 (or don't set it at all) to
not skip
ACT_OPT_project : same as command line argument
ACT_OPT_project_home : same as command line argument
ACT_OPT_realm : same as command line argument
ACT_OPT_conf_file : comma separated list of files
ACT_OPT_conf_path : colon separated list of directories
=head1 CONFIGURATION FILES
=head2 Realms
This module suports realms such that when passing a realm to
L<get_opts()> (or via the C<--realm> command line argument), values
for the realm take precedence over the default realm's values. For
example, given YAML:
default:
key1: value1
realm:
key1: value2
C<Activator::Options-E<gt>get_opts( \@ARGV )> would return:
$opts = { key1 => value1 }
and C<Activator::Options-E<gt>get_opts( \@ARGV, 'realm' )> would return:
$opts = { key1 => value2 }
=head2 Overrides
Sometimes it is desireable to force a value to override the
organiztaion/project/realm value when many config files are merged to
create the C<$opts> hash. The special realm C<overrides> can be utilzed in
these cases, and will stomp any values that come from YAML
configurations. E.g.:
default:
name: David Davidson from Deluth, Delaware
some_realm:
name: Sally Samuelson from Showls, South Carolina
other_realm:
name: Ollie Oliver from Olive Branch, Oklahoma
overrides:
default:
name: Ron Johnson from Ronson, Wisconson
some_realm:
name: Johnny Jammer, the Rhode Island Hammer
Would produce the following C<$opts>:
$opts = {
default => {
name => 'Ron Johnson from Ronson, Wisconson',
},
some_realm => {
name => 'Johnny Jammer, the Rhode Island Hammer',
lib/Activator/Options.pm view on Meta::CPAN
my ( $self, $opts, $argv ) = @_;
my @barewords;
my @unrec;
# loop through $argv (which we assume to be a ref to @ARGV) and
# set any opts if they exist.
while ( my $arg = shift @$argv ) {
my ( $key, $value ) = $self->_get_arg( $arg );
# ignore barewords
if ( ! defined( $key ) ) {
DEBUG("Ignoring bareword '$arg'");
push @barewords, $arg;
next;
}
# finish up if we find terminator
if ( $key eq '--' ) {
DEBUG("Found arguments terminator --");
unshift @$argv, '--';
unshift @$argv, @barewords;
last;
}
# TODO: consider supporting realm specific command line arguments
#
# # skip this key if it is for a different realm
# if ( $key =~ /^__(\w+)__(\w+)$/ ) {
# $key_realm = $1;
# $key = $2;
# if( $realm ne $key_realm ) {
# push @unrec, $arg;
# next;
# }
# }
$opts->{ $key } = $value;
}
unshift @$argv, @unrec;
}
# do variable replacements throughout
sub _var_replace {
my ( $self, $opts, $replacements ) = @_;
# Activator::Registry->replace_in_hashref( $opts,
}
=head1 DEBUG MODE
Since this module is part of L<Activator>, you can set your
L<Activator::Log> level to DEBUG to see how your C<$opts> are
generated.
#### TODO: in the future, there needs to be a 'lint' hash within the
#### realm that says where every variable came from.
=head1 COOKBOOK
This section gives some examples of how to utilze this module. Each
section below (cleverly) assumes we are writing a Cookbook application
that can fetch recipies from a database.
#### TODO: these examples use currently unimplemented features. FIX IT!
=head2 End User
Use Case: A user has a CPAN module that provides C<cookbook.pl> to
lookup recipies from a database. The project installs these files:
/etc/cookbook.d/org.yml
/usr/lib/perl5/site-perl/Cookbook.pm
/usr/bin/cookbook.pl
C<org.yml> has the following data:
---
default:
db_name: cookbook
db_user: chef
db_passwd: southpark
The user can run the script as such:
#### list recipes matching beans in the organization's public db
#### using the public account
cookbook.pl lookup beans
#### lookup beans in user's db
cookbook.pl --db_name=my_db \
--db_user=cookie \
--db_passwd=cheflater lookup beans
#### user creates $HOME/$USER.yml
cookbook.pl --conf_file=$HOME/$USER.yaml lookup beans
#### user creates $HOME/.cookbook.d
cookbook.pl lookup beans
=head2 Simple Development
Use Case: developer is working on C<cookbook.pl>. Project directory
looks like:
$HOME/src/Cookbook/lib/Cookbook.pm
$HOME/src/Cookbook/bin/cookbook.pl
$HOME/src/Cookbook/etc/cookbook.d/org.yml
$HOME/src/Cookbook/.cookbook.d/$USER.yml
With these configurations:
org.yml:
---
default:
db_name: cookbook
db_user: chef
db_passwd: southpark
$USER.yml
---
default:
( run in 0.624 second using v1.01-cache-2.11-cpan-39bf76dae61 )