Configuration files don't have to be ugly. Inspired by nginx configuration files I decided to go a bit further and implement a format which is highly readable, less error prone, requires little to no special markup, and yet is robust to allow nested blocks for the projects that require more than just a plain key-value list.
Currently Config::Neat is implemented as a suite of Perl modules which, in addition to parsing and rendering configuration files, implement automatic configuration file inheritance (aka includes) and validation against schema (see below).
In its simplest form, the configuration file can look like this:
# Server configuration
server Some string
port 8080
port 8081
use_ssl YES
You are not forced to enclose strings in quotes, or specify delimiters at the end of each line; you will never need to escape single or double quotes.
When it comes to having different (even nested) sections, multiline lists or strings, block comments, Config::Neat will offer you such an opportunity:
/*
Global server configuration
*/
server {
listen 8080
use_ssl YES
debug NO
log_format $remote_addr - $remote_user [$time]
$status $size $request
supported_mime_types text/html text/css text/xml text/plain
image/gif image/jpeg image/png image/x-icon
application/x-javascript
/*
My virtual hosts
*/
virtual_hosts {
www.domain.com {
root /var/www/domain
...
}
www.otherdomain.com {
root /var/www/otherdomain
...
}
}
}
See sample/readme.nconf
file, which gives a full overview
of the supported syntax.
Perl module is located in perl/lib
subdirectory.
It depends on Tie::IxHash
module available from CPAN.
use Config::Neat;
my $cfg = Config::Neat->new();
my $data = $cfg->parse_file('/path/to/myconfig.nconf');
# now $data contains a parsed hash tree which you can examine
# consider the example config above
my $list = $data->{'server'}->{'supported_mime_types'};
#
# $list now is an array reference:
# ['text/html', 'text/css', ..., 'application/x-javascript']
my $log_format = $data->{'server'}->{'log_format'}->as_string;
#
# $log_format now is a scalar:
# '$remote_addr - $remote_user [$time] $status $size $request'
This module adds config inheritance to Config::Neat files by automatically
processing @inherit file#subnode
, -somekey
and +otherkey
keys.
See Config::Neat::Inheritable source code
for further explanation.
This module adds config validation against provided schema (schema itself can be defined using Config::Neat format). See Config::Neat::Schema source code for further explanation.
This module allows you render Config::Neat-compatible structures from your data (but read below for limitations).
- When you need to convert your old configuration files to a new format (and then manually tweak the output).
- When you want to dump some data for diff purposes or just for reading.
- When readability of your output is more important than knowing original data types of each node in your data output.
Do not use it for arbitrary data serialization/deserialization. JSON and YAML will work better for this kind of task.
Why? Because Config::Neat was primarily designed to allow easier configuration file authoring and reading, and uses relaxed syntax where strings are treated like space-separated arrays (and vice versa), and where there's no strict definition for boolean types, no null values, etc.
It's the developer's responsibility to treat any given parameter as a boolean,
or string, or an array. This means that once you serialize your string into
Config::Neat format and parse it back, it will be converted to an array,
and you will need to use ->as_string
method to get the value as string.
In other words, when doing this:
my $c = Config::Neat->new();
my $r = Config::Neat::Render->new();
my $parsed_data = $c->parse($r->render($arbitrary_data));
$parsed_data will almost always be different from $arbitrary_data.
However, doing this immediately after:
my $parsed_data_2 = $c->parse($r->render($parsed_data));
Should produce the same data structure again.
use Config::Neat::Render;
my $r = Config::Neat::Render->new();
my $data = {
'foo' => 'Hello, World!',
'bar' => [1, 2, 3],
'baz' => {
'etc' => ['foo bar', 'baz', '', 1]
}
};
print $r->render($data);
The output will be:
bar 1 2 3
baz
{
etc `foo bar` baz `` 1
}
foo Hello, World!
Note that hashes in Perl do not guarantee the correct order, so blocks may have
individual parameters shuffled randomly. To sort the keys, you can provide a reference
to an ordered list of key names in the sort
option:
...
my @order = qw(foo bar baz);
print $r->render($data, {sort => \@order});
And now the output will be:
foo Hello, World!
bar 1 2 3
baz
{
etc `foo bar` baz `` 1
}
Alternatively, setting sort
to a true value will just sort keys alphabetically.
This script will read the configuration file and emit the parsed data structure in either Config::Neat::Render, Data::Dumper or JSON format. This script can be used to validate the syntax of the configuration file and understand its internal tree representation.
There are a few syntax highlighters available (see the highlighters
folder):
- for Sublime Text desktop editor (also compatible with TextMate)
- for Visual Studio Code
- for JavaScript-based editor called CodeMirror.
You can also use CodeMirror with Config::Neat highlighter to statically highlight configuration snippets/examples within web pages.