NAME
Panda::Config::Perl - Convenient and flexible config loader in perl format.
SYNOPSIS
myapp.conf
$db = {
host => 'db.myproj.com',
driver => 'Pg',
user => 'ilya',
password => 'rumeev',
};
$home = Path::Class::Dir->new('/home/myproj');
$var_dir = $home->subdir('var');
$log_dir = $var_dir->subdir('log'); $log_dir->mkpath(0, 0755);
$host ||= 'mysite.com';
$uri = URI->new("http://$host");
#include "chat.conf"
...
chat.conf
#namespace chat
$history_cnt = 10;
$tmp_dir = $NS::var_dir->subdir('chat');
$service_uri = URI->new( $NS::uri->as_string .'/chat' );
...
myapp.dev.conf
$dev = 1;
$db->{host} = 'dev.myproj.com';
$NS::chat::history_cnt += 5;
...other differences
local.conf
$host = 'dev.mysite.com';
#include "myapp.conf"
#include "myapp.dev.conf";
in application:
my $cfg = Panda::Config::Perl->process('myapp.conf');
print $cfg->{db}{user}; # ilya
print $cfg->{db}{host}; # db.myproj.com
print $cfg->{chat}{tmp_dir}; # Path::Class::Dir object (/home/myproj/var/chat)
print $cfg->{host}; # mysite.com
print $cfg->{uri}; # URI object http://mysite.com
print $cfg->{chat}{service_uri}; # URI object (http://mysite.com/chat)
$cfg = Panda::Config::Perl->process('local.conf', {hello => 'world'});
print $cfg->{db}{user}; # ilya
print $cfg->{db}{host}; # dev.myproj.com
print $cfg->{host}; # dev.mysite.com
print $cfg->{uri}; # URI object http://dev.mysite.com
print $cfg->{chat}{service_uri}; # URI object http://dev.mysite.com/chat
print $cfg->{chat}{history_cnt}; # 15
print $cfg->{hello}; # world
DESCRIPTION
This module provides you with powerful config system.
It allows you to:
Write variable definitions in perl language. You do not need to define a huge hash in config file - you just write separate variables.
$var = 'value'; $arr = [1..100];
These variables are accessible in config hash by name.
$cfg = Panda::Config::Perl->process(...); say length @{$cfg->{arr}};
Split your configs into separate files and include them.
#include "something.conf" #include $filename . ".conf" if ($something) { #include "something.conf" }
Access variables between configs.
$myvar = $NS::var + 10; # access variable 'var' from root namespace
Overwrite variables from another config file. Just change their values.
$NS::var++;
If both destination and source are hashrefs, they are merged
$NS::somehash = {a => 1}; $NS::somehash = {key => $NS::somehash->{a}}; ... exists $cfg->{somehash}{a}; # true exists $cfg->{somehash}{key}; # true
Use namespaces.
#namespace jopa $var = 10;
Everything under namespace jopa goes to $cfg->{jopa}{...}
This is very useful for big projects where your config might grow over 100kb.
CONFIG SYNTAX
Syntax is quite simple - it's perl. Just define variable with desired names.
$var_name = 'value';
Values can be any that scalars can be: scalar, hashref, arrayref, subroute, etc. DO NOT write use strict
or you will be forced to define variables via our
which is ugly for config.
If you define in myapp.conf (root config)
$welcome_msg = 'hello world';
it will be accessible via
$cfg->{welcome_msg}
Hashes acts as they are expected:
$msgs = {
welcome => 'hello world',
bye => 'bye world',
};
...
$cfg->{msgs}{bye};
It is a good idea to reuse variables in config to allow real flexibility:
$var_dir = $home->subdir('var');
$log_dir = $var_dir->subdir('log');
$chat_log_dir = $log_dir->subdir('chat');
...
In contrast to:
$var_dir = 'var';
$log_dir = 'log';
$chat_log_dir = 'chat';
or
$var_dir = 'var';
$log_dir = 'var/log';
$chat_log_dir = 'var/log/chat';
...will grow :(
The second and third examples are much less flexible. By means of second example we just hardcoded a part of config logic in our application: it supposes that var_dir
is UNDER home
and log_dir
is UNDER var_dir
, etc, which must not be an application's headache anyway. In third example we have a lot of copy-paste and application still supposes that var_dir
is under home.
NAMESPACES
Initial config file (that one which was passed to process
method) evaluated in root namespace. That means that all variables appears on the first level in $cfg
hash.
If you define a namespace then everything in that namespace goes under namespace key in $cfg
. You can change namespaces as many times as you like. Every namespace definition doesn't depend on namespace it defined in, and starts a namespace from current file's root namespace (which is a real root namespace in initial file).
initial.conf
$var = 10; # root namespace, $cfg->{var}
#namespace ns1
$var = 20; # $cfg->{ns1}{var}
#namespace ns1::subns
$var = 20; # $cfg->{ns1}{subns}{var}
#namespace ns2
$var = 30; # $cfg->{ns2}{var}
{
#namespace ns3
$var = 40; # $cfg->{ns3}{var}
}
$var2 = 50; # $cfg->{ns2}{var2}, because the last namespace in scope (#namespace ns2) is still in effect
#namespace
$var2 = 60; # $cfg->{var2}, because '#namespace' without a name reverts to the local root namespace
When you include a file it inherits current namespace and it becomes that file's local root namespace. It means that all namespaces that file defines are added to it's local root namespace.
initial.conf
#namespace abc
#include "my.conf"
my.conf
$var = 10; # $cfg->{abc}{var}
#namespace def
$var = 20; # $cfg->{abc}{def}{var}
#namespace xyz
$var = 30; # $cfg->{abc}{xyz}{var}
#namespace
$str = 'asd'; # $cfg->{abc}{str}
#namespace-abs hi
$str = 'ttt'; # $cfg->{hi}{str}
#namespace-abs
$str = 'yyy'; # $cfg->{str}
As you can see, #namespace-abs
defines a namespace which always starts from a real root namespace regardless of what local root namespace is set to.
Namespace name must be a literal string, variables and expressions are not supported.
INCLUDE
You can include another config file by saying
#include "path/to/file.conf"
As you could see in NAMESPACES
section, included file inherits current visible namespace as local root namespace. The path to config file can either be an absolute path, starting with '/' or path relative to including config file (not a current working dir!).
You can use any perl-valid expression as a filename
ACCESSING VARIABLES FROM OTHER CONFIG FILES
To access variable var
which is in root namespace, use
$NS::var
To access variable var
which is in namespace xxx::yyy
, use
$NS::xxx::yyy::var
$NS
stands for NameSpace root.
MERGING
When you change some hash contents (for example from a file which is conditionally included), instead of writing
$NS::db->{host} = 'another.host.com';
$NS::db->{user} = 'another_user';
$NS::db->{password} = 'otherpass';
...
You can write
$NS::db = {
host => 'another.host.com',
user => 'another_user',
password => 'otherpass',
...
};
All assigment operations in config files merge hashrefs if both left and right operands are hashrefs. Merge is done by Panda::Lib's merge
function with default flags, which does hash merging extremely fast.
If you really need to completely overwrite an existing hashref with a new value, use list assigment operator, because merging is only done for OP_SASSIGN, not for OP_AASSIGN.
($NS::db) = { ... };
CLASS METHODS
process ($file, \%initial_cfg)
Processes config $file
and returns the result as a hashref.
initial_cfg
is a set of predefined variables which will appear in root namespace just before processing config. You can predefine variables in an arbitrary namespace using keys ending with '::', for example:
Panda::Config::Perl->process($file, {
var => 'str',
'MyNS::' => { num => 10, 'SubNS::' => { array => [1,2,3] } },
'OtherNS::SubNS::' => { hash => {} },
});
$cfg->{var};
$cfg->{MyNS}{num};
$cfg->{MyNS}{SubNS}{array};
$cfg->{OtherNS}{SubNS}{hash};
PERFORMANCE
It takes about 7ms to process config tree with 30 files of summary size 220kb on Core i7 3930K.
SEE ALSO
This module deprecates Catalyst::Plugin::ConfigLoader::MultiState
AUTHOR
Pronin Oleg <syber@cpan.org>, Crazy Panda, CP Decision LTD
LICENSE
You may distribute this code under the same terms as Perl itself.