NAME

Kelp::Module::Config - Configuration for Kelp applications

DESCRIPTION

This is one of the two modules that are automatically loaded for each and every Kelp application. The other one is Kelp::Module::Routes. It reads configuration files containing Perl-style hashes, and merges them depending on the value of the application's mode attribute.

The main configuration file name is config.pl, and it will be searched in the conf and ../conf directories. You can also set the KELP_CONFIG_DIR environmental variable with the path to the configuration files.

This module comes with some default values, so if there are no configuration files found, those values will be used. Any values from configuration files will add to or override the default values.

ORDER

First the module will look for conf/config.pl, then for ../conf/config.pl. If found, they will be parsed and merged into the default values. The same order applies to the mode file too, so if the application mode is development, then conf/development.pl and ../conf/development.pl will be looked for. If found, they will also be merged to the config hash.

ACCESSING THE APPLICATION

The application instance can be accessed within the config files via the app keyword.

{
    bin_path => app->path . '/bin'
}

INCLUDING FILES

To include other config files, one may use the include keyword.

# config.pl
{
    modules_init => {
        Template => include('conf/my_template.pl')
    }
}

# my_template.pl
{
    path => 'views/',
    utf8 => 1
}

Any config file may be included as long as it returns a hashref.

MERGING

The first configuration file this module will look for is config.pl. This is where you should keep configuration options that apply to all running environments. The mode-specific configuration file will be merged to this config, and it will take priority. Merging is done as follows:

Scalars will always be overwritten.
Hashes will be merged.
Arrays will be overwritten, except in case when the name of the array contains a sigil as follows:
  • + in front of the name will add the elements to the array:

    # in config.pl
    {
        middleware => [qw/Bar Foo/]
    }
    
    # in development.pl
    {
        '+middleware' => ['Baz']    # Add 'Baz' in development
    }
  • - in front of the name will remove the elements from the array:

    # in config.pl
    {
        modules => [qw/Template JSON Logger/]
    }
    
    # in test.pl
    {
        '-modules' => [qw/Logger/]  # Remove the Logger modules in test mode
    }
  • No sigil will cause the array to be completely replaced:

    # in config.pl
    {
        middleware => [qw/Bar Foo/]
    }
    
    # in cli.pl
    {
        middleware => []    # No middleware in CLI
    }

Note that the merge sigils only apply to arrays. All other types will keep the sigil in the key name:

# config.pl
{
    modules      => ["+MyApp::Fully::Qualified::Name"],
    modules_init => {
        "+MyApp::Fully::Qualified::Name" => { opt1 => 1, opt2 => 2 }
    }
}

# development.pl
{
    modules_init => {
        "+MyApp::Fully::Qualified::Name" => { opt3 => 3 }
    }
}

REGISTERED METHODS

This module registers the following methods into the underlying app:

config

A wrapper for the /get method.

# Somewhere in the app
my $pos = $self->config('row.col.position');

# Gets {row}->{col}->{position} from the config hash

config_hash

A reference to the entire configuration hash.

my $pos = $self->config_hash->{row}->{col}->{position};

Using this or config is entirely up to the application developer.

_cfg

A tiny object that contains only three methods - merge, clear and set. It allows you to merge values to the config hash, clear it completely or set it to an entirely new value. This method comes handy when writing tests.

# Somewhere in a .t file
my $app = MyApp->new( mode => 'test' );

my %original_config = %{ $app->config_hash };
$app->_cfg->merge( { middleware => ['Foo'] } );

# Now you can test with middleware Foo added to the config

# Revert to the original configuration
$app->_cfg->set( \%original_config );

ATTRIBUTES

This module implements some attributes, which can be overridden by subclasses.

ext

The file extension of the configuration files. Default is pl.

separator

A regular expression for the value separator used by "get". The default is qr/\./, i.e. a dot.

path

Specifies a path, or an array of paths where to look for configuration files. This is particularly useful when writing tests, because you can set a custom path to a peculiar configuration.

data

The hashref with data contained in all of the merged configurations.

METHODS

The module also implements some methods for parsing the config files, which can be overridden in extending classes.

get

get($string)

Get a value from the config using a separated string.

my $value = $c->get('bar.foo.baz');
my $same  = $c->get('bar')->{foo}->{baz};
my $again = $c->data->{bar}->{foo}->{baz};

By default the separator is a dot, but this can be changed via the "separator" attribute.

load

load(filename)

Loads, and parses the file $filename and returns a hash reference.

process_mode

process_mode($mode)

Finds the file (if it exists) corresponding to $mode, parses it and merges it into the data. Useful, when you want to process and extra config file during the application initialization.

# lib/MyApp.pm
sub build {
    $self->loaded_modules->{Config}->process_mode( 'more_config' );
}

DEFAULTS

This module sets certain default values. All of them may be overridden in any of the conf/ files. It probably pays to view the code of this module and look and the defaults sub to see what is being set by default, but here is the short version:

charset

UTF-8

app_url

http://localhost:5000

modules

An arrayref with module names to load on startup. The default value is ['JSON', 'Template']

modules_init

A hashref with initializations for each of the loaded modules, except this one, ironically.

middleware

An arrayref with middleware to load on startup. The default value is an empty array.

middleware_init

A hashref with initialization arguments for each of the loaded middleware.

SUBCLASSING

You can subclass this module and use other types of configuration files (for example YAML). You need to override the ext attribute and the load subroutine.

package Kelp::Module::Config::Custom;
use Kelp::Parent 'Kelp::Module::Config';

# Set the config file extension to .cus
attr ext => 'cus';

sub load {
    my ( $self, $filename ) = @_;

    # Load $filename, parse it and return a hashref
}

1;

Later ...

# app.psgi
use MyApp;

my $app = MyApp->new( config_module => 'Config::Custom' );

run;

The above example module will look for config/*.cus to load as configuration.

TESTING

Since the config files are searched in both conf/ and ../conf/, you can use the same configuration set of files for your application and for your tests. Assuming the all of your test will reside in t/, they should be able to load and find the config files at ../conf/.

ENVIRONMENT VARIABLES

KELP_CONFIG_WARN

This module will not warn for missing config and mode files. It will silently load the default configuration hash. Set KELP_CONFIG_WARN to a true value to make this module warn about missing files.

$ KELP_CONFIG_WARN=1 plackup app.psgi