| Config-Objective documentation | Contained in the Config-Objective distribution. |
Config::Objective - Perl module for parsing object-oriented config files
use Config::Objective;
use Config::Objective::String;
use Config::Objective::List;
my $conf = Config::Objective->new('filename',
{
'var1' => Config::Objective::String->new(),
'var2' => Config::Objective::List->new(),
...
},
'include_dir' => '/usr/local/share/appname');
print "var1 = \"" . $conf->var1 . "\"\n";
The Config::Objective module provides a mechanism for parsing config files to manipulate configuration data. Unlike most other config file modules, which represent config data as simple variables, Config::Objective represents config data as perl objects. This allows for a much more flexible configuration language, since new classes can be easily written to add methods to the config syntax.
The Config::Objective class supports the following methods:
The constructor. The first argument is the filename of the config file to parse. The second argument is a reference to a hash that maps names to configuration objects.
The remaining arguments are interpretted as a hash of attributes for the object. Currently, the only supported attribute is include_dir, which specifies the directory to search for include files (see File Inclusion). If not specified, include_dir defaults to ".".
Once the constructor parses the config file, you can call the get() method of any of the objects by using the object name as an autoloaded method (see Recommended Methods).
Returns a reference to the object of the specified object name. The object name is the first argument.
Returns a list of known object names.
Returns a hash where the keys are the known object names and the values are the result of calling the get() method on the corresponding object.
The config file format supported by Config::Objective is described here.
Config::Objective supports three types of data: scalars, lists, and hashes. The syntax for these types is intentionally similar to their perl equivalents.
A scalar is represented as a simple integer or string value. If it is composed only of letters, numbers, and the underscore character, it can be written literally:
foo all_word_characters 123 alpha_123_numeric 4sure
However, if the value contains whitespace or other non-word characters, it must be quoted:
"telnet/tcp"
"use quotes for whitespace"
"quotes can be escaped like this \" inside quoted strings"
"quoted
strings
can span
multiple lines"
A list is represented as a sequence of comma-delimited values enclosed by square brackets:
[ this, is, a, list ]
Note that each value in a list can itself be a scalar, list, or hash:
[ this, is, a, [ nested, list ] ]
[ this, list, contains, a, { hash => value } ]
A hash is represented as a sequence of zero or more comma-delimited entries enclosed in curly braces:
{ this => 1, is => 2, a => 3, hash => 4 }
As in perl, each entry contains a key and a value. However, unlike perl, the value is optional:
{ this, is, a, hash, without, values }
{ this => hash, has => "some values", but, not, others }
When no value is specified for a given entry, its value is undefined.
Note that hash keys must always be scalars. However, values may be scalars, lists, or hashes:
{ "this is a" => [ list, within, a, hash ] }
{ "this is a" => { sub => hash } }
Each statement in the config file results in calling a method on a configuration object. The syntax is:
object[->method] [args];
In this syntax, "object" is the name of the object. The object must be created and passed to the Config::Objective constructor, as described above.
The "->method" portion is optional. If specified, it indicates which method should be called on the object. If not specified, a method called default() will be used.
The "args" portion is also optional. It specifies one or more comma-delimited arguments to pass to the method. If multiple arguments are provided, the entire argument list must be enclosed in parentheses. Each argument can be a simple scalar, list, hash, or a complex, nested list or hash structure, as described above.
So, putting this all together, here are some example configuration statements:
### call default method with no arguments
object;
### call a specific method, but still no args
object->method;
### call default method, but specify a single scalar argument
object scalar_arg;
### call default method, but specify a single list argument
object [ this, is, a, single, list, argument ];
### call a specific method and specify a single hash argument
object->method { this, is, a, single, hash, argument };
### call a specific method with multiple scalar args
object->method(arg1, arg2, arg3);
### call a specific method with multiple args of different types
object->method(scalar_arg, [ list, argument ], { hash => argument });
The config syntax also provides some rudementary support for conditional evaluation. A conditional directive is signalled by the use of a "%" character at the beginning of a line (i.e., no leading whitespace). There can be space between the "%" and the conditional directive, however, which can improve readability when using nested conditional blocks.
The conditional directives are %if, %else, %elif, and %endif. They can be used to enclose other config statements, which are evaluated or skipped based on whether the conditional expression evaluates to true. For example:
%if ( expression )
... other config directives ...
%endif
The most basic expression is simply a method call that returns a true or false value. The syntax for this is the same as a normal config statement, except without the trailing semicolon:
%if ( object[->method] [args] )
If no method is specified, the equals() method will be called by default.
Multiple expressions can be combined using the "&&", "||", and "!" operators. Additional parentheses can also be used for grouping within the expression. For example:
%if ( ( object1 foo && ! object2 bar ) || object3 baz )
File inclusion is another type of conditional evaluation. It allows you to include another file in the config file that is currently being parsed, similar to the C preprocessor's "#include" directive. The syntax is:
%include "filename"
If the specified filename is not an absolute path, Config::Objective will look for it in the directory specified by the include_dir attribute when the Config::Objective object was created.
Note that the %include directive will be ignored within an %if block whose condition is false. This means that you cannot start an %if block in one file, add a %include directive, and provide the %endif directive in the included file. All %if blocks must be fully contained within the same file.
Any text between a "#" character and the next newline is considered a comment. The "#" character loses this special meaning if it is enclosed in a quoted string or immediately preceded by a "\".
This section explains the details of how configuration objects are used.
There are no strict requirements for how a class must be designed in order to be used for a configuration object. The following methods are recommended in that they will be used by Config::Objective in certain circumstances, but they do not need to be present if they are not actually going to be used.
Return the value encapsulated by the object. This is used when you use call the variable name as a method of the Config::Objective object. For example:
print "var1 = '" . $conf->var1 . "'\n";
This will implicitly call the get() method of the object named var1.
This is the default method used when a configuration file references an object with no method.
This is the default method used when a configuration file references an object with no method as part of an expression. (See "Conditional Evaluation" above.)
Config::Objective supplies several classes that can be used for encapsulating common types of configuration data.
See the documentation for each of these classes for more information.
Mark D. Roth <roth@uiuc.edu>
perl.
| Config-Objective documentation | Contained in the Config-Objective distribution. |
### ### Copyright 2002-2003 University of Illinois Board of Trustees ### Copyright 2002-2003 Mark D. Roth ### All rights reserved. ### ### Config::Objective - Perl module for parsing object-oriented config files ### ### Mark D. Roth <roth@uiuc.edu> ### Campus Information Technologies and Educational Services ### University of Illinois at Urbana-Champaign ### package Config::Objective; use 5.006; use strict; use warnings; #use overload; use Parse::Lex; use Config::Objective::DataType; use Config::Objective::Parser; our $VERSION = '0.9.1'; our $AUTOLOAD; ############################################################################### ### internal functions for use by parser ############################################################################### sub _lexer { my ($parser) = @_; my ($token, $lexer); $lexer = $parser->YYData->{'lexer'}; # print "lexer = $lexer\n"; while (1) { $token = $lexer->next; if ($lexer->eoi) { # print "lexer returning EOI\n"; return ('', undef); } next if ($token->name eq 'COMMENT'); # print "lexer returning (" . $token->name . ", \"" . $token->text . "\")\n"; return ($token->name, $token->text); } } sub _error { my ($parser) = @_; my ($config, $lexer, $file, $line); $config = $parser->YYData->{'config'}; $file = $config->{'file_stack'}->[-1]; $lexer = $parser->YYData->{'lexer'}; $line = $lexer->line; die("$file:$line: parse error\n"); } sub _call_obj_method { my ($self, $obj, $method, @args) = @_; my ($retval, $line, $msg); # print "==> _call_obj_method('$obj', '$method'"; # map { print ", '$_'"; } @args # if (@args > 1 || defined($args[0])); # print ")\n"; die "$obj: unknown config object" if (!exists($self->{'objs'}->{$obj})); $method = 'default' if (!defined($method)); $retval = eval { $self->{'objs'}->{$obj}->$method(@args); }; if ($@) { if (@{$self->{'lexer_stack'}}) { $line = $self->{'lexer_stack'}->[-1]->line; $msg = "$self->{'file_stack'}->[-1]:$line: "; } $msg .= "$obj"; die "$msg: $@"; } # print "<== _call_obj_method(): returning '" # . (defined($retval) ? $retval : 'undef') . "'\n"; return $retval; } ############################################################################### ### constructor ############################################################################### sub new { my ($class, $file, $objs, %opts) = @_; my ($self); $self = \%opts; bless($self, $class); $self->{'objs'} = $objs; $self->{'objs'} = {} if (!defined($self->{'objs'})); $self->{'include_dir'} = '.' if (!defined($self->{'include_dir'})); $self->{'file_stack'} = []; $self->{'cond_stack'} = []; $self->{'list_stack'} = []; $self->{'hash_stack'} = []; $self->{'in_expr'} = 0; $self->parse($file); return $self; } ############################################################################### ### config parser ############################################################################### sub parse { my ($self, $file) = @_; my ($fh, $lexer, $parser); # print "==> parse('$file')\n"; open($fh, $file) || die "open($file): $!\n"; push(@{$self->{'file_stack'}}, $file); $lexer = Parse::Lex->new( 'AND', '&&', 'COMMA', ',', 'COMMENT', '(?<!\\\\)#.*$', 'ELIF', '^\%[ \t]*elif', 'ELSE', '^\%[ \t]*else', 'ENDIF', '^\%[ \t]*endif', 'EOS', ';', 'PAREN_START', '\(', 'PAREN_END', '\)', 'HASH_ARROW', '=>', 'HASH_START', '{', 'HASH_END', '}', 'IF', '^\%[ \t]*if', 'INCLUDE', '^\%[ \t]*include', 'LIST_START', '\[', 'LIST_END', '\]', 'METHOD_ARROW', '->', 'NOT', '!', 'OR', '\|\|', 'WORD', '\w+', 'QSTRING', [ '(?<!\\\\)"', '([^"]|(?<=\\\\)")*', '(?<!\\\\)"' ], sub { my ($token, $string) = @_; $string =~ s/^"//; $string =~ s/"$//; $string =~ s/\\"/"/g; return $string; }, 'ERROR', '(?s:.*)', sub { my $line = $_[0]->lexer->line; my $file = $self->{'file_stack'}->[-1]; die "$file:$line: syntax error: \"$_[1]\"\n"; } ); $lexer->from(\*$fh); $lexer->configure('Skip' => '\s+'); push(@{$self->{'lexer_stack'}}, $lexer); $parser = Config::Objective::Parser->new(); $parser->YYData->{'lexer'} = $lexer; $parser->YYData->{'config'} = $self; $parser->YYParse(yylex => \&_lexer, # yydebug => 0x1F, yyerror => \&_error); pop(@{$self->{'file_stack'}}); pop(@{$self->{'lexer_stack'}}); close($fh); # print "<== parse('$file')\n"; return 1; } ############################################################################### ### allow direct access to object values ############################################################################### sub AUTOLOAD { my ($self) = @_; my ($method); $method = $AUTOLOAD; $method =~ s/.*:://; return if ($method eq 'DESTROY'); # return (overload::Overloaded($self->{'objs'}->{$method}) # ? $self->{'objs'}->{$method} # : $self->{'objs'}->{$method}->get()); return $self->{'objs'}->{$method}->get(); } ############################################################################### ### return a config object ############################################################################### sub get_obj { my ($self, $obj) = @_; return $self->{'objs'}->{$obj}; } ############################################################################### ### get a list of config object names ############################################################################### sub obj_names { my ($self) = @_; return keys %{$self->{'objs'}}; } ############################################################################### ### get a hash of object names and values ############################################################################### sub get_hash { my ($self) = @_; my ($href); $href = {}; map { $href->{$_} = $self->$_; } $self->obj_names(); return $href; } ############################################################################### ### cleanup and documentation ############################################################################### 1; __END__