Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

RFC: Standard configuration interface...

by EvanK (Chaplain)
on Feb 18, 2007 at 20:31 UTC ( [id://600742]=perlmeditation: print w/replies, xml ) Need Help??

The concept

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.

Basically, we have many modules under the Config:: namespace out on CPAN, many of which serve different purposes and most of which have totally different interfaces. For the sake of the examples, I'll use the package name CFI (Config File Interface). The idea is to build a DBI-like abstraction layer where the programmer provides a format name, and can easily support or convert between any number of different formats. They would all support some common functions and could easily implement extended functionality as well.

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 :)

Update: cleaned up the (admittedly) convoluted code examples a bit, in response to chromatic's reply

Replies are listed 'Best First'.
Re: RFC: Standard configuration interface...
by LTjake (Prior) on Feb 19, 2007 at 01:42 UTC

    Does Config::Any do what you want? I know it doesn't have any write methods, but that could be patched in.

    --
    "Go up to the next female stranger you see and tell her that her "body is a wonderland."
    My hypothesis is that she’ll be too busy laughing at you to even bother slapping you.
    " (src)

      I had apparently overlooked this module originally, thanks for pointing it out! It's not as flexible as it could be, but I've contacted the author with some suggestions, and we'll see where that goes.

      __________
      Systems development is like banging your head against a wall...
      It's usually very painful, but if you're persistent, you'll get through it.

Re: RFC: Standard configuration interface...
by chromatic (Archbishop) on Feb 18, 2007 at 21:46 UTC

    Why do you write:

    # OBJECT = ->new( SCALAR format ); my $config_general = new CFI('general');

    ... and then write:

    # BOOLEAN = ->write_config( [SCALAR filename | GLOB filehandle] [, HAS +HREF options] ); $config_general->write(*CONFIG_OUTPUT);

    They're both methods. Why not make them look the same?

Re: RFC: Standard configuration interface...
by Herkum (Parson) on Feb 20, 2007 at 19:19 UTC
    I thought that was the point of Config::Auto? Isn't this basically doing the same thing?
      Good point, it does...to an extent. It doesn't have an OO interface, but that's more a matter of preference, I suppose. Mabye I'll just subclass Config::Auto and build on top of it.

      __________
      Systems development is like banging your head against a wall...
      It's usually very painful, but if you're persistent, you'll get through it.

Re: RFC: Standard configuration interface...
by ides (Deacon) on Feb 21, 2007 at 22:20 UTC

    I may be a bit biased here as it's something I helped create, but have you looked into Gantry::Conf? It does some of what you're after, and might be a good starting point. It doesn't "convert" the files, but it does create an abstraction so the application doesn't need to care "how" the admin/implementor/etc choose to config it.

    Also, just an FYI, but Gantry::Conf doesn't rely on any of Gantry itself, so it could easily be moved into its own distribution if this type of idea catches on with the community.

    Frank Wiles <frank@revsys.com>
    www.revsys.com

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://600742]
Approved by marto
Front-paged by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (2)
As of 2024-03-19 06:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found