collect '//server/corion/images/'; output '/home/corion/imagestream/output'; size 160; size 640; exclude_tag 'private'; prefer '.svg' => '.jpg'; prefer '.svg' => '.png'; prefer '.cr2' => '.jpg'; #### sub collect($) { push @image_directories, @_; }; sub size($) { push @image_sizes, @_; }; sub prefer($$) { push @prefer_format, [@_]; }; ... #### my $spec = { collect => { name => 'collect', arg_count => 1, desc => 'A directory from which to collect images', }, prefer => { name => 'prefer', arg_count => 2, desc => 'Prefer this image format over files with the same name in other formats', }, # ... }; #### my %handler; for my $item (values %$spec) { my $n = $item->{name}; my $fetch; $result->{$n} = []; if (0 == $item->{arg_count}) { $fetch = sub() { push @{ $result->{$n}}, 1; }; } elsif (1 == $item->{arg_count}) { $fetch = sub($) { push @{ $result->{$n} }, @_; }; } elsif (2 == $item->{arg_count}) { $fetch = sub($$) { push @{ $result->{$n} }, [@_]; }; } else { $fetch = sub($;) { push @{ $result->{$n}}, [@_]; }; }; $handler{$n} = $fetch; }; # Install the subroutines for (keys %handler) { no strict 'refs'; *{$_} = $handler{ $_ }; }; # "execute" the config: my $cfg_str = join ";\n", "package " . __PACKAGE__, "#line $config_source#1", "$config_data", "1"; #warn $cfg_str; my $ok = eval $cfg_str; my $err = $@; if ($ok) { return $result } else { warn $err; return }; #### use Scope::Guard qw(guard); { my @guards; for (keys %handler) { # Save the old state: my($k,$p) = ($_,\&$_); push @guards, guard { # restore the old state *$k = $p; }; # Install our new handler *$_ = \&$handler{$_}; } # "execute" the config # ... } # Scope::Guard executes our guard blocks here #### local @{My::App}{keys %handler} = values %handler; # Can't modify glob elem in local at ... #### for my $n (keys %handler) { local *$n = $handler{ $n }; } # no handlers defined here anymore #### my ($ok,$err); my $cfg_str = join ";\n", "package " . __PACKAGE__, "#line $config_source#1", "$config_data", "1"; my @handlers = keys %handler; { no strict 'refs'; # We don't want to introduce another scope, as that will # negate the effect of the local: NEXT: my $n = shift @handlers; local *{$n} = $handler{ $n }; goto NEXT if @handlers; $ok = eval $cfg_str; $err = $@; } if ($ok) { return $result } else { warn $err; return }; #### ... { my $guard = local_install_subs \%handler; $ok = eval $cfg_str; $err = $@; } ...