Panda::Config::Perl - Convenient and flexible config loader in perl format.
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
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.
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.
use strict
our
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.
var_dir
home
log_dir
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.
process
$cfg
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.
#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-abs
Namespace name must be a literal string, variables and expressions are not supported.
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!).
NAMESPACES
You can use any perl-valid expression as a filename
To access variable var which is in root namespace, use
var
$NS::var
To access variable var which is in namespace xxx::yyy, use
xxx::yyy
$NS::xxx::yyy::var
$NS stands for NameSpace root.
$NS
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 Data::Recursive's merge function with default flags, which does hash merging extremely fast.
merge
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) = { ... };
Processes config $file and returns the result as a hashref.
$file
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:
initial_cfg
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};
It takes about 7ms to process config tree with 30 files of summary size 220kb on Core i7 3930K.
This module deprecates Catalyst::Plugin::ConfigLoader::MultiState
Pronin Oleg <syber@cpan.org>, Crazy Panda, CP Decision LTD
You may distribute this code under the same terms as Perl itself.
To install Panda::Config::Perl, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Panda::Config::Perl
CPAN shell
perl -MCPAN -e shell install Panda::Config::Perl
For more information on module installation, please visit the detailed CPAN module installation guide.