#!/usr/bin/perl
use warnings;
use strict;
####
package Constrained;
use Errno qw/EINVAL/;
####
sub _invalidate {
$! = shift;
die sprintf('Constraint violation: %s by %s::%s in %s line %s.',
$!,
map { qq($_) } (caller 1)[0,3,1,2] ),
"\n";
}
####
sub TIESCALAR {
my $class = shift;
if (defined $_[1]) {
$_[0]($_[1]) or _invalidate EINVAL;
}
bless { code => $_[0], val => $_[1]}, $class;
}
####
sub STORE {
my ($self, $val) = @_;
$self->{code}($val) or _invalidate EINVAL;
$self->{val} = $val;
}
####
sub FETCH {
defined $_[0]{val} or _invalidate EINVAL;
$_[0]->{val};
}
sub DESTROY {
# nothing to do
}
1;
####
package main;
my ($v, $c) = (qr/[aeiouy]/i, qr/[bcdfghjklmnpqrstvwxyz]/i);
{
tie my $foo, 'Constrained', sub {shift=~/^$c$v$v$c$c$/};
sub foo () :lvalue { $foo }
}
####
print q(Testing FETCH error for undefined value,), $/;
defined( eval { print 'foo is ', foo, $/ }) or print $@, $/;
print q(Testing STORE error for invalid value, 'quasi',), $/;
print defined( eval { foo = 'quasi'}) ?
('foo is ', foo, $/, $/) :
($@, $/);
print q(Testing STORE error valid value, 'quash',), $/;
print defined( eval { foo = 'quash'}) ?
('foo is ', foo, $/, $/) :
($@, $/);
print q(Testing modification, 'foo =~ s/h/e/',), $/;
print defined( eval { foo =~ s/h/e/}) ?
('foo is ', foo, ' - ERROR', $/, $/) :
($@, $/);
print q(Testing TIESCALAR error invalid value, 'suite',), $/;
print defined( eval {
tie my $bar, 'Constrained',
sub {shift=~/^$c$v$v$c$c$/}, 'suite';
sub bar () :lvalue { $bar }
}) ?
('bar is ', bar, $/, $/) :
($@, $/);
print q(Testing TIESCALAR error valid value, 'suits',), $/;
print defined( eval {
tie my $baz, 'Constrained',
sub {shift=~/^$c$v$v$c$c$/}, 'suits';
sub baz () :lvalue { $baz }
}) ?
('baz is ', baz, $/, $/) :
($@, $/);
####
__END__
Testing FETCH error for undefined value,
Constraint violation: Invalid argument by main::Constrained::FETCH in lvval.pl line 50.
Testing STORE error for invalid value, 'quasi',
Constraint violation: Invalid argument by main::Constrained::STORE in lvval.pl line 53.
Testing STORE error valid value, 'quash',
foo is quash
Testing modification, 'foo =~ s/h/e/',
Constraint violation: Invalid argument by main::Constrained::STORE in lvval.pl line 63.
Testing TIESCALAR error invalid value, 'suite',
Constraint violation: Invalid argument by main::Constrained::TIESCALAR in lvval.pl line 70.
Testing TIESCALAR error valid value, 'suits',
baz is suits