in reply to Email "Reflector" design snags

First question: a long, long time ago you asked a similar question. I recommened (and still do) Config::General. However, Config::General only works on a file. I did give you a work around - subclass and override. Now, personally i would prefer for Config::General to accept an array ref, but by using copy-and-paste, you can rewrite its _open() method to accept an array instead:
use strict; use Data::Dumper; my (undef, @conf) = split ($/, " <template> url = http://www.foo.com/ # URL to fetch AvantGo = No # apply heuristics maxdepth = 2 # depth to traverse bpp = 4 # pits-per-pixel compression = zlib # or 'DOC' or 'None' title = My Document # title of output </template> " ); my $conf = MyConfig::General->new(\@conf); print Dumper {$conf->getall}; package MyConfig::General; use base 'Config::General'; sub _open { my ( $this, $arrayref ) = @_; my ( @content, $c_comment, $longline, $hier, $hierend, @hierdoc ); local $_; for (@$arrayref) { chomp; if ( !$hierend ) { s/(?<!\\)#.+$//; next if /^#/; next if /^\s*$/; s/\\#/#/g; } if (/^\s*(\S+?)(\s*=\s*|\s+)<<(.+?)$/) { $hier = $1; $hierend = $3; } elsif ( defined $hierend && /^(\s*)\Q$hierend\E$/ ) { my $indent = $1; $hier .= " " . chr(182); if ($indent) { foreach (@hierdoc) { s/^$indent//; $hier .= $_ . "\n"; } } else { $hier .= join "\n", @hierdoc; } push @{ $this->{content} }, $hier; @hierdoc = (); undef $hier; undef $hierend; } elsif (/^\s*\/\*/) { if (/\*\/\s*$/) { $c_comment = 0; } else { $c_comment = 1; } } elsif (/\*\//) { if ( !$c_comment ) { warn "invalid syntax: found end of C-comment without previ +ous start!\n"; } $c_comment = 0; } elsif (/\\$/) { chop; s/^\s*//; $longline .= $_ if ( !$c_comment ); } else { if ($longline) { s/^\s*//; $longline .= $_ if ( !$c_comment ); push @{ $this->{content} }, $longline; undef $longline; } elsif ($hier) { push @hierdoc, $_; } else { if ( !$c_comment ) { my $incl_file; if ( /^\s*<<include (.+?)>>\s*$/i || ( /^\s*include (.+?)\s*$/i && $this->{UseApacheInclude} ) ) { $incl_file = $1; if ( $this->{IncludeRelative} && $this->{configpath} && $incl_file !~ /^\// ) { $this->_open( $this->{configpath} . "/" . $incl_file ); } else { $this->_open($incl_file); } } else { push @{ $this->{content} }, $_; } } } } } return 1; }
For your second question, why not use a data structure to check for the existence of each conf variable. You could have a hash whose key is the config var in question, and maybe it points to another hash with keys such as required, maximum, or regex. If there is an error, put that error in an array or a hash, then, if there are elements in that key or hash, you know that the config is invalid. Here is a slightly tested but working example just to give you an idea:
my @errors; my %valid = ( url => { required => 1, regex => qr{^http://[\w./]}, }, maxdepth => { required => 1, regex => qr{^\d+$}, maximum => 10, }, ); my $conf = MyConfig::General->new(\@conf); my %conf = $conf->getall; my %tmpl = %{$conf{template}}; for (keys %valid) { my $v = $valid{$_} or next; if ($v->{required}) { unless ($tmpl{$_} !~ /^\s*$/) { push @errors, "$_ is missing" } } if ($v->{regex}) { unless ($tmpl{$_} =~ /$v->{regex}/) { push @errors, "$_ has invalid format: $tmpl{$_}" } } if ($v->{maximum}) { unless ($tmpl{$_} <= $v->{maximum}) { push @errors, "$_ is too large: $tmpl{$_}" } } } print Dumper \@errors;
This could definitely be abstracted further, but it should give you an idea to run with for now. Good luck!

jeffa

L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
B--B--B--B--B--B--B--B--
H---H---H---H---H---H---
(the triplet paradiddle with high-hat)