NAME
    Method::Workflow - Create classes that provide workflow method keywords.

DESCRIPTION
    In this module a workflow is a sequence of methods, possibly nested,
    associated with an object or class, that can be programmatically
    generated, chained or mixed.

    Generally you declare workflow methods as small parts of a greater
    design. A good example of what this module attemps to achieve is Ruby's
    RSPEC <http://rspec.info/>. However workflows need not be restricted to
    testing.

    Example workflow (Method::Workflow::Case):

    Each 'task' method will be run for each 'case' method

        my $target;
        case a { $target = "case 1" }
        case b { $target = "case 2" }
        case c { $target = "case 2" }

        action display { print "$target\n" }
        action display_cap { print uc($target) . "\n" }

        run_workflow();

    Prints:

        case 1
        CASE 1
        case 2
        CASE 2
        case 3
        CASE 3

SYNOPSYS
    There are 2 parts to this, the first is creating workflow element
    classes which export keywords to define workflow methods. The other part
    is using workflow element classes to construct workflows.

  WORKFLOW ELEMENTS
    SimpleWorkflowClass.pm:

        package SimpleWorkflowClass;
        use strict;
        use warnings;

        use Method::Workflow;
        use base 'Method::Workflow::Base';

        accessors qw/ my_accessor_a my_accessor_b /;

        keyword 'wflow';

        sub run {
            my $self = shift;
            my ( $root ) = @_;
            ...
            return $self->method->( $element, $self );
        }

    Explanation:

    use Method::Workflow
        This imports the 'keyword' keyword that is used later.

    use base 'Method::Workflow::Base'
        You must subclass Method::Workflow::Base or another class which
        inherits from it.

    accessors qw/ my_accessor_a my_accessor_b /
        Method::Workflow exports the 'accessors' function by default. This
        is a simple get/set accessor generator. Nothing forces you to use
        this in favor of say Moose, but it is available to keep your classes
        light-weight.

    keyword 'wflow'
        Here we declare a keyword to export that inserts a new object of
        this class into the workflow being generated when the 'wflow'
        keyword is used.

    sub run { ... }
        This is what is called to run the method contained in this workflow,
        you could hijack it to do other things as well / instead.

    $self->method->( $element, $self )
        The method is a method on the class for which the worflow was
        created, not for the instance of the element, thus the firs argumunt
        should be the root object/class.

  DECLARING WORKFLOWS
    Workflows run from the root (class/object) up. Nested items are run in
    order after the item in which they are defined. Each item runs, and runs
    it's children before returning and allowing it's siblings to run.

    Simple Example:

        wflow root {
            ...
            wflow nested {
                ...
            }
        }

   WORKFLOW ARGUMENTS
    Workflows are given 2 arguments. The first argument is the object/class
    that is the root element of the workflow. The second argument is the
    workflow instance being run. When a workflow is defined using a keyword,
    the first argumunt will automatically be shifted off as $self.

   CLASS LEVEL (NO MAGIC)
    ClassWithWorkflow.pm:

        package ClassWithWorkflow;
        use strict;
        use warnings;
        use WorkflowClass;

        start_class_workflow();

        wflow first {
            # $self is shifted for you for free. This is a class-level workflow so
            # $self will be the class name: 'ClassWithWorkflow'
            $self->do_thing;

            wflow nested {
                wflow deep { 'deep' }
                return 'nested';
            }
            return 'first';
        }

        wflow second {
            wflow nestedA { 'nestedA' }
            wflow nestedB { 'nestedB' }
            return 'second';
        }

        # Forgetting this can be dire, thats what the magic in the next section is
        # for.
        end_class_workflow();

        1;

    my_script.t:

        #!/usr/bin/perl
        use strict;
        use warnings;
        use Data::Dumper;
        use ClassWithWorkflow;

        my @results = ClassWithWorkflow->run_workflow();
        print Dumper \@results;

    Results:

        $ perl my_script.t
        $VAR1 = [
            'first',
            'nested',
            'deep',
            'second',
            'nestedA',
            'nestedB',
        ];

   CLASS LEVEL (MAGIC)
    Note The magic comes from Hook::AfterRuntime read the caveats section of
    its documentation. If you do not understand these limitations they may
    bite you. See the 'CLASS LEVEL (NO MAGIC)' section below if you need te
    work around any issues.

    ClassWithWorkflow.pm:

        package ClassWithWorkflow;
        use strict;
        use warnings;
        use WorkflowClass qw/ :classlevel /;

        wflow first {
            # $self is shifted for you for free. This is a class-level workflow so
            # $self will be the class name: 'ClassWithWorkflow'
            $self->do_thing;

            wflow nested {
                wflow deep { 'deep' }
                return 'nested';
            }
            return 'first';
        }

        wflow second {
            wflow nestedA { 'nestedA' }
            wflow nestedB { 'nestedB' }
            return 'second';
        }

        sub new {
            my $class = shift;
            bless( { @_ }, $class );
        }

        1;

    my_script.t:

        #!/usr/bin/perl
        use strict;
        use warnings;
        use Data::Dumper;
        use ClassWithWorkflow;

        my @results = ClassWithWorkflow->run_workflow();
        print Dumper \@results;

    Results:

        $ perl my_script.t
        $VAR1 = [
            'first',
            'nested',
            'deep',
            'second',
            'nestedA',
            'nestedB',
        ];

   IN AN OBJECT
    ObjectWithWorkflow.pm:

        package ObjectWithWorkflow;
        use strict;
        use warnings;

        # import the 'wflow' keyword which also works as a method!
        use WorkflowClass;

        sub new {
            my $class = shift;
            bless( { @_ }, $class );
        }

        sub insert_useless_workflow {
            my $self = shift;

            # Keyword form inserts it to the active element, $self->wflow(...)
            # would insert it into the root workflow for the object, which is not
            # what we want.
            wflow useless { "useless" }
        }

        1;

    my_script.t:

        #!/usr/bin/perl
        use strict;
        use warnings;
        use Data::Dumper;
        use ObjectWithWorkflow;

        my $obj = ObjectWithWorkflow->new;

        $obj->wflow( 'My Workflow', sub {
            # Not automatic in this form
            my $self = shift;

            $self->insert_useless_workflow();

            wflow {
                # $self is shifted for you for free because keyword was used. This
                # is an object workflow so $self will be the instance ($obj)
                $self->do_thing;

                'nested'
            }
            return 'my workflow';
        });

        print Dumper [ $obj->run_workflow() ];

    Results:

        $ perl my_script.t
        $VAR1 = [
            'my workflow',
            'useless',
            'nested',
        ];

   BOTH
    A package can have both a class level workflow and object level
    workflows, it just works.

   WORKFLOW 'ROLES'
    You can create the equivilent of a Moose role for workflows. Define a
    class that has a subroutine that defines the workflow components you
    want to re-use. Call the subroutine within the workflows that are to
    reuse it.

        {
            package WorkflowRole;
            use WorkflowClass;

            sub reusable {
                my $class = shift;
                my ($arg) = @_;
                wflow { print "I am reusable! Arg: $arg\n" }
            }
        }

        {
            package RoleConsumerA;
            use WorkflowClass;

            start_class_workflow;
            WorkflowRole->reusable( 'A' );
            end_class_workflow;
        }

        {
            package RoleConsumerB;
            use WorkflowClass;

            start_class_workflow;
            WorkflowRole->reusable( 'B' );
            end_class_workflow;
        }

        RoleConsumerA->run_workflow;
        RoleConsumerB->run_workflow;

    Results:

        I am reusable! Arg: A
        I am reusable! Arg: B

  TASKS
    Within workflows you may define tasks. Tasks are just like workflows
    except that tey are added to the root of the running workflow as opposed
    to the current element. Tasks run after the workflow has completed.

    To define tasks:

        wflow root {
            print "root\n";
            task a { print "a\n" }

            wflow nested {
                print "nested\n";
                task c { print "c\n }

                wflow deep {
                    print "deep\n";
                }
            }

            task b { print "b\n }

            wflow nested2 {
                print "nested2\n";
            }
        }

    Results (run order):

        root
        nested
        deep
        nested2
        a
        b
        c

   ORDERING TASKS
    You can sort or shuffle tasks. You can specify order as an import flag.
    The default is 'ordered' which will run them in the order they were
    defined.

    Shuffled:

        use WorkflowClass ':random';

    Sorted by name:

        use WorkflowClass ':sorted';

    Note: Some more advanced workflows may make use of an ordering param on
    a workflow:

        wflow name ( sorted => 1 ) { ... }

    All Mothod::Workflow::Base objects have the 'ordered', 'sorted', and
    'random' attributes for your workflow to query.

SEE ALSO
    Method::Workflow::Base
        The base class all workflows must inherit from

    Method::Workflow::Stack
        The stack class that tracks what class/object/element to which new
        workflow element instances should be added.

    Method::Workflow::Case
        Run tasks against multiple scenarios.

    Method::Workflow::SPEC
        An RSPEC based workflow.

NOTES
    Why is it called Method-Workflow
        Each workflow element is a method that runs on the object for which
        it is defined thus it is a workflow of methods.

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.

