The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

THIS MODULE DEPRECATED. USE Activator::Config instead.

Activator::Options - process options for a script combining command line, environment variables, and configuration files.

SYNOPSIS

  use Activator::Options;

  my $opts = Activator::Options->get_opts( \@ARGV);  # default realm
  my $opts = Activator::Options->get_opts( \@ARGV, $otherrealm);

  #### Get a hashref of command line arguments, and an arrayref of barewords
  my ( $argv, $barewords ) = Activator::Options->get_args( \@ARGV );

DESCRIPTION

This module allows a script to obtain options from command line, envirnoment variables, and YAML configuration files.

Prcedence Heirarchy

The precedence heirarchy from highest to lowest is:

  • command line options

  • environment variables

  • forced overrides from config files

  • merged settings from YAML configuration files

Configuration File Heirarchy

In order to facilite the varied ways in which software is developed, deployed, and used, the following heirarchy lists the configuration file heirarchy suported from highest to lowest:

  $ENV{USER}.yml - user specific settings
  <realm>.yml    - realm specific settings and defaults
  <project>.yml  - project specific settings and defaults
  org.yml        - top level organization settings and defaults

It is up to the script using this module to define what project is, and up to the project to define what realms exist, which all could come from any of the command line options, environment variables or 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 get_opts() function.

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 project is by insuring that $ENV{ACT_OPT_project} is set. This module will throw 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 --project option if you like.

Realms

This module supports the concept of realms to allow multiple similar configurations to override only the esential keys to "git 'er done".

Configuration Logic Summary

  • 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.

  • All realms are then merged with the default realm, realm config taking precedence.

  • All default realm environment variables override all values for each realm (excepting overrides realm).

  • All specific realm environment variables override that realm's values for the key.

  • The default realm overrides section is used to override matching keys in all realms.

  • The specific realm overrides section is used to override matching keys in the requested realm.

  • Any command line options given override ALL matching keys for all realms.

  • # TODO: NOT YET IMPLEMENTED

    Perform variable substitution

COMMAND LINE ARGUMENTS

This module allows long or short options using '-' or '--' notation, allows barewords in any order, and recognizes the arguments terminator '--'. 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. @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 (Getopt::Long for example). If you do use another options module, make sure you call 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.

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

ENVIRONMENT VARIABLES

Environment variables can be used to override any top level YAML configuration key which is a scalar. The expected format is 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

CONFIGURATION FILES

Realms

This module suports realms such that when passing a realm to get_opts() (or via the --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

Activator::Options->get_opts( \@ARGV ) would return:

$opts = { key1 => value1 }

and Activator::Options->get_opts( \@ARGV, 'realm' ) would return:

$opts = { key1 => value2 }

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 $opts hash. The special realm 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 $opts:

 $opts = {
    default => {
       name => 'Ron Johnson from Ronson, Wisconson',
    },
    some_realm => {
       name => 'Johnny Jammer, the Rhode Island Hammer',
    },
    other_realm => {
      name => 'Ron Johnson from Ronson, Wisconson',
    },
 }

Variable Substitution

#### TODO: NOT YET IMPLEMENTED

Substitution occurs as the last step of processing. Every value for every key (including values within lists ) are visited. Values for any key can optionally contain a reference to another key by using ${} notation. Use the indirect operator '->' to reference deeply nested values. For example:

  default:
    key1: value1
    key2: value2
  realm1:
    foo: bar
  realm2:
    key2: ${key1}
  realm3:
    key3: ${realm1->foo}/${key2}         # value == 'bar/value2'
    key4: ${realm1->foo}/${realm2->key2} # value == 'bar/value1'

Note that you must fully qualify any deeply nested references.

METHODS

new()

Constructor: implements singleton. Not very useful. Use get_opts().

get_opts()

Usage:

  Activator::Options->get_opts( \@ARGV );         # default realm
  Activator::Options->get_opts( \@ARGV, $realm );

Strip recognized options from @ARGV and return the configuration hash $opts for $realm based on @ARGV. $realm is optional (default is 'default'), and if not specified either the command line argument (--realm) or environment variable (ACT_OPT_<realm> unless ACT_OPT_skip_env is set) will be used. Not specifying a realm via one of these mechanisms is a fatal error.

Examples:

  #### get options for default realm
  my $opts = Activator::Options->get_opts( \@ARGV );

  #### get options for 'some' realm
  my $opts = Activator::Options->get_opts( \@ARGV, 'some' );

See get_args() for a description of the way command line arguments are processed.

get_args()

Usage: Activator::Options->get_args( $argv_raw )

Takes a reference to a list of command line arguments (usually \@ARGV) and returns a hash. $argv_raw is not changed.

  • Arguments can be barewords, '-' notation or '--' notation.

  • Any arguments after the arguments terminator symbol (a plain '--' argument) are returned as barewords. Bareword order of specification is maintained.

  • Values with spaces must be double-quoted, and can themselves contain quotes

      --mode="sliding out of control"
      --plan="pump the "brakes" vigorously"
  • Flag arguments are counted. That is -v -v would set $opts->{v} = 2

  • Argument bundling is not supported.

Examples:

  @ARGV                | Value returned
 ----------------------+-----------------------------------------
  --arg                | $argv = { arg => 1 }
  --arg --arg          | $argv = { arg => 2 }
  --arg=val            | $argv = { arg => 'val' }
  --arg=val --arg=val2 | $argv = { arg => [ 'val', 'val2' ] }
  --arg="val val"      | $argv = { arg => 'val val' }

Returns array: ( $args_hashref, $barewords_arrayref )

Throws Activator::Exception::Option when arg is invalid (which at this time is only when a barewod arg of '=' is detected).

DEBUG MODE

Since this module is part of Activator, you can set your Activator::Log level to DEBUG to see how your $opts are generated.

 #### TODO: in the future, there needs to be a 'lint' hash within the
 #### realm that says where every variable came from.

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!

End User

Use Case: A user has a CPAN module that provides 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

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

Simple Development

Use Case: developer is working on 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:
    db_name:   $USER
    db_user:   $USER
    db_passwd: passwd
  staging:
    db_name:   staging
    db_user:   test
    db_passwd: test

  #### when developing, call the script like this to lookup bean
  #### recipies from developers personal db
  cd $HOME/src/Cookbook
  bin/cookbook.pl lookup beans

  #### To demo the project to someone else, developer creates a demo
  #### account, which has the environment variable ACT_OPT_realm set
  #### to 'staging'. demo user then uses the script as if it were
  #### installed, but connects to the staging database:
  cookbook.pl lookup beans

  #### if the developer wants to see what the demo user sees:
  cd $HOME/src/Cookbook
  bin/cookbook.pl --realm=staging lookup beans

TODO: complex development

Someday, we'll have a really neat example of all the goodness this module is capable of.

SEE ALSO

 L<Activator::Exception>
 L<Activator::Log>

AUTHOR

Karim A. Nassar

COPYRIGHT

Copyright (c) 2007 Karim A. Nassar <karim.nassar@acm.org>

You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.