NAME

Config::Settings - Parsing pleasant configuration files

SYNOPSIS

# myapp.settings

hello {
  world 1;
};

# myapp.pl

use Config::Settings;

my $settings = Config::Settings->new->parse_file ("myapp.settings");

print "Hello world!\n" if $settings->{hello}->{world};

DESCRIPTION

Rationale

The first thing that probably comes to most people's mind when they see this module is "Why another Config:: module?". So I feel I should probably first explain what motivated me to write this module in the first place before I go into more details of how it works.

There are already numerous modules for doing configuration files available on CPAN. YAML appears to be a prefered module, as do Config::General. There are of course also modules like Config::Any which lets be open to many formats instead of being bound to any particular one, but this modules only supports what is already implemented in another module so if one feels one is not entirely happy with any format with an implementation on CPAN, it doesn't really doesn't solve the fundamental issue that was my incentive to implement a new format.

So let us have a look at the other formats. As previously mentioned, one of the more popular formats today appears to be YAML. YAML isn't really a configuration file format as such, it's a serialization format. It's just better than the more riddiculous alternatives like say XML. It's well documented which is an important feature and reading it, unlike XML, doesn't require a whole lot of brain power for either a human or a machine. A problem with YAML is the whitespace and tab sensitivity. Some will of course not call this a problem. After all, python is constructed on the very same principle, but this isn't python. This is perl. Chances are that if a python-ish structure had been more appropriate for your brain, you would already be using python and not reading the documentation for this module.

But more importantly, this sensitivity is also a problem for people who are not familiar with the format. When I work on a Catalyst project, I seldom work alone. I work with graphic designers, I work with administrators, I work with a lot of people who is not likely to ever have encountered YAML before. Now, YAML *is* easy to read, but unfortunately it's not always easy to write. And sometimes, these people who I am working with needs to make a change to the settings for an application. They make the change, hit tab a few times to make the element position correctly, save the file, and voila it explodes without it really being obvious why.

A different format that has recently become more popular is the Config::General module. This module has adopted the format used by Apache. It's a mixture of markup language and simple key/value pairs. And in light of what I talked about with regards to YAML, this certainly is a better alternative. More people has configured Apache, and even if they haven't it's still more obvious how to modify the configuration file. The syntax of the format is to a much larger extent self-documenting and this is an important feature for a configuration file format. So what is the problem with this module?

For starters, it occationally becomes *too* simple. There is for instance no way of constructing a single element array in it, or really, a good way of specifying an array at all. An array in the Config::General sense is more about a directive being specified multiple times, not constructing arrays. However, I can see why the decision to keep this out of the configuration format was made. Staying true to the Apache format and allowing real arrays really cannot be done. Another thing that bothers me about this format is the weird way it uses something that looks like a markup language to declare sections. I don't like this, I always tend to forget closing tags for more complicated data structures. Such structures rarely exists in a real Apache configuration file, but are very common in a configuration file for a perl program. And the closing tags are also uneccesarily long. Their long name does nothing to help me remember which closing tag belongs to which starting tag, it's really just noise in a configuration file.

Design goals

In the rationale I layed out above, I pointed out some important qualities in a configuration file format.

It must be easy to read.
It must be easy to write.
The syntax must to a high degree be self-documenting.
It should still allow somewhat complex data structures.
It should not have riddiculously redundant syntax as its only option.

These qualities can sometimes be incompatible with each other, depending on how they are achieved. And there's in any case no such thing as a perfect solution. The best one can hope to achieve is something we can be comfortable with.

One configuration format I've been happy with previously has been the BIND (The nameserver) configuration format. It's very C/perl-ish, allows you to express somewhat complex configuration structures with a very simlpe syntax, and most importantly, it just "feels right". So I looked around on CPAN to see if I could find a parser that would allow me to parse something that was at least somewhat similar, but couldn't find any. So I started this module as a project both to see if a reasonable parser for such a format could be made and to learn how to use the Parse::RecDescent module.

# This is an example from the default named.conf on my system.

zone "localhost" {
  type master;
  file "master/localhost-forward.db";
};

METHODS

new

my $parser = Config::Settings->new;

Constructs a new configuration file parser. See below for constructor arguments.

parse

my $settings = $parser->parse ($string);

Parses a text string. This will soon be extended to also allow references and filehandles, but right now only plain strings are supported.

parse_file

my $settings = $parser->parse_file ($filename);

Sugar for parsing the content of a given file.

CONSTRUCTOR ARGUMENTS

symbol_table

my $parser = Config::Settings->new (symbol_table => {});

Specifies a custom symbol table to use.

EXAMPLES

A Catalyst application

 name "MyApp";

 Model::MyApp {
   schema_class "MyApp::Schema";

   connect_info {
     dsn        "dbi:SQLite:dbname=__HOME__/db/myapp.db";
     AutoCommit 1;
   };
 };

 View::TT {
   ENCODING           "UTF-8";
   TEMPLATE_EXTENSION ".html";
   INCLUDE_PATH       "__HOME__/templates";
 };

 Plugin::Authentication {
   default_realm "members";

   realms {
     members {
       credential {
         class              "Password";
         password_field     "password";
         password_type      "hashed";
         password_hash_type "SHA-256";
       };

       store {
         class      "DBIx::Class";
         user_model "MyApp::User";
       };
     };
   };
 };

SEE ALSO

Config::General

BUGS

Most software has bugs. This module probably isn't an exception. If you find a bug please either email me, or add the bug to cpan-RT.

AUTHOR

Anders Nor Berle <berle@cpan.org>

COPYRIGHT AND LICENSE

Copyright 2008 by Anders Nor Berle.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.