Skip to content

Commit

Permalink
Item14237: Added 'perl' and 'legacy' spec formats.
Browse files Browse the repository at this point in the history
'legacy' is for the Foswiki 2.x style .spec format. Currently parsed but
not completely supported. Compatibility is not guarantted though the
parser was trying to resemble the LoadSpec.pm code as much as possible.

'perl' is for perl code format where it'd be possible for spec writer to
write any Perl code which would then be inserted into a sub body and
called. Sub return value is supposed to be a list of spec items.

- Made Foswiki::Config::_getSubHash a public method.
  • Loading branch information
vrurg committed Dec 28, 2016
1 parent 94d21b3 commit 8c0d138
Show file tree
Hide file tree
Showing 11 changed files with 788 additions and 21 deletions.
16 changes: 13 additions & 3 deletions EmptyExtension/lib/Foswiki/Extension/EmptyExtension/Config.spec
@@ -1,3 +1,13 @@
# ---+ Extensions
# ---++ %$MODULE%
1;
#!perl
my @f = </etc/*>;

(
-section => "Extensions" => [
-section => "EmptyExtension" => [
'EmpyExt.FileList' => [
-default => \@f,
],
],
],
);
# vim: ft=perl
19 changes: 19 additions & 0 deletions UnitTestContrib/test/unit/ConfigTests.pm
Expand Up @@ -11,6 +11,11 @@ around set_up => sub {
my $orig = shift;
my $this = shift;

say STDERR "My attrs: ",
join( ", ", Foswiki::Class::getClassAttributes( ref($this) ) );

$this->{_this_keys_isnt_valid_attr} = 1;

$orig->( $this, @_ );

# You can now safely modify $Foswiki::cfg
Expand Down Expand Up @@ -439,4 +444,18 @@ sub test_specFilesAttribute {
return;
}

sub test_legacyParse {
my $this = shift;

my $cfg = $this->app->cfg;
my ($specFile) =
( grep { $_->path =~ /UnitTestContrib/ } @{ $cfg->specFiles->list } );

my $parser = $cfg->getSpecParser( $specFile->fmt );

my @specs = $parser->parse($specFile);

return;
}

1;
62 changes: 54 additions & 8 deletions core/lib/Foswiki/Config.pm
Expand Up @@ -19,7 +19,9 @@ use Unicode::Normalize;
use Cwd qw( abs_path );
use Try::Tiny;
use Foswiki qw(urlEncode urlDecode make_params);

use Foswiki::Configure::FileUtil;
use Foswiki::Exception::Config;

use Foswiki::Class qw(app extensible);
extends qw(Foswiki::Object);
Expand All @@ -31,7 +33,7 @@ use constant TRAUTO => 1;
# This should be the one place in Foswiki that knows the syntax of valid
# configuration item keys. Only simple scalar hash keys are supported.
#
my $ITEMREGEX = qr/(?:\{(?:'(?:\\.|[^'])+'|"(?:\\.|[^"])+"|[A-Za-z0-9_]+)\})+/;
our $ITEMREGEX = qr/(?:\{(?:'(?:\\.|[^'])+'|"(?:\\.|[^"])+"|[A-Za-z0-9_]+)\})+/;

# Generic booleans, used in some older LSC's
our $TRUE = 1;
Expand All @@ -55,6 +57,12 @@ my %remap = (

$Foswiki::regex{optionNameRegex} = qr/^-([[:alpha:]][[:alnum:]]*)$/;

# Hash of parser_format => Parser::Module format. If parser module doesn't load
# the corresponding key would then exists but be undefined.
# This info is ok to share across different application instances as a module
# would be loaded only once per address space.
my %parserModules;

=begin TML
---++ ObjectAttribute data
Expand Down Expand Up @@ -345,6 +353,8 @@ sub _createSpecParser {

my $fmtClass = "Foswiki::Config::Spec::Format::" . $format;

$parserModules{$format} = $fmtClass;

return $this->create( $fmtClass, cfg => $this, @_ );
}

Expand All @@ -368,6 +378,8 @@ sub getSpecParser {
# considered on Foswiki::App.
say STDERR "Cannot load parser for spec format '" . $format . "': "
. $e;

$parserModules{$format} = undef;
};

$parsers->{$format} = $parser;
Expand Down Expand Up @@ -1254,16 +1266,45 @@ sub normalizeKeyPath {
return $prefix . join( $joint, @keys ) . $suffix;
}

# Returns subhash of the config data where key is stored. The key name is
# returned as second element.
sub _getSubHash {
=begin TML
---++ ObjectMethod getSubHash($keyPath, %params) -> (\%subHash, $keyName)
Returns subhash of a config data where key defined by =$keyPath= is stored. The
key short name (the last element of key path) is returned as second element.
The =%params= hash keys are:
| *Name* | *Description* | *Default* |
| =data= | Data hash ref | =$app->cfg->data= |
| =autoVivify= | Automatically create non-existing subhashes. | _FALSE_ |
The method either returns an empty list if the key path doesn't refer to
a valid subhash. For example, for the following data structure:
<verbatim>
{
Key1 => {
Key2 => 'Value',
}
}
</verbatim>
_Key1.Key2_ would be a valid path but _Key1.Key2.Key3_ is incorrect.
Alternatively, if =autoVivify= is true the latter keypath would still be
incorrect while _Key1.NewKey.Key3_ would create a subhash for NewKey and return
it to the caller. The subhash will be empty meaning that _Key3_ doesn't exists.
=cut

sub getSubHash {
my $this = shift;
my $key = shift;
my %params = @_;

my @keys = $this->arg2keys($key);

my $subHash = $this->data;
my $subHash = $params{data} || $this->data;

while ( @keys > 1 ) {
my $key = shift @keys;
Expand Down Expand Up @@ -1291,7 +1332,7 @@ $app->cfg->get("{Root}{Branch}{Leaf}");
sub get {
my $this = shift;

my ( $subHash, $leafName ) = $this->_getSubHash( \@_ );
my ( $subHash, $leafName ) = $this->getSubHash( \@_ );

return $subHash->{$leafName};
}
Expand All @@ -1311,7 +1352,7 @@ sub set {
my ( $cfgPath, $value ) = @_;

my ( $subHash, $leafName ) =
$this->_getSubHash( $cfgPath, autoVivify => 1, );
$this->getSubHash( $cfgPath, autoVivify => 1, );

$subHash->{$leafName} = $value;
}
Expand Down Expand Up @@ -2272,7 +2313,12 @@ sub prepareSpecFiles {
}

sub _prepareSpecParsers {
return {};

# Keep track of previously failed modules.
return {
map { $_ => undef }
grep { !defined $parserModules{$_} } keys %parserModules
};
}

my @validSecOptions = qw(text);
Expand Down
17 changes: 17 additions & 0 deletions core/lib/Foswiki/Config/DataHash.pm
Expand Up @@ -152,6 +152,23 @@ has _trace => (
builder => '_prepareTrace',
);

around BUILDARGS => sub {
my $orig = shift;
my $class = shift;
my %params = @_;

my @profile;

# Simplify object creation by using app's cfg for required attribute cfg
# from Foswiki::Config::CfgObject. Won't work during app's construction
# stage.
if ( !$params{cfg} && $params{app} && $params{app}->has_cfg ) {
push @profile, cfg => $params{app}->cfg;
}

return $orig->( $class, @profile, @_ );
};

sub TIEHASH {
my $class = shift;
my %params = @_;
Expand Down
4 changes: 2 additions & 2 deletions core/lib/Foswiki/Config/Spec/CacheFile.pm
Expand Up @@ -29,9 +29,9 @@ sub store {

sub prepareEntries {
my $this = shift;

my $content = $this->content;

return [] unless $content;

return thaw( $this->content );
Expand Down
6 changes: 4 additions & 2 deletions core/lib/Foswiki/Config/Spec/File.pm
Expand Up @@ -102,6 +102,8 @@ sub refreshCache {

return if $this->validCache;

say STDERR "Invalided cache for ", $this->path if DEBUG;

my $cfg = $this->cfg;

my $parser = $cfg->getSpecParser( $this->fmt );
Expand All @@ -117,8 +119,8 @@ sub refreshCache {
section => $this->section,
specs => \@specs,
);
$this->cacheFile->store($dataObj->getLeafNodes);

$this->cacheFile->store( $dataObj->getLeafNodes );

return;
}
Expand Down
12 changes: 6 additions & 6 deletions core/lib/Foswiki/Config/Spec/Format/data.pm
Expand Up @@ -9,10 +9,10 @@ extends qw(Foswiki::Object);
with qw(Foswiki::Config::Spec::Parser Foswiki::Config::CfgObject);

sub parse {
my $this = shift;
my $this = shift;
my $specFile = shift;
my $specSrc = $specFile->content;

my $specSrc = $specFile->content;
my $specCode = <<SPECCODE;
(
#line 1 "spec data"
Expand All @@ -21,14 +21,14 @@ sub parse {
SPECCODE

my @specs = eval $specCode;

if ($@) {
Foswiki::Exception::Config::BadSpecSrc->throw(
file => $specFile->path,
text => $@,
);
}

return @specs;
}

Expand All @@ -50,4 +50,4 @@ This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
As per the GPL, removal of this notice is prohibited.
As per the GPL, removal of this notice is prohibited.

0 comments on commit 8c0d138

Please sign in to comment.