NAME
Sub::Spec::Clause::features - Specify subroutine features
VERSION
version 0.10
SYNOPSIS
In your spec:
features => {
FEATURE => VALUE,
...
}
DESCRIPTION
The 'features' clause allows subroutines to express their features. Current known features are:
reverse => 1
Specifies that subroutine supports reverse operation. To reverse, caller can add special argument '-reverse'. For example:
$SPEC{triple} = {args=>{num=>'num*'}, features=>{reverse=>1}};
sub triple {
my %args = @_;
my $num = $args{num};
[200, "OK", $args{-reverse} ? $num/3 : $num*3];
}
triple(num=>12); # => 36
triple(num=>12, -reverse=>1); # => 4
undo => 1
Specifies that subroutine supports undo operation. Undo is like 'reverse', but before doing undo you can save undo information first and then in the undo phase you use the previously saved undo information.
To undo, caller will add special argument '-undo' => 1, as well as '-state' => $state. $state is an state object which must support these operations: get([\%opts, ]$key), set([\%opts, ]$key => $val[, $key2 => $val, ...]), and delete([\%opts, ]$key[, $key2, ...]).
Example:
use Cwd qw(abs_path);
use File::Slurp;
$SPEC{lc_file} = {args=>{path=>'str*'}, features=>{undo=>1}};
sub triple {
my %args = @_;
my $path = $args{path};
my $undo = $args{-undo};
my $state = $args{-state};
$path = abs_path($path)
or return [500, "Can't get file absolute path"];
if ($undo) {
# we did not lc_file() the file previously
my $st = $state->load($path)
or return [412, "No undo information"]
write_file $path, $st->{content};
utime undef, $st->{mtime}, $path;
} else {
my @st = stat($path)
or return [500, "Can't stat file"];
my $content = read_file($path);
$state->save($path=>{mtime=>$st[9], content=>$content});
write_file $path, lc($content);
}
[200, "OK"];
}
So if you perform a series of operations, where each operation is a call to a subroutine which supports undo, all you need as an undo stack is just a list of subroutine names and arguments. To perform undo, you just call each subroutine in reverse order, and supplying -undo => 1 argument to each call:
To do stuffs:
f1(\%args1, -undo=>0, -state=$state);
f2(\%args2, -undo=>0, -state=$state);
f3(\%args3, -undo=>0, -state=$state);
...
To undo:
...
f3(\%args3, -undo=>1, -state=>$state);
f2(\%args2, -undo=>1, -state=>$state);
f1(\%args1, -undo=>1, -state=>$state);
dry_run => 1
Specifies that subroutine supports dry-run (simulation) mode. Example:
use Log::Any '$log';
$SPEC{rmre} = {
summary => 'Delete files in curdir matching a regex',
args => {re=>'str*'},
features => {dry_run=>1}
};
sub rmre {
my %args = @_;
my $re = qr/$args{re}/;
my $dry_run = $args{-dry_run};
opendir my($dir), ".";
while (my $f = readdir($dir)) {
next unless $f =~ $re;
$log->info("Deleting $f ...");
next if $dry_run;
unlink $f;
}
[200, "OK"];
}
pure => 1
Specifies that subroutine is "pure" and has no "side effects" (these are terms from functional programming / computer science). Having a side effect means changing something, somewhere (e.g. setting the value of a global variable, modifies its arguments, writing some data to disk, changing system date/time, etc.) Specifying a function as pure means, among others:
the function needs not be involved in undo operation;
you can safely include it during dry run;
TODO
Some features which might benefit from standardization: transaction/atomicity, i18n (is translatable, supported languages, locales).
SEE ALSO
AUTHOR
Steven Haryanto <stevenharyanto@gmail.com>
COPYRIGHT AND LICENSE
This software is copyright (c) 2011 by Steven Haryanto.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.