I would most welcome comments regarding my proposed module Config::File.

It's plus points (for me) are: Update
After reading the replies below, my first thought was to modify it to use Safe, and add options to new to allow for carp, croak or undef on non-existant keys.
However,
After re-reading YAML and the specs, I've been won over! I see the light! I feel happy!
(I'm not kidding)

The POD

NAME

Config::File


SYNOPSIS

OO usage

use Config::File; $conf = Config::File->new( file => $file, path => $path ); print $conf->key;

Procedural usage, retrieving a hash

use Config::File 'config'; %hash = config( file => $file, path => $path ); print $hash{key};

Procedural usage, retrieving a hashref

use Config::File 'config'; $hashref = config( file => $file, path => $path ); print $hashref->{key};

DESCRIPTION

Alows complex configuration information such as nested arrays and hashes to be written in plain text files.

The config file must be in the form of an anonymous perl hash structure.

Using the OO method allows access to configuration values with a minimum of typing and also provides a safety mechanism, such that any call to a non-existant configuration key is fatal.

A procedural method 'config' is provided which will return the entire configuration data structure as a hash or hashref.

The procedural method is about 5% faster than the OO method. Using a hashref is about 1 - 2% faster than using a normal hash.


EXPORT

Exports 'config' subroutine on request.


EXAMPLE

For an example, running this code...

use Config::File; $conf = Config::File->new(file => 'my.conf'); print $conf->db_password, $/; print $conf->blocked_words->[0], $/; print $conf->emails->{webmaster}, $/;

...on the file 'my.conf'...

{ db_password => 'secret', blocked_words => ['list', 'of', 'words'], emails => { sales => 'sales@fireartist.com', webmaster => 'webmaster@fireartist.com', } }

...would give the output...

secret list webmaster@fireartist.com

And the code...

package Config::File; $VERSION = '0.30'; use strict; require Exporter; use vars qw($VERSION @ISA @EXPORT_OK $AUTOLOAD); @ISA = qw(Exporter); @EXPORT_OK = qw/config/; use Carp; use File::Spec; sub new { my $class = shift; my %arg = @_; my $self = {}; bless( $self, $class ); my $file = _file_path( $arg{file}, $arg{path} ); $self->{conf} = do($file) or croak('new() could not load config file'); return $self; } sub config { my $self = shift if ref $_[0]; my %arg = @_; my $file = _file_path( $arg{file}, $arg{path} ); my $conf = do($file) or croak('hash() could not load config file'); return (wantarray ? %{ $conf } : $conf); } sub _file_path { my ($file, $path) = @_; my $exists; croak('_file_path requires file arg') unless defined $file; if ($path && -e File::Spec->catfile( $path, $file )) { $exists = File::Spec->catfile( $path, $file ); } elsif ( -e $file ) { $exists = $file; } else { for (@INC) { if (-e File::Spec->catfile( $_, $file )) { $exists = File::Spec->catfile( $_, $file ); last; } } } croak('could not find config file') unless defined $exists; return $exists; } sub AUTOLOAD { my $self = shift; my $method = $AUTOLOAD; $method =~ s/.*://; unless (defined $self->{conf}->{$method}) { croak($method.' not defined in config file'); } return $self->{conf}->{$method} if not wantarray; if (ref $self->{conf}->{$method} eq 'HASH') { return %{ $self->{conf}->{$method} }; } elsif (ref $self->{conf}->{$method} eq 'ARRAY') { return @{ $self->{conf}->{$method} }; } else { return $self->{conf}->{$method}; } } sub DESTROY { } 1;

Replies are listed 'Best First'.
Re: RFC: Config::File
by Jenda (Abbot) on Feb 10, 2004 at 21:02 UTC
    1. Using Perl code as the config format is usualy not that a good idea. Especialy if there will be other people editing the config file(s). It's too easy to break something (or into something).
    2. I don't think croaking on keys not present in the config file is such a great idea. Undef always works better for me.
    3. I don't understand why would anyone want to separate the filename and path and have your module put them together.
    4. There are too many config related modules on CPAN already.

    You'd better use YAML or XML. IMHO of course.

    Jenda
    Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
       -- Rick Osborne

    Edit by castaway: Closed small tag in signature

      "You'd better use YAML or XML. IMHO of course."

      For simple config files, I've found that XML::Simple is a good solution.

      «Rich36»
Re: RFC: Config::File
by flyingmoose (Priest) on Feb 10, 2004 at 20:05 UTC
    Two comments:

    1 -- Seems like a wrapper to Data::Dumper with file loading built in (and required) with the addition of handling for non-existant values. Personally, I'll stick with Data::Dumper and just write to a file when needed, or I would use something like YAML -- which does a very similar thing (LoadFile) and is cross-platform.

    2 -- Why not use Data::Dumper internally to increase code reuse?

      I'm not really sure, but I'm wondering if you're looking at this backwards.
      This module only allows for loading a configuration file, not saving. Providing a filename is therefore always required.
      Data::Dumper wouldn't provide any code-reuse, as all the parsing is done by perl's own do function.
      I also like my code-editor providing syntax checking for the config file, unlike if the file were a YAML format.

      However, I hadn't really looked at YAML before and from what I've just seen, it does look interesting. - I'll remember it's there in the future.
      Thanks for your feedback.

        This module only allows for loading a configuration file, not saving. Providing a filename is therefore always required.
        Consider that RFE #1. It should support saving. A config file class that only allows for loading is missing a rather important feature if you want the config file to be manipulated by software (such as a GUI or web interface or command line) rather than by text editors. You won't see many config modules in other languages neglect to implement a save feature. It's just not done. CPAN has a few holes in that sort of logic here and there, mainly because it's community supported -- that's ok -- but I'd rather see the signal to noise ratio improved by including core features where applicable.
Re: RFC: Config::File
by hossman (Prior) on Feb 11, 2004 at 04:13 UTC
    • If you're going to use "do" you should pay more attention to the error variables it sets. Test $! and $@ (in that order) and provide an appropriate message that includes them so the user understands what they did wrong -- especially in the case of $@
    • If you're going to evaluate an arbitrary file, you should at least do some magic with Safe so that you don't have to worry about malicious people putting arbitrary code into an config file that was (mistakenly) left world writable.
    • Even if you don't use Safe, at the *least* you should but a nice big warning in the POD about the possibility of malicious code in config files
Re: RFC: Config::File
by hardburn (Abbot) on Feb 10, 2004 at 22:52 UTC

    If I was to write a config file in Perl syntax (which I know is bad, but sometimes I'm too lazy to get out a proper config file parser off CPAN), I would probably just write it and let it access the scalar values directly off the package, such as $My::Config::SOME_VAR. Apply Scalar::Readonly and you have most of the benifits of a read-only config file.

    ----
    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

      Quite a few web apps (TWiki, YABB, etc) have been known to have a "settings.pl" file that they simply use on startup. This does exactly what you mention above. Very simple, and works very very well. Non-perl coders have no trouble keeping the syntax as long as non-pathological data structures (I love pathological data structures!) are used.

      I suppose there is a security risk if permissions on settings.pl are not well known, obviously... but if you have a permissions problem on your web app, executing arbitrary local code is the least of many concerns.