NAME
    Method::Workflow - An OO general purpose declarative workflow framework

DESCRIPTION
    This module provides an Object Oriented workflow framework. By default
    this framework uses keywords for a declarative interface. Most elements
    of the workflow are defined as methods, and will ultimately be run on a
    specified object.

    This Framowerk is intended to be used through higher level tools such as
    Fennec. As such the API leans twords providing more choices and
    capabilities. In most use cases an API wrapper that hides most of the
    descisions should be implemented.

SYNOPSYS
    This synopsys makes no attempt to convey a use case. This is simply an
    example of how to use the API. You define nestable workflows which
    should define scenarios and data. You also define tasks which make use
    of that data.

        use strict;
        use warnings;
        use Method::Workflow;

        my @color;

        my $wf = start_workflow;

        workflow rainbow {
            $self->do_thing; # $self is automatically given to you, it will be the
                             # $invocant object listed below

            workflow red {
                $self->do_thing_again; #$self is free again.

                task red { push @color => 'red' }
            }
            workflow yellow {
                ...
                task yellow { push @color => 'yellow' }
            }
            workflow green {
                ...
                task green { push @color => 'green' }
            }
            workflow blue {
                ...
                task blue { push @color => 'blue' }
            }
        }

        # Define on object that the methods will be run on.
        my $invocant = SomeClass->new() || undef;

        # What return data do we care about?
        my @want = qw/ results task_results /;

        # Do it.
        my ( $results, $task_results ) = $wf->run_workflow( $invocant, @want );

WORKFLOWS AND TASKS
    Workflows will run to depth in the order they are defined. Tasks are run
    *after* all workflows complete. This leads to the potential for powerful
    advanced workflows. It also leads to potential spooky action.

        workflow root {
            my $thing;

            workflow a {
                $thing = 'a';
                task { print "$thing\n" }
            }
            workflow b {
                $thing = 'b';
                task { print "$thing\n" }
            }
        }

    This example will print b twice, that is because all workflows run
    first, meaning the value of $thing is set to 'b' before the tasks run.
    This is not a bug, but rather a desired feature. See
    Method::Workflow::SPEC and Method::Workflow::Case for expamples.

UNDER THE HOOD
    Method::Workflow::Util exports several methods for manipulating 'the
    stack'. This is not the perl stack, but rather a stack of workflows. The
    stack is an array of workflows.

    When you use the declaritive keywords such as 'workflow NAME { ... }' a
    new workflow is created, this workflow is added as a child to the
    topmost workflow on the stack. When a workflow is run, it is pushed anto
    the stack as the topmost item, thus any methods declared within are
    added to the proper parent.

    When you call run_workflow() on a workflow it will run the workflows
    method, then recurse into nested workflows. Once workflows have been run
    to depth the tasks and errors are propogated down to the initial
    run_workflow() call. At this point errors are handled, and tasks are
    run.

API
    To create a workflow without magic:

        my $workflow = Methad::Workflow->new( name => $name, method => sub {
            # When magic is used $invocant is actually shifted off as $self.
            # $workflow is this workflow.
            my ( $invocant, $workflow ) = @_;
            ...
        });

    The invocant is reffered to as $self for the sake of higher level
    libraries which will hide the workflow details from their users.

  EXPORTS
    $root_wf = start_workflow()
        Starts a workflow and pushes it on to the stack. It is important to
        store the workflow as it will be shifted off of the stack when the
        reference count hits zero. It is also important to either remove all
        references, or call $root_wf->end when you are done defining the
        workflow.

    workflow NAME { ... }
        Define an element of a workflow

    task NAME { ... }
        Define a task for the current workflow

  ORDERING TASKS
    There are 3 sorting options, 'ordered' (default), 'sorted', and
    'random'. They can be specified when defining a workflow, or when
    calling start_workflow. here is an example from the tests:

        my @order;
        $wf = start_workflow( sorted => 1 );
        workflow root {
            task c { push @order => 'c' }
            task a { push @order => 'a' }
            task b { push @order => 'b' }
            task 'y' { push @order => 'y' }
            task z { push @order => 'z' }
            workflow x ( ordered => 1 ) {
                task f { push @order => 'f' }
                task e { push @order => 'e' }
                task d { push @order => 'd' }
            }
        }
        $wf->run_workflow();

        is_deeply(
            \@order,
            [ qw/ a b c f e d y z /],
            "Nested re-ordering"
        );

  ABSTRACT METHODS
    These are convenience methods that allow you to hook into the workflow
    process.

    $obj = $obj->init( %args )
        Called by the constructor giving you the opportunity to change or
        even replace the created object at construction time.

    @list = $class->required()
        Should return a list of attributes that are required at
        construction.

    @list = $obj->pre_run()
        Should return a list of coderefs that should be called after this
        workflows method is run, but before any nested workflows are run.

    @list = $obj->post_run()
        Should return a list of coderefs that should be called after nested
        workflows are run.

    $obj->run()
        Should run this workflows method, and store results and errors. Here
        is the default example.

            sub run {
                my $self = shift;
                my ( $invocant ) = @_;
                $self->push_results( $self->do( $self->method, $invocant ));
            }

  SIMPLE ACCESSORS
    $wf->parallel( $max )
    $max = $wf->parallel()
        Get/Set the max number of parallel processes to use when running
        tasks in parallel, 0 means do not run tasks in parallel.

    $wf->method( \&code )
    $code = $wf->method()
        Get/Set the method referenced by the workflow.

    $wf->name( $name )
    $name = $wf->name()
        Get/Set the workflow name.

    $wf->debug( $bool )
    $bool = $wf->debug()
        Turn debuging on/off.

    $ordering_type = $wf->has_ordering()
        Return the stringified name of the ordering method, undef if none is
        specified.

    $bool = $wf->ordered()
    $wf->ordered( $bool )
        True if tasks are to be run in the order in which they were defined.

    $bool = $wf->random()
    $wf->random( $bool )
        True if tasks are to be run in random order.

    $bool = $wf->sorted()
    $wf->sorted( $bool )
        True if tasks should be sorted

  ACTION METHODS
    $wf->observe()
        Mark the workflow as observed (happens when run)

    $bool = $wf->observed()
        Check if the workflow has been observed

    $wf->add_item( @items )
    $wf->add_items( @items )
        Add tasks/workflows to this one. (called when keywords are used)

    $wf->begin()
        Set this workflow as the current on the stack.

    $wf->end()
        Pop this workflow off the stack (errors if this workflow is not the
        top)

    @return = $wf->do( $code, $invocant )
        Run $code as a method on $invocant. The return is what $code
        returns.

    @results = $wf->run_workflow( $invocant, @want )
        Run the workflow. @results is an array of arrays, each inner array
        is the list of returns for an element of @want. Possible @want
        values are: results, errors, tasks, task_results, task_errors,
        task_tasks.

  MANIPULATING RESULTS
    These methods manipulate the results array which stores the return value
    from the method with which the workflow was created. The run() method is
    responsible for populating this.

    @items = $wf->results()
        Get the results

    @items = $wf->pull_results()
        Get the results while also deleting them from the workflow.

    $items = $wf->results_ref()
        Get/Set the arrayref storing the results.

    $wf->push_results( @items )
        Add results

  MANIPULATING TASKS
    @items = $wf->tasks()
        Get the tasks

    @items = $wf->pull_tasks()
        Get the tasks while also deleting them from the workflow.

    $items = $wf->tasks_ref()
        Get/Set the arrayref storing the tasks.

    $wf->push_tasks( @items )
        Add tasks

  MANIPULATING ERRORS
    @items = $wf->errors()
        Get the errors

    @items = $wf->pull_errors()
        Get the errors while also deleting them from the workflow.

    $items = $wf->errors_ref()
        Get/Set the arrayref storing the errors

    $wf->push_errors( @items )
        Add errors

  MANIPULATING NESTED WORKFLOWS
    @items = $wf->children()
        Get a list of all nested workflows (not to depth)

    @items = $wf->pull_all_children()
        Get all the nested workflows while also deleting them from the
        workflow.

    @items = $wf->pull_children( $type )
        Get all the nested workflows of a specific type while also deleting
        them from the workflow.

    $items = $wf->children_ref()
        Get/Set the hashref storing the nested workflows.

    $wf->push_children( @items )
        Add a nested workflow

    my @types = $wf->keys_children()
        Get a list of all the types of nested workflows (What they are
        blessed as)

  CUSTOM ERROR HANDLERS
    $wf->error_handler( \&custom_handler )
    $handler = $wf->error_handler()
        Get/Set the error handler.

    The error handler should be a coderef. All errors will be passed in as
    aguments. Each error is an array, the first element is a workflow stack
    trace, the second is the error message itself. The stack trace is an
    array of workflow objects.

    Here is the default handler as an example:

        sub default_error_handler {
            for my $set ( @_ ) {
                my ( $trace, $msg ) = @$set;
                warn join(
                    "\n  ",
                    $msg,
                    'Workflow Stack:',
                    map { blessed($_) . '(' . $_->name . ')' } @$trace
                ) . "\n";
            }
            die "There were errors (see above)";
        }

EXTENDING
    To extend Method::Workflow you should subclass Method::Workflow,
    possibly subclass Method::Workflow::Task, and familiarize yourself with
    Method::Workflow::Stack. Also read the section 'UNDER THE HOOD'.

FENNEC PROJECT
    This module is part of the Fennec project. See Fennec for more details.
    Fennec is a project to develop an extendable and powerful testing
    framework. Together the tools that make up the Fennec framework provide
    a potent testing environment.

    The tools provided by Fennec are also useful on their own. Sometimes a
    tool created for Fennec is useful outside the greator framework. Such
    tools are turned into their own projects. This is one such project.

    Fennec - The core framework
      The primary Fennec project that ties them all together.

AUTHORS
    Chad Granum exodist7@gmail.com

COPYRIGHT
    Copyright (C) 2010 Chad Granum

    Method-Workflow is free software; Standard perl licence.

    Method-Workflow is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license for
    more details.

