=head1 NAME

Panda::Config::Perl - Convenient and flexible config loader in perl format.

=head1 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

=head1 DESCRIPTION

This module provides you with powerful config system.

It allows you to:

=over

=item

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}};

=item

Split your configs into separate files and include them.

    #include "something.conf"
    #include $filename . ".conf"
    if ($something) {
        #include "something.conf"
    }

=item

Access variables between configs.

    $myvar = $NS::var + 10; # access variable 'var' from root namespace

=item

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
   

=item

Use namespaces.

    #namespace jopa
    $var = 10;
    
Everything under namespace jopa goes to $cfg->{jopa}{...}

=back

This is very useful for big projects where your config might grow over 100kb.

=head1 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 C<use strict> or you will be forced to define variables via C<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 C<var_dir> is UNDER C<home> and C<log_dir> is UNDER C<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 C<var_dir> is under home.

=head1 NAMESPACES

Initial config file (that one which was passed to C<process> method) evaluated in root namespace. That means that all variables
appears on the first level in C<$cfg> hash.

If you define a namespace then everything in that namespace goes under namespace key in C<$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, C<#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.

=head1 INCLUDE

You can include another config file by saying

    #include "path/to/file.conf"
    
As you could see in C<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

=head1 ACCESSING VARIABLES FROM OTHER CONFIG FILES

To access variable C<var> which is in root namespace, use

    $NS::var
    
To access variable C<var> which is in namespace C<xxx::yyy>, use

    $NS::xxx::yyy::var
    
C<$NS> stands for NameSpace root.

=head1 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 L<Panda::Lib>'s C<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) = { ... };

=head1 CLASS METHODS

=head4 process ($file, \%initial_cfg)

Processes config C<$file> and returns the result as a hashref.

C<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};

=head1 PERFORMANCE

    It takes about 7ms to process config tree with 30 files of summary size 220kb on Core i7 3930K.

=head1 SEE ALSO

This module deprecates L<Catalyst::Plugin::ConfigLoader::MultiState>

=head1 AUTHOR

Pronin Oleg <syber@cpan.org>, Crazy Panda, CP Decision LTD

=head1 LICENSE

You may distribute this code under the same terms as Perl itself.

=cut