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

NAME

MMDS::Properties -- Flexible properties handling for MMDS

SUMMARY

    use MMDS::Properties;

    my $cfg = new MMDS::Properties;

    # Preset a property.
    $cfg->set_property("config.version", "1.23");

    # Parse a properties file.
    $cfg->parsefile("config.prp");

    # Get a property value
    $version = $cfg->get_property("config.version");
    # Same, but with a default value.
    $version = $cfg->get_property("config.version", "1.23");

    # Get the list of subkeys for a property, and process them.
    my $aref = $cfg->get_property_keys("item.list");
    foreach my $item ( @$aref ) {
        if ( $cfg->get_property("item.list.$item") ) {
            ....
        }
    }

DESCRIPTION

The property mechanism is modelled after the Java implementation of properties.

In general, a property is a string value that is associated with a key. A key is a series of names (identifiers) separated with periods. Names are treated case insensitive. Unlike in Java, the properties are really hierarchically organized. This means that for a given property you can fetch the list of its subkeys, and so on. Moreover, the list of subkeys is returned in the order the properties were defined.

Property lookup can use a preset property context. If a context ctx has been set using set_context('ctx'), get_property('foo.bar') will first try 'ctx.foo.bar' and then 'foo.bar'. get_property('.foo.bar') (note the leading period) will only try 'ctx.foo.bar' and raise an exception if no context was set.

Design goals:

  • properties must be hierarchical of unlimited depth;

  • manual editing of the property files (hence unambiguous syntax and lay out);

  • it must be possible to locate all subkeys of a property in the order they appear in the property file(s);

  • lightweight so shell scripts can use it to query properties.

METHODS

new

new is the standard constructor. new doesn't require any arguments, but you can pass it a list of initial properties to store in the resultant properties object.

new

clone is like new, but it takes an existing properties object as its invocant and returns a new object with the contents copied.

WARNING This is not yet a deep copy, so take care.

parsefile file [ , path [ , context ] ]

parsefile reads a properties file and adds the contents to the properties object.

file is the name of the properties file. This file is searched in all elements of path (an array reference) unless the name starts with a slash. Default path is . (current directory).

context can be used to designate an initial context where all properties from the file will be subkeys of.

For the detailed format of properties files see below.

get_property prop [ , default ]

Get the value for a given property prop.

If a context ctx has been set using set_context('ctx'), get_property('foo.bar') will first try 'ctx.foo.bar' and then 'foo.bar'. get_property('.foo.bar') (note the leading period) will only try 'ctx.foo.bar' and raise an exception if no context was set.

If no value can be found, default is used.

In either case, the resultant value is examined for references to other properties or environment variables. Such a reference looks like

   ${name}
   ${name:default}

name can be the name of an environment variable or property. If name is found in the environment, its value is substituted and the expansion process continues, re-examining the new contents, until no further substitutions can be made. If a non-empty value exists for the property name its value is used in a similar way. Hence an empty value for a property will be ignored. If no value can be found, the default string (not to be confused with the default parameter) will be returned.

As an additional service, a tilde ~ in what looks like a file name will be expanded to ${HOME}.

The method result_in_context can be used to determine how the result was obtained. It will return a non-empty string indicating the context in which the result was found, an emptry string indicating the result was found without context, or undef if no value was found at all.

get_property_noexpand prop [ , default ]

This is like get_property, but does not do any expansion.

gps prop [ , default ]

This is like get_property, but raises an exception if no value could be established.

This is probably the best and safest method to use.

get_property_keys prop

Returns an array reference with the names of the (sub)keys for the given property. The names are unqualified, e.g., when properties foo.bar and foo.blech exist, get_property_keys('foo') would return ['bar', 'blech'].

expand value [ , context ]

Perform the expansion as described with get_property.

set_property prop, value

Set the property to the given value.

set_properties prop1 => value1, ...

Add a hash (key/value pairs) of properties to the set of properties.

set_context context

Set the search context. Without argument, clears the current context.

get_context

Get the current search context.

result_in_context

Get the context status of the last search.

Empty means it was found out of context, a string indicates the context in which the result was found, and undef indicates search failure.

dump [ start [ , stream ] ]

Produce a listing of all properties from a given point in the hierarchy and write it to the stream.

stream defaults to *STDOUT.

dumpx [ start [ , stream ] ]

Like dump, but dumps with all values expanded.

PROPERTY FILES

Property files contain definitions for properties. This module uses an augmented version of the properties as used in e.g. Java.

In general, each line of the file defines one property. The syntax of such a line can be:

 foo.bar = blech
 foo.xxx = "yyy"
 foo.zzz = 'xyzzy'

Whitespace has no significance. A colon : may be used instead of =. Lines that are blank or empty, and lines that start with # are ignored.

When several properties with a common prefix must be set, they can be grouped:

 foo {
    bar = blech
    xxx = "yyy"
    zzz = 'zyzzy'
 }

Groups (also known as contexts) may be nested.

Property files can include other property files:

 include "myprops.prp"

All properties that are read from the file are entered in the current context. E.g.,

 foo {
   include "myprops.prp"
 }

will enter all the properties from the file with an additional foo. prefix.

Property names consist of one or more identifiers (series of letters and digits) separated by periods.

Property values can be anything. Unless the value is placed between single quotes '', the value will be expanded before being assigned to the property.

Expansion means:

  • A tilde ~ in what looks like a file name will be replaced by ${HOME}.

  • If the value contains ${name}, name is first looked up in the current environment. If an environment variable name can be found, its value is substituted.

    If no suitable environment variable exists, name is looked up as a property and, if it exists and has a non-empty value, this value is substituted.

    Otherwise, the ${name} string is removed.

  • If the value contains ${name:value}, name is looked up as described above. If, however, no suitable value can be found, value is substitution.

This process continues until no modifications can be made.

Note that if a property is referred as ${.name}, name is looked up in the current context only.

BUGS

Although in production, this module is still slightly experimental and subject to change.

AUTHOR

Johan Vromans, V2R/R&D, Compuware Europe B.V.