I'd like to be able to write something like the following...
call_with_scope({
x => 47,
}, sub {
printf "$x\n";
printf "$y\n";
});
$y
no strict "vars"
call_with_scope(...)
call_with_scope
eval
no strict "vars"
local
call_with_scope
47
48
#!/usr/bin/env perl
use strict;
use warnings;
sub call_with_scope {
my ($env, $func) = @_;
my %property;
my @preamble;
foreach my $k (keys %$env) {
$property{$k} = $env->{$k};
# deliberately omitted: logic to ensure that ${$k} is a well-formed variable
push @preamble, "local \$$k = \$property{'$k'};";
}
# force scalar context
do {
my $str = join('', 'no strict "vars";', @preamble, '$_[1]->();');
return scalar(eval($str));
};
}
do {
no strict 'vars';
local $x;
my $y = 48;
call_with_scope(
{
x => 47,
},
sub {
printf "$x\n";
printf "$y\n";
}
);
};
I'm trying to write something kind of like Test::LectroTest ... except that instead of using a source filter and comments like in
Property { ##[ x <- Int, y <- Int ]## <body> }
... I want to write something likeProperty({x => gen_int, y => gen_int}, sub { <body> })
where$x
and$y
inside body get their values when an "instantiation" of a property test is performed.
You can do this by defining $x
and $y
as globals in the caller's package.
no strict 'refs';
my $caller = caller;
for my $var (keys %$properties) {
*{$caller.'::'.$var} = $properties->{$var};
}
$code->();
But this can't be easily localized. And polluting the caller's namespace with globals potentially leads to mysterious data leaking between tests. In general, use as little magic as possible in a test library; the user will have enough of their own weird magic to debug.
Instead, provide a function which returns the properties. For example, p
.
package LectroTest;
use Exporter qw(import);
our @EXPORT = qw(test p);
our $P;
sub test {
my($props, $test) = @_;
local $P = $props;
$test->();
}
sub p {
return $P;
}
And the test looks like:
use LectroTest;
test(
{ x => 42 }, sub { print p->{x} }
);