pericmd 026: Configuration file support

Perinci::CmdLine supports reading configuration files. I had planned for an abstract configuration system, something like Config::Any but IOD-based, but as that is not ready yet and I needeed config file support immediately, I implemented the bare essentials.

Configuration is basically a way to use files to supply values for function arguments, just like command-line options which is also a way to use command-line to supply values for function arguments. It is useful for cases where using command-line options is cumbersome (e.g. the values are long/many) or insecure (e.g. supplying password).

Configuration is searched either in the user’s home directory (~/.config or ~) or global directory /etc (I’m not sure about the Windows equivalent for /etc, any input? So far I’ve only used File::HomeDir->my_home on Windows). Configuration file name is program_name + .conf. The format of configuration file is IOD, which is basically INI with some extra features (it’s more INI-compatible than other formats like TOML).

The configuration section maps to subcommand (if program does not have subcommands, just put the parameters outside any section). Configuration parameter maps to function argument name (without any translation to command-line options, so foo_bar function argument is specified as foo_bar instead of foo-bar or --foo-bar).

There is a concept of “profiles”, which lets you keep multiple sets of values in a single file by using section. You just need to add ” profile=PROFILENAME” to any configuration section to make a configuration belong to a certain profile.

Parameters outside any section will be applied to all subcommands and profiles.

Let’s see an example. Program myprog:

our %SPEC;
$SPEC{myfunc} = {
    v => 1.1,
    args => {
        user => {schema=>'str*', req=>1},
        pass => {schema=>'str*', req=>1},
    },
};
sub myfunc {
    my %args = @_;
    if ($args{user} eq 'ujang' && $args{pass} eq 'alu') {
        [200, "OK", "nasi goreng"];
    } elsif ($args{user} eq 'nyai' && $args{pass} eq 'nyiru') {
        [200, "OK", "sayur asem"];
    } else {
        [401, "Wrong password"];
    }
}

use Perinci::CmdLine::Any;
Perinci::CmdLine::Any->new(
    url => '/main/myfunc',
)->run;

If we run this program:

% ./myprog
ERROR 400: Missing required argument(s): pass, user

We can of course supply the user and pass arguments via command-line options (–user and –pass), but passing passwords over the command-line is unsafe due to ps ax and all. So let’s put them in a configuration file. In ~/myprog.conf:

user=ujang
pass=alu

Then when we run the program again:

% ./myprog
nasi goreng

Let’s put in several profiles in the config file:

[profile=u1]
user=ujang
pass=alu

[profile=u2]
user=nyai
pass=nyiru

When we run the program:

% ./myprog --config-profile u1 ;# we pick ujang
nasi goreng
% ./myprog --config-profile u2 ;# we pick nyai
sayur asem

You can override the value of function arguments from command-line, since the command-line has higher precedence:

% ./myprog --config-profile u1 --pass foo
ERROR 401: Wrong password

You can also customize the location of config file via --config-path or disable searching of config file via --no-config.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s