NAME
Sub::Spec::Runner::Orderly - Run a set of subs (with dependency
ordering, order changing, etc)
VERSION
version 0.05
SYNOPSIS
In YourModule.pm:
package YourModule;
use 5.010;
our %SPEC;
$SPEC{a} = { depends => {sub=>'b'}, ... };
sub a { my %args = @_; say "a"; [200, "OK"] }
$SPEC{b} = { depends => {sub=>'c'}, ... };
sub b { my %args = @_; say "b"; [200, "OK"] }
$SPEC{c} = { depends => {and=>[{sub=>'d'}, {sub=>'e'}]}, ... };
sub c { my %args = @_; say "c"; [200, "OK"] }
$SPEC{d} = { depends => {sub=>'e'}, ... };
sub d { my %args = @_; say "d"; [200, "OK"] }
$SPEC{e} = { ... };
sub e { my %args = @_; say "e"; [200, "OK"] }
In main module:
use Sub::Spec::Runner::Orderly;
my $runner = Sub::Spec::Runner::Orderly->new(load_modules=>0);
$runner->add('YourModule::a');
$runner->run;
Will output:
e
d
c
b
a
DESCRIPTION
This class can run a set of subroutines along with their dependencies
(read from sub spec's 'depends' clause) according to the dependency tree
order. During the run, subroutines can instruct the runner to
skip/repeat some subroutines, jump to another subroutine, or even add
more subroutines and reorder the execution. The runner will finish after
all subroutines have been run (or skipped).
This module uses Log::Any logging framework. Use something like
Log::Any::App, etc to see more logging statements for debugging.
This module uses Moo for object system.
args => HASHREF
Arguments to pass to each subroutine. Note that each argument will only
be passed if the 'args' clause in subroutine spec specifies that the sub
accepts that argument, or if subroutine doesn't have an 'args' clause.
Example:
package Foo;
our %SPEC;
$SPEC{sub0} = {};
sub sub0 { ... }
$SPEC{sub1} = {args=>{}};
sub sub1 { ... }
$SPEC{sub2} = {args=>{foo=>"str"}};
sub sub2 { ... }
$SPEC{sub3} = {args=>{bar=>"str"}};
sub sub2 { ... }
$SPEC{sub4} = {args=>{foo=>"str", bar=>"str"}};
sub sub4 { ... }
package main;
use Sub::Spec::Runner::Orderly;
my $runner = Sub::Spec::Runner::Orderly->new(sub_args => {foo=>1, foo=>2});
$runner->add("Foo::sub$_") for (1 2 3 4);
$runner->run;
Then only sub0 and sub4 will receive 'foo' and 'bar' args. sub1 won't
receive any arguments, sub2 will only receive 'foo', sub3 will only
receive 'bar'.
load_modules => BOOL
Whether to load (require()) modules when required, default is yes.
METHODS
add($subname[, $spec])
Add subroutine to the set of subroutines to be run. Example:
$runner->add('Foo::bar');
Sub spec will be retrieved from $Foo::SPEC{bar}, except when $spec is
specified.
Will also automatically add all dependencies.
order_by_dependencies
Reorder set of subroutines by dependencies. Normally need not be called
manually since it will be caled by run() prior to running subroutines,
but might be useful if you want to add more subroutines in the middle of
a run and then reorder.
todo_subs => ARRAYREF
Return the current list of subroutine names not yet runned, in order.
Previously run subroutines can belong to this list again if repeat()-ed.
done_subs => ARRAYREF
Return the current list of subroutine names already run, in order.
Never-run subroutines can belong to this list too if skip()-ed.
run(%opts) => [STATUSCODE, ERRMSG, RESULT]
Options: ignore_errors => BOOL (default false).
Run the set of subroutines previously added by add(). Will return status
code 400 if there are no subroutines to run.
Prior to running, 'depends' clause in each subroutine's spec will be
read and resolved first to add depended subroutines and change the order
of execution according to dependency tree. Will return 412 if dependency
cannot be resolved.
Then it will call pre_run(), which you can override. pre_run() must
return true, or run() will immediately return with 412 error.
Then it will run each subroutine successively and store its result. Each
subroutine will be called with arguments specified in 'args', with one
extra special argument, '-runner' which is the runner object. Prior to
running a subroutine, pre_sub() will be called. It must return true, or
run() will immediately return with 500 error.
The subroutine being run can see the status/result of other subroutines
by calling $runner->done(), $runner->result(). It can share data by
using $runner->stash(). It can also change the ordering or repeat/skip
some subroutines by calling $runner->done(), skip(), skip_all(),
repeat(), repeat_all(). It can jump to other subroutines using
$runner->jump(). See the respective method documentation for more
detail.
After running a subroutine, post_sub() will be called. It must return
true, or run() will immediately return with 500 error.
If ignore_errors is not set to true, then if the subroutine a
non-success result, run() will immediately exit with that result. The
meaning of subroutine's success can be changed by overriding
success_res() (by default, all 2xx and 3xx are considered success).
After all subroutines have been run (or skipped), run() will call
post_run() which must return true or otherwise run() will immediately
exit with 500 status.
After that, run() will return the summary. It will return status 200 if
there are at least one subroutine returning success, or 500 otherwise.
format_subname($subname) => STR
Can be used to format info log message: "Running XXX ..." when about to
run a subroutine inside run(). Default is "Running Package::bar ..."
(just the subname)
success_res($res) => BOOL
By default, all responses with 2xx and 3xx are assumed as a success. You
can override this.
pre_run() => BOOL
See run() for more details. Can be overridden by subclass.
pre_sub() => BOOL
See run() for more details. Can be overridden by subclass.
post_sub() => BOOL
See run() for more details. Can be overridden by subclass.
post_run() => BOOL
See run() for more details. Can be overridden by subclass.
done(SUBNAME[, VALUE]) => OLDVAL
If VALUE is set, set a subroutine to be done/not done. Otherwise will
return the current done status of SUBNAME.
SUBNAME can also be a regex, which means all subroutines matching the
regex. The last SUBNAME's current done status will be returned.
skip(SUBNAME)
Alias for done(SUBNAME, 1).
skip_all()
Alias for skip(qr/.*/, 1).
repeat(SUBNAME)
Alias for done(SUBNAME, 0).
repeat_all()
Alias for repeat(qr/.*/, 1).
branch_done(SUBNAME, VALUE)
Just like done(), except that will set SUBNAME *and all its dependants*.
Example: if a depends on b and b depends on c, then doing branch_done(c,
1) will also set a & b as done.
SUBNAME must be a string and not regex.
jump($subname)
Jump to another subname. Can be called in pre_sub() or inside subroutine
or post_sub().
stash(NAME[, VALUE]) => OLDVAL
Get/set stash data. This is a generic place to share data between
subroutines being run.
SEE ALSO
Sub::Spec
Sub::Spec::Clause::depends
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.