Anyone who's ever had to migrate from one config file format to another knows how messy it can get, so I'm thinking of starting work on a standard interface of accessor functions for the many different config file formats out there. Before I delve too deeply into this, I wanted to lay out my idea and get other monks' impressions and thoughts.
The example cases
An example for use with
Config::General:
ServerRoot "d:/wamp/apache2"
Timeout 300
Alias /icons/ "d:/wamp/Apache2/icons/"
<Directory "d:/wamp/Apache2/icons">
Options Indexes MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
Alias /manual/ "d:/wamp/Apache2/htdocs/manual/"
<Directory "d:/wamp/Apache2/htdocs/manual">
Options Indexes
AllowOverride None
Order allow,deny
Allow from all
<Files *.html>
SetHandler type-map
</Files>
SetEnvIf Request_URI ^/manual/(de|en|es|fr|ja|ko|ru)/ prefer-langu
+age=$1
RedirectMatch 301 ^/manual(?:/(de|en|es|fr|ja|ko|ru)){2,}(/.*)?$ /
+manual/$1$2
</Directory>
ScriptAlias /cgi-bin/ "d:/wamp/Apache2/cgi-bin/"
<Directory "d:/wamp/apache2/cgi-bin">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
An example for use with
Config::IniFiles:
security = user
workgroup = WORKGROUP
[homes]
comment = Home Directories
browseable = no
writable = yes
readonly = no
available = yes
public = no
An example for use with
YAML:
---
name: ingy
age: old
weight: heavy
# I should comment that I also like pink, but don't tell anybody.
favorite colors:
- red
- green
- blue
The syntax
use CFI;
# OBJECT = new( FORMAT );
# FORMAT - scalar string denoting the file format to use
# note that the case of the format wouldn't matter..."inifiles" and "I
+niFiles" would be equivalent
my $config_general = new CFI('general');
my $config_inifiles = new CFI('inifiles');
my $config_yaml = new CFI('yaml');
For existing
Config:: modules, someone (the original authors, if they'd like to) would write a wrapper specifically for this interface, such as
CFD::General for the existing
Config::General (again following the DBI/DBD example). And any new formats could be implemented directly under the standard interface. They would all implement a set of common accessor functions, up to the level of complexity that the file format supports. They should all have a way to read (and most should have a way to write) config files:
# BOOLEAN = ->read_config( SOURCE, OPTIONS );
# SOURCE - scalar containing filename, scalar ref containing file c
+ontents, or filehandle to read from
# OPTIONS - hash reference containing any driver-specific options
$config_general->read_config(\*CONFIG_FILE, { -AllowMultiOptions => 1,
+ -AutoTrue => 1 });
$config_inifiles->read_config('./test.ini', { -nocase => 1, -allowcont
+inue => 1, -commentchar => ';' });
$config_yaml->read_config(\$default_values);
# BOOLEAN = ->write_config( DESTINATION, OPTIONS );
# DESTINATION - scalar containing filename, or scalar ref to assign
+contents to, or filehandle to write to
# OPTIONS - hash reference containing any driver-specific option
+s
$config_general->write(\*CONFIG_OUTPUT);
$config_yaml->write('~/test.yml');
The programmer should be able to access deeply nested structures just as easily as flat or shallow structures. There would be at least three logical seperations of data...parameters, blocks, and attributes:
# VALUE = ->param( BLOCKS, FIELD );
# BLOCKS - name(s) of any number of logical blocks (or a single unde
+f to use default (root) block)
# FIELD - name of field to retrieve (or undef to retrieve all field
+names in given block)
# parameters are name:value pairs. if undef is passed as a parameter,
+ return a list of all parameters in current block
$config_general->param('Directory','d:/wamp/Apache2/htdocs/manual','Fi
+les','*.html','SetHandler'); # returns 'type-map'
$config_inifiles->param('homes',undef); # returns array ('comment','br
+owseable','writable','readonly','available','public')
$config_yaml->param('favorite colors'); # returns ['red','green','blue
+']
# VALUE = ->block( BLOCKS );
# BLOCKS - name(s) of any number of logical blocks (or a single unde
+f to use default (root) block)
# blocks are groups or sections that contain parameters
$config_general->block('Directory'); # returns ('d:/wamp/Apache2/icons
+','d:/wamp/Apache2/htdocs/manual','d:/wamp/apache2/cgi-bin')
$config_inifiles->block(); # returns ('homes')
$config_yaml->block(); # returns ('name','age','weight','favorite colo
+rs')
# HASHREF = ->export();
# this exports the entire configuration as a hashref structure. usefu
+l for converting one format to another
# BOOLEAN = ->import( CONFIG );
# CONFIG - hash reference containing an entire configuration
# this takes a structure of anonymous hashes and arrays and interprets
+ it as a config structure.
$config_general->import( $config_yaml->export() );
So thats the initial concept, it would become trivial to write an application that accepts multiple config file formats or converts from one format to another. Even just migrating an existing application from one format to another would be much easier. There will be issues, of course, since all possible config formats may not fit within this scheme, but I'm sure it could be improved.
Suggestions, comments, admonishments? I'm eager for feedback, positive or negative, but please, if you downvote me for this, at least tell me why :)