NAME
    Sub::Spec::Runner - Run subroutines

VERSION
    version 0.23

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
    NOTICE: This module and the Sub::Spec standard is deprecated as of Jan
    2012. Rinci is the new specification to replace Sub::Spec, it is about
    95% compatible with Sub::Spec, but corrects a few issues and is more
    generic. "Perinci::*" is the Perl implementation for Rinci and many of
    its modules can handle existing Sub::Spec sub specs.

    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.

    This module parses the 'run_sub' deps clause in Sub::Spec spec. It
    allows specifying dependency of a subroutine to another subroutine. It
    accept fully qualified subroutine name as value, or a 2-element array
    containing the subroutine name and its arguments. The depended
    subroutine will be add()-ed to the runner. Example:

     run_sub => 'main::func2'

     run_sub => ['My::Other::func', {arg1=>1, arg2=>2}]

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().

  allow_add_same_sub => BOOL (default 0)
    If this attribute is set to 0 (the default), then if you add() the same
    subroutine more than once, even with different argument, then the
    subsequent add will be ignored. For example:

     $runner->add('sub1', {arg1=>1}); # 'sub1' added
     $runner->add('sub1', {arg1=>2}); # not added

    If this attribute is set to 1, then you can add() the same subroutine
    more than once but with different arguments.

     $runner->add('sub1', {arg1=>1}); # 'sub1' added with args: {arg1=>1}
     $runner->add('sub1', {arg1=>2}); # 'sub1' added again with args: {arg1=>2}

  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.

    If this attribute is a true but negative value, then the order will be
    reversed.

  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 the 'features' clause in Sub::Spec 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.

    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 ~/.undo)
    Location to put undo data. See save_item_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 'features' clause in Sub::Spec 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.

  last_res => RESULT
    This attribute will be set by run() to be the result of each item after
    running each item.

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 queue of subroutines to be run. Example:

     $runner->add('Package::subname');
     $runner->add('Package::anothersub', {arg=>1});

    Will first load the module (unless load_modules attribute to false).
    Then attempt to get the sub spec by reading the %SPEC package var (this
    behavior can be changed by overriding get_spec()). 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,
    recursively.

    If the same subroutine is added twice, then it will only be queued once,
    unless allow_add_same_sub attribute is true, in which case duplicate
    subroutine name can be added as long as the arguments are different.

  $runner->empty()
    Empty all items in the queue. This is the opposite of what add() does.

  $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_items() => ARRAYREF
    Return the items in queue not yet run, in order. Previously run
    subroutines can belong to this list again if repeat()-ed.

  $runner->done_items() => ARRAYREF
    Return the items in queue already run. Never-run subroutines can belong
    to this list too if skip()-ed.

  $runner->run(%opts) => [STATUSCODE, ERRMSG, RESULT]
    Run (call) the queue 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 item in the queue 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_item_undo_data() will be called to get
    undo data (see get_item_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_item_undo_data() will be called to save undo information (see
    save_item_undo_data() for more details on customizing storage of undo
    data). If 'undo' attribute is true, remove_item_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.

    Options for run(), %opts:

    *   use_last_res => BOOL

        Default is false. If set to true, then instead of returning
        statistics/summary result, run() will return the last item's result.

  $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($subname, $args) => BOOL
    See run() for more details. Can be overridden by subclass.

  $runner->get_item_undo_data($subname, $args) => $undo_data
    Get item's undo data. If 'undo' attribute is set to true, this method
    will be called by run() before running each item.

  $runner->save_item_undo_data($subname, $args, $undo_data)
    Save item's undo data. If 'undo' attribute is defined, this method will
    be called by run() after each item is called, to save the resulting undo
    data.

    The default implementation saves undo data in YAML files 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). The data being stored is an array of hashes
    ("records"). Each record contains: {args => ..., undo_datas => [step,
    ...]}. Because the Sub::Spec specification allows different undo data
    for different arguments, the YAML file store undo data for each
    different argument in each record.

    To store undo data in other location, you can set the 'undo_data_dir' or
    alternatively override the *_undo_data() methods and provide your own
    implementation.

  $runner->get_sub_undo_data($subname) => $records
    Get all undo data records for a certain subname. See
    save_item_undo_data() for more details.

  $runner->remove_item_undo_data($subname, $args)
    Remove an item's undo data. If 'undo' attribute is set to true, will be
    called by run() after a successful undo.

  $runner->remove_sub_undo_data($subname, $args)
    Remove a sub's undo data.

  $runner->post_sub($subname, $args) => 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[, $args]) => RESULT
    Return the result of run subroutine named SUBNAME. If subroutine is not
    run yet, will return undef.

    If there are multiple items matching $subname, only the first item's
    result will be returned.

  $runner->is_done($subname[, $args]) => BOOL
    Check whether an item is already run/done.

  $runner->done(SUBNAME[, ARGS[, 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.

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

  $runner->skip_all()
    Skip all subroutines.

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

  $runner->repeat_all()
    Repeat all subroutines.

  $runner->done_branch(SUBNAME, ARGS, 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', undef, 1) will also set a & b as done.

  $runner->skip_branch(SUBNAME[, ARGS])
    Alias for done_branch(SUBNAME, ARGS, 1).

  $runner->repeat_branch(SUBNAME[, ARGS])
    Alias for done_branch(SUBNAME, ARGS, 0).

  $runner->jump($subname, $args)
    Jump to another item. 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) 2012 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.

