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 . Howover 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" } task display { print "$target\n" } run_workflow(); # Prints: # case 1 # case 2 # 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 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. $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. $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 ABJECT 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 { my $self = shift; $self->insert_useless_workflow(); wflow { '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. 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.