The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

OpenInteract2::Config::Ini - Read/write INI-style (++) configuration files

SYNOPSIS

 # If no 'directory' specified @INCLUDE directives are assumed to be
 # in the same directory as the 'filename'

 my $config = OpenInteract2::Config::Ini->new({
     filename => 'myconf.ini'
 });
 
 # Pass in an explicit directory to resolve @INCLUDE directives
 
 my $config = OpenInteract2::Config::Ini->new({
     filename  => 'myconf.ini',
     directory => '/path/to/config',
 });
 
 # Use a string with INI sections instead of a file; @INCLUDE
 # directives assumed to be in the current directory
 
 my $config = OpenInteract2::Config::Ini->new({
     content  => $some_string,
 });

 # Pass in an explicit directory to resolve @INCLUDE directives
 
 my $config = OpenInteract2::Config::Ini->new({
     content   => $some_string,
     directory => '/path/to/config',
 });
 
 # Use the configuration just like a hash
 print "Main database driver is:", $config->{datasource}{main}{driver}, "\n";
 $config->{datasource}{main}{username} = 'mariolemieux';

 # Write out the configuration; this should preserve order and
 # comments
 
 $config->write_file;

DESCRIPTION

This is a very simple implementation of a configuration file reader/writer that preserves comments and section order, enables multivalue fields, enables multi-line values, and has one or two-level sections.

Yes, there are other configuration file modules out there to manipulate INI-style files. But this one takes several features from them while providing a very simple and uncluttered interface.

  • From Config::IniFiles we take comment preservation and the idea that we can have multi-level sections like:

     [Section subsection]
  • From Config::Ini and AppConfig we borrow the usage of multivalue keys:

     item = first
     item = second
  • From an idea I had in the shower we have another usage of multivalue keys:

     @,item = first, second

    The leading '@' indicates we are declaring multiple values, the next character ',' indicates that we are separating the values with a comma. So you could also have:

     @|item = first | second

    Similar to how we treat the '=' we swallow whitespace on both sides of the separation character. So the following evaluate to equivalent data structures:

     @,item = first,second
     @,item = first, second
     @,item = first  ,   second

    You can also use multiple declarations of these to define a single field. That should prevent configuration lines from getting too long:

     @,object_field = title, posted_on_date, author, editor
     @,object_field = approved_by, approved_on, is_active,
     @,object_field = active_on, content
  • From countless other configuration systems you can include the contents of other files inline using '@INCLUDE'. So given the following files:

     File: db_config.ini
     [db]
     dsn = DBI:Pg:dbname=foo
     user = foo
     password = bar
     
     File: caching.ini
     [cache]
     use = no
     expiration = 600
     class = OpenInteract2::Cache::File
     
     [cache params]
     directory = /path/to/cache
     lock_directory = /path/to/cache_lock

    You can them bring them all together in one with:

     File: server.ini
     [Global]
     version = 1.19
     timezone = America/New_York
     
     @INCLUDE = db_config.ini
     @INCLUDE = caching.ini

Example

Given the following configurations:

 [datasource]
 default_connection_db = main
 db                    = main
 db                    = other

 [db_info main]
 db_owner      =
 username      = captain
 password      = whitman
 dsn           = dbname=usa
 db_name       =
 driver_name   = Pg
 sql_install   =
 long_read_len = 65536
 long_trunc_ok = 0
 comment       = this is the database for mr whitman who \
 is not feeling very well as of late

 [db_info other]
 db_owner      =
 username      = tyger
 password      = blake
 dsn           = dbname=britain
 db_name       =
 driver_name   = Pg
 sql_install   =
 long_read_len = 65536
 long_trunc_ok = 0

You would get the following Perl data structure:

 $config = {
   datasource => {
      default_connection_db => 'main',
      db                    => [ 'main', 'other' ],
   },
   db_info => {
      main => {
           db_owner      => undef,
           username      => 'captain',
           password      => 'whitman',
           dsn           => 'dbname=usa',
           db_name       => undef,
           driver_name   => 'Pg',
           sql_install   => undef,
           long_read_len => '65536',
           long_trunc_ok => '0',
           comment       => 'this is the database for mr whitman who is not feeling very well as of late',
      },
      other => {
           db_owner      => undef,
           username      => 'tyger',
           password      => 'blake',
           dsn           => 'dbname=britain',
           db_name       => undef,
           driver_name   => 'Pg',
           sql_install   => undef,
           long_read_len => '65536',
           long_trunc_ok => '0',
      },
   },
 };

'Global' Key

Anything under the 'Global' key in the configuration will be available under the configuration object root. For instance:

 [Global]
 DEBUG = 1

will be available as:

 $CONFIG->{DEBUG}

METHODS

Class Methods

new( \%params )

Create a new configuration object. If you pass in 'filename' as a parameter we will parse the file and fill the returned object with its values.

If you pass in raw INI text in the parameter 'content' we try to translate it using the same means as reading a file.

NOTE: THIS DOES NOT WORK YET And if you pass in a hashref in the parameter 'struct' we attempt to map its keys and values to the internal format which can then be saved as normal. This will throw an exception if your structures are nested too deeply. For instance, this would be ok:

 my $foo = {
    top_key => { myvalue => 1, yourvalue => [ 'one', 'two' ] },
    bottom_key => { other => { mine => '1', yours => 2 }, bell => 'weather' },
 };

As it would represent:

 [top_key]
 myvalue = 1
 yourvalue = one
 yourvalue = two

 [bottom_key]
 bell = weather

 [bottom_key other]
 mine = 1
 yours = 2

But the following has references nested too deeply:

 my $foo = {
    top_key => {
        myvalue => 1,
        yourvalue => [ 'one', 'two' ]
    },
    bottom_key => {
        other => {
            mine => {              <--- this key's value is too deep
                zaphod => 'towel',
            },
            yours => {             <--- this key's value is too deep
               abe => 'honest',
            },
        }
        bell => 'weather',
    },
 };

Returns: a new OpenInteract2::Config::Ini object

Object Methods

as_data()

Get the data back from the object as an unblessed hash reference.

sections()

Returns a list of available sections.

get( $section, [ $sub_section ], $parameter )

Returns the value from $section (and $sub_section, if given) for $parameter.

Returns: value set in config. If called in array context and there are multiple values for $parameter, returns an array. Otherwise returns a simple scalar if there is one value, or an arrayref if multiple values.

set( $section, [ $sub_section ], $parameter, $value )

Set the key/value $parameter/$value pair in the configuration. Note that $value can be a simple scalar or an array reference.

Returns: the value set

delete( $section, [ $sub_section ])

Remove the $section (and $sub_section, if given) entirely.

Returns: the value deleted

write_file( $filename )

Serializes the INI file (with comments, as applicable) to $filename.

Note: this DOES NOT write any '@INCLUDE' directives back to the files we read them from. Everything will be written to the same file. (Patches welcome if you would like to change this, probably by tagging the sections read in from a file with that absolute filename and then writing those sections back out to the file from this method.)

Items from the config object root go into 'Global'.

Returns: the filename to which the INI structure was serialized.

Debugging Note

Configuration input and output can generate a ton of logging information, so it uses a separate logging category 'LOG_CONFIG' as imported from OpenInteract2::Constants. Set this to DEBUG with fair warning...

Internal Methods

_translate_ini( \@lines|$content )

Translate the arrayref \@lines or the scalar content from INI format into a Perl data structure. Before we translate them into a data structure we first resolve all '@INCLUDE' directives.

Returns: the object filled with the content.

_resolve_all_includes( \@lines )

Translate all '@INCLUDE' directives to the configuration they point to. Throws an exception if we cannot read the file specified in the directive. This file path is created by giving to catfile in File::Spec the metadata value 'directory' (which can be passed in to new()) and the filename in the directive.

Note that INCLUDE-ed files can themselves have '@INCLUDE' directives.

Returns: arrayref of fully-resolved configuration.

_read_section_head( $full_section, \@comments )

Splits the section into a section and sub-section, returning a two-item list. Also puts the full section in the object internal order and puts the comments so they can be linked to the section.

Returns: a two-item list with the section and sub-section as elements. If the section is only one-level deep, it is the first and only member.

_read_item( $section, $subsection, $parameter, $value )

Reads the value from [$section $subsection] into the object. If the $section is 'Global' we set the $parameter and $value at the root level.

Returns: the value set

_set_value( \%values, $parameter, $value )

Note that $value can be a simple scalar or an array reference.

Sets $parameter to $value in \%values. We do not care where \%values is in the tree.

If a value already exists for $parameter, we make the value of $parameter an arrayref and push $value onto it.

_output_section( $section, $sub_section )

Serializes the section $section and $sub_section.

Returns: a scalar suitable for output.

_show_item( $parameter, $value )

Serialize the key/value pair.

Returns: "$parameter = $value"

SEE ALSO

AppConfig

Config::Ini

Config::IniFiles

COPYRIGHT

Copyright (c) 2001-2004 Chris Winters. All rights reserved.

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

AUTHORS

Chris Winters <chris@cwinters.com>