NAME
    Sub::Spec::Runner - Run subroutines

VERSION
    version 0.14

SYNOPSIS
    In YourModule.pm:

     package YourModule;

     use 5.010;
     our %SPEC;

     $SPEC{a} = { deps => {run_sub=>'b'}, ... };
     sub a { my %args = @_; say "a"; [200, "OK"] }

     $SPEC{b} = { deps => {run_sub=>'c'}, ... };
     sub b { my %args = @_; say "b"; [200, "OK"] }

     $SPEC{c} = { deps => {all=>[{run_sub=>'d'}, {run_sub=>'e'}]}, ... };
     sub c { my %args = @_; say "c"; [200, "OK"] }

     $SPEC{d} = { deps => {run_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;

     my $runner = Sub::Spec::Runner->new(load_modules=>0);
     $runner->add('YourModule::a');
     $runner->run;

    Will output:

     e
     d
     c
     b
     a

DESCRIPTION
    This class "runs" a set of subroutines. "Running" a subroutine basically
    means loading the module and calling the subroutine, plus a few other
    stuffs. See run() for more details.

    This module uses Log::Any for logging.

    This module uses Moo for object system.

ATTRIBUTES
  common_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;

     my $runner = Sub::Spec::Runner->new(common_args => {foo=>1, foo=>2});
     $runner->add("Foo::sub$_") for (1 2 3 4);
     $runner->run;

    Then only sub0 and sub4 will receive the 'foo' and 'bar' args. sub1
    won't receive any arguments, sub2 will only receive 'foo', sub3 will
    only receive 'bar'.

  load_modules => BOOL (default 1)
    Whether to load (require()) modules when required (in add()). You can
    turn this off if you are (or want to make) sure that all the subroutines
    to be run are already loaded.

  stop_on_sub_errors => BOOL (default 1)
    When run()-ning subroutines, whether a non-success return value from a
    subroutine stops the whole run. If turned off, run() will continue to
    the next subroutine. Note that you can override what constitutes a
    success return value by overriding success_res().

  order_before_run => BOOL (default 1)
    Before run() runs the subroutines, it will call order_by_dependencies()
    to reorder the added subroutines according to dependency tree (the
    'sub_run' dependency clause). You can turn off this behavior by setting
    this attribute to false.

  undo => BOOL (default undef)
    If set to 0 or 1, then these things will happen: 1) Prior to running,
    all added subroutines will be checked and must have 'undo' or 'reverse'
    feature, or are 'pure' (see Sub::Spec::Clause::features for more details
    on specifying features). 2) '-undo_action' and '-undo_data' special
    argument will be given with value 0/1 to each sub supporting undo (or
    '-reverse' 0/1 for subroutines supporting reverse). No special argument
    will be given for pure subroutines.

    Additionally, if 'undo' is set to 1, then order_by_dependencies() will
    reverse the order of run. This will only be done if 'order_before_run'
    attribute is set to true. Otherwise, you might have to do the reversing
    of order by yourself, if so desired.

    In summary: setting to 0 means run normally, but instruct subroutines to
    store undo information to enable undo in the future. Setting to 1 means
    undo. Setting to undef (the default) means disregard undo stuffs.

  undo_data_dir => STRING (default ~/.subspec/.undo)
    Location to put undo data. See save_undo_data() for more details.

  dry_run => BOOL (default 0)
    If set to 0 or 1, then these things will happen: 1) Prior to running,
    all added subroutines will be checked and must have 'dry_run' feature,
    or are 'pure' (see Sub::Spec::Clause::features for more details on
    specifying features). 2) '-dry_run' special argument will be given with
    value 0/1 to each sub supporting dry run. No special argument will be
    given for pure subroutines.

METHODS
  $runner->get_spec($subname) => SPEC
    Get spec for sub named $subname. Will be called by add(). Can be
    overriden to provide your own specs other than from %SPECS package
    variables.

  $runner->add($subname[, $args])
    Add subroutine to the set of subroutines to be run. Example:

     $runner->add('Package::subname');

    Will first get the sub spec by loading the module and read the %SPEC
    package var (this behavior can be changed by overriding get_spec()).
    Will not load modules if 'load_modules' attribute is false. Will die if
    cannot get spec.

    After that, it will check dependencies (the 'deps' spec clause) and die
    if some dependencies are unmet. All subroutine names mentioned in
    'run_sub' dependency clause will also be add()-ed automatically.

  $runner->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 if you turn off 'order_before_run' attribute, you'll need to call
    this method explicitly if you want ordering.

    Will return a true value on success, or false if dependencies cannot be
    resolved (e.g. there is circular dependency).

  $runner->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.

  $runner->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.

  $runner->run() => [STATUSCODE, ERRMSG, RESULT]
    Run (call) a set of subroutines previously added by add().

    First it will check 'undo' attribute. If defined, then all added
    subroutines are required to have undo/reverse/pure feature or otherwise
    run() will immediately return with error 412.

    Then it will check 'dry_run' attribute. If true, then all added
    subroutines are required to have dry_run/pure feature or otherwise run()
    will immediately return with error 412.

    After that it will call order_by_dependencies() to reorder the
    subroutines according to dependency order. This can be turned off via
    setting 'order_before_run' attribute to false.

    After that, 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 call each subroutine successively. Each subroutine will be
    called with arguments specified in 'common_args' attribute and args
    specified in add(), with one extra special argument, '-runner' which is
    the runner object.

    Before running a subroutine: pre_sub() will be called. It must return
    true, or run() will immediately return with 500 error. If 'undo'
    attribute is set to true, get_undo_data() will be called to get undo
    data (see get_undo_data() for more details on customizing storage of
    undo data).

    The subroutine being run can see the status/result of other subroutines
    by calling $runner->done($subname), $runner->result($subname). It can
    share data by using $runner->stash(). It can also repeat/skip some
    subroutines by calling $runner->skip(), skip_all(), repeat(),
    repeat_all(), branch_done(). It can jump to other subroutines using
    $runner->jump(). See the respective method documentation for more
    details.

    After running a subroutine: Runner will store the return value of each
    subroutine. Exception from subroutine will be trapped by eval() and upon
    exception return value of subroutine is assumed to be 500.

    If subroutine runs successfully: if 'undo' attribute is defined and
    false (meaning normal run but save undo information), save_undo_data()
    will be called to save undo information (see save_undo_data() for more
    details on customizing storage of undo data). If 'undo' attribute is
    true, remove_undo_data() will be called to remove undo information.

    After that, post_sub() will be called. It must return true, or run()
    will immediately return with 500 error.

    If 'stop_on_sub_errors' attribute is set to true (the default), then if
    the subroutine returns 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. Otherwise run() will immediately exit
    with 500 status.

    After that, run() will return the summary in RESULT (number of
    subroutines run, skipped, successful, etc). It will return status 200 if
    there are at least one subroutine returning success, or 500 otherwise.

  $runner->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)

  $runner->success_res($res) => BOOL
    By default, all responses with 2xx and 3xx are assumed as a success. You
    can override this.

  $runner->pre_run() => BOOL
    See run() for more details. Can be overridden by subclass.

  $runner->pre_sub() => BOOL
    See run() for more details. Can be overridden by subclass.

  $runner->get_undo_data($subname, $args) => $undo_data
    Get undo data. The default implementation loads from file (see
    save_undo_data() for more details). You can override this method or just
    set 'undo_data_dir' to the desired location.

  $runner->save_undo_data($subname, $args, $undo_data)
    Save undo data. The default implementation saves undo data in YAML file
    under directory specified by 'undo_data_dir', one file per subname, the
    file being named <subname>.yaml ("::" replaced with by "." because it is
    not valid in some filesystems). You can override this method or just set
    'undo_data_dir' to the desired location.

  $runner->remove_undo_data($subname, $args)
    Remove undo data.

  $runner->post_sub() => BOOL
    See run() for more details. Can be overridden by subclass.

  $runner->post_run() => BOOL
    See run() for more details. Can be overridden by subclass.

  $runner->result(SUBNAME) => RESULT
    Return the result of run subroutine named SUBNAME. If subroutine is not
    run yet, will return undef. Will die if subroutine is not in the list of
    added subroutines.

  $runner->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. Will die if subroutine named
    SUBNAME is not in the list of added subs.

    SUBNAME can also be a regex, which means all subroutines matching the
    regex. The last SUBNAME's current done status will be returned.

  $runner->skip(SUBNAME)
    Alias for done(SUBNAME, 1).

  $runner->skip_all()
    Alias for skip(qr/.*/, 1).

  $runner->repeat(SUBNAME)
    Alias for done(SUBNAME, 0).

  $runner->repeat_all()
    Alias for repeat(qr/.*/, 1).

  $runner->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.

  $runner->jump($subname)
    Jump to another subname. Can be called in pre_sub() or inside subroutine
    or post_sub().

  $runner->stash(NAME[, VALUE]) => OLDVAL
    Get/set stash data. This is a generic place to share data between
    subroutines being run.

FAQ
  What is the point of this module?
    Sub::Spec allows us to add various useful metadata to subroutines, like
    dependencies and specific features. Sub::Spec::Runner utilizes this
    information to make calling subroutines a bit more like running
    programs/installing software packages, e.g.:

    *   checking requirements prior to calling a subroutine;

        For example, you can specify that backup_db() requires the program
        "/usr/bin/mysqldump".

    *   reordering list of subroutines to run according to
        interdependencies;

        You can specify that a() and b() must be run before c(), d() must be
        run before c(), and so on. The runner will automatically resolve
        dependencies by loading required modules and reorder subroutine
        execution.

    *   running in dry-run mode;

        See 'dry_run' attribute for more details.

    *   running in undo mode;

        See 'undo' attribute for mor details.

    *   summary/statistics;

        Including the number of subroutines run, number of
        successes/failures, etc.

  What are some of the applications for this module?
    Sub::Spec::CmdLine uses this module in a straightforward way.
    Sub::Spec::CmdLine allows you to run subroutines from the command-line.

    Our Spanel project uses this module to run "setuplets", which are
    hosting server setup routines broken down to smaller bits. Each bit can
    be run individually (in dry-run and/or undo mode) but dependencies will
    always be respected (e.g. setuplets A requires running B, so running
    only A will always run B first).

SEE ALSO
    Sub::Spec

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.

