"Sentinel" - create lightweight SCALARs with get/set callbacks
package Some::Class;
use Sentinel;
sub foo :lvalue
{
my $self = shift;
sentinel get => sub { return $self->get_foo },
set => sub { $self->set_foo( $_[0] ) };
}
sub bar :lvalue
{
my $self = shift;
sentinel value => $self->get_bar,
set => sub { $self->set_bar( $_[0] ) };
}
sub splot :lvalue
{
sentinel obj => shift, get => \&get_splot, set => \&set_splot;
}
sub wibble :lvalue
{
sentinel obj => shift, get => "get_wibble", set => "set_wibble";
}
This module provides a single lvalue function,
"sentinel", which yields a scalar that
invoke callbacks to get or set its value. Primarily this is useful to create
lvalue object accessors or other functions, to invoke actual code when a new
value is set, rather than simply updating a scalar variable.
$scalar = sentinel %args
Returns (as an lvalue) a scalar with magic attached to it. This
magic is used to get the value of the scalar, or to inform of a new value
being set, by invoking callback functions supplied to the sentinel. Takes
the following named arguments:
- get => CODE
- A "CODE" reference or
"obj" method name to invoke when the
value of the scalar is read, to obtain its value. The value returned from
this code will appear as the value of the scalar.
- set => CODE
- A "CODE" reference or
"obj" method name to invoke when a new
value for the scalar is written. The code will be passed the new value as
its only argument.
- value => SCALAR
- If no "get" callback is provided, this
value is given as the initial value of the scalar. If the scalar manages
to survive longer than a single assignment, its value on read will retain
the last value set to it.
- obj => SCALAR
- Optional value to pass as the first argument into the
"get" and
"set" callbacks. If this value is
provided, then the "get" and
"set" callbacks may be given as direct
sub references to object methods, or simply method names, rather than
closures that capture the referent object. This avoids the runtime
overhead of creating lots of small one-use closures around the
object.
A useful behaviour of this module is generation of mutation accessor methods
that automatically wrap
"get_"/"set_"
accessor/mutator pairs:
foreach (qw( name address age height )) {
my $name = $_;
no strict 'refs';
*$name = sub :lvalue {
sentinel obj => shift, get => "get_$name", set => "set_$name";
};
}
This is especially useful for methods whose values are simple
strings or numbers, because they allow Perl's rich set of mutation operators
to be applied to the object's values.
$obj->name =~ s/-/_/g;
substr( $obj->address, 100 ) = "";
$obj->age++;
$obj->height /= 100;
If an XS compiler is available at build time, this module is implemented using
XS. If not, it falls back on an implementation using a
"tie"d scalar. A pureperl installation can
also be requested at build time by passing the
"--pp" argument to Build.PL:
$ perl Build.PL --pp
$ ./Build
With thanks to "leont",
"Zefram", and others from
"irc.perl.org/#p5p" for assisting with
trickier bits of XS logic. Thanks to "mst"
for suggesting a pureperl implementation for XS-challenged systems.
Paul Evans <leonerd@leonerd.org.uk>