1Workflow(3)           User Contributed Perl Documentation          Workflow(3)
2
3
4

NAME

6       Workflow - Simple, flexible system to implement workflows
7

VERSION

9       This documentation describes version 0.15 of Workflow
10

SYNOPSIS

12        use Workflow::Factory qw( FACTORY );
13
14        # Defines a workflow of type 'myworkflow'
15        my $workflow_conf  = 'workflow.xml';
16
17        # contents of 'workflow.xml'
18
19        <workflow>
20            <type>myworkflow</type>
21            <time_zone>local</time_zone>
22            <description>This is my workflow.</description>
23
24            <state name="INITIAL">
25                <action name="upload file" resulting_state="uploaded" />
26            </state>
27            <state name="uploaded" autorun="yes">
28                <action name="verify file" resulting_state="verified file">
29                     <!-- everyone other than 'CWINTERS' must verify -->
30                     <condition test="$context->{user} ne 'CWINTERS'" />
31                </action>
32                <action name="null" resulting_state="annotated">
33                     <condition test="$context->{user} eq 'CWINTERS'" />
34                </action>
35            </state>
36            <state name="verified file">
37                <action name="annotate">
38                    <condition name="can_annotate" />
39                </action>
40                <action name="null">
41                    <condition name="!can_annotate" />
42                </action>
43            </state>
44            <state name="annotated" autorun="yes" may_stop="yes">
45                <action name="null" resulting_state="finished">
46                   <condition name="completed" />
47                </action>
48            </state>
49            <state name="finished" />
50        </workflow>
51
52        # Defines actions available to the workflow
53        my $action_conf    = 'action.xml';
54
55        # contents of 'action.xml'
56
57        <actions>
58            <action name="upload file" class="MyApp::Action::Upload">
59                <field name="path" label="File Path"
60                       description="Path to file" is_required="yes" />
61            </action>
62
63            <action name="verify file" class="MyApp::Action::Verify">
64                <validator name="filesize_cap">
65                    <arg>$file_size</arg>
66                </validator>
67            </action>
68
69            <action name="annotate"    class="MyApp::Action::Annotate" />
70
71            <action name="null"        class="Workflow::Action::Null" />
72        </actions>
73
74        # Defines conditions available to the workflow
75        my $condition_conf = 'condition.xml';
76
77        # contents of 'condition.xml'
78
79        <conditions>
80            <condition name="can_annotate"
81                       class="MyApp::Condition::CanAnnotate" />
82        </conditions>
83
84        # Defines validators available to the actions
85        my $validator_conf = 'validator.xml';
86
87        # contents of 'validator.xml'
88
89        <validators>
90            <validator name="filesize_cap" class="MyApp::Validator::FileSizeCap">
91                <param name="max_size" value="20M" />
92            </validator>
93        </validators>
94
95        # Stock the factory with the configurations; we can add more later if
96        # we want
97        $self->_factory()->add_config_from_file(
98            workflow   => $workflow_conf,
99            action     => $action_conf,
100            condition  => $condition_conf,
101            validator  => $validator_conf
102        );
103
104        # Instantiate a new workflow...
105        my $workflow = $self->_factory()->create_workflow( 'myworkflow' );
106        print "Workflow ", $workflow->id, " ",
107              "currently at state ", $workflow->state, "\n";
108
109        # Display available actions...
110        print "Available actions: ", $workflow->get_current_actions, "\n";
111
112        # Get the data needed for action 'upload file' (assumed to be
113        # available in the current state) and display the fieldname and
114        # description
115
116        print "Action 'upload file' requires the following fields:\n";
117        foreach my $field ( $workflow->get_action_fields( 'FOO' ) ) {
118            print $field->name, ": ", $field->description,
119                  "(Required? ", $field->is_required, ")\n";
120        }
121
122        # Add data to the workflow context for the validators, conditions and
123        # actions to work with
124
125        my $context = $workflow->context;
126        $context->param( current_user => $user );
127        $context->param( sections => \@sections );
128        $context->param( path => $path_to_file );
129
130        # Execute one of them
131        $workflow->execute_action( 'upload file' );
132
133        print "New state: ", $workflow->state, "\n";
134
135        # Later.... fetch an existing workflow
136        my $id = get_workflow_id_from_user( ... );
137        my $workflow = $self->_factory()->fetch_workflow( 'myworkflow', $id );
138        print "Current state: ", $workflow->state, "\n";
139

QUICK START

141       The eg/ticket/ directory contains a configured workflow system.  You
142       can access the same data and logic in two ways:
143
144       ·   a command-line application (ticket.pl)
145
146       ·   a CGI script               (ticket.cgi)
147
148       ·   a web application          (ticket_web.pl)
149
150       To initialize:
151
152               perl ticket.pl --db
153
154       To run the command-line application:
155
156               perl ticket.pl
157
158       To access the database and data from CGI, add the relevant
159       configuration for your web server and call ticket.cgi:
160
161               http://www.mysite.com/workflow/ticket.cgi
162
163       To start up the standalone web server:
164
165               perl ticket_web.pl
166
167       (Barring changes to HTTP::Daemon and forking the standalone server
168       won't work on Win32; use CGI instead, although patches are always
169       welcome.)
170
171       For more info, see eg/ticket/README
172

DESCRIPTION

174   Overview
175       This is a standalone workflow system. It is designed to fit into your
176       system rather than force your system to fit to it. You can save
177       workflow information to a database or the filesystem (or a custom
178       storage). The different components of a workflow system can be included
179       separately as libraries to allow for maximum reusibility.
180
181   User Point of View
182       As a user you only see two components, plus a third which is really
183       embedded into another:
184
185       ·   Workflow::Factory - The factory is your interface for creating new
186           workflows and fetching existing ones. You also feed all the
187           necessary configuration files and/or data structures to the factory
188           to initialize it.
189
190       ·   Workflow - When you get the workflow object from the workflow
191           factory you can only use it in a few ways -- asking for the current
192           state, actions available for the state, data required for a
193           particular action, and most importantly, executing a particular
194           action. Executing an action is how you change from one state to
195           another.
196
197       ·   Workflow::Context - This is a blackboard for data from your
198           application to the workflow system and back again. Each
199           instantiation of a Workflow has its own context, and actions
200           executed by the workflow can read data from and deposit data into
201           the context.
202
203   Developer Point of View
204       The workflow system has four basic components:
205
206       ·   workflow - The workflow is a collection of states; you define the
207           states, how to move from one state to another, and under what
208           conditions you can change states.
209
210           This is represented by the Workflow object. You normally do not
211           need to subclass this object for customization.
212
213       ·   action - The action is defined by you or in a separate library. The
214           action is triggered by moving from one state to another and has
215           access to the workflow and more importantly its context.
216
217           The base class for actions is the Workflow::Action class.
218
219       ·   condition - Within the workflow you can attach one or more
220           conditions to an action. These ensure that actions only get
221           executed when certain conditions are met. Conditions are completely
222           arbitrary: typically they will ensure the user has particular
223           access rights, but you can also specify that an action can only be
224           executed at certain times of the day, or from certain IP addresses,
225           and so forth. Each condition is created once at startup then passed
226           a context to check every time an action is checked to see if it can
227           be executed.
228
229           The base class for conditions is the Workflow::Condition class.
230
231       ·   validator - An action can specify one or more validators to ensure
232           that the data available to the action is correct. The data to check
233           can be as simple or complicated as you like. Each validator is
234           created once then passed a context and data to check every time an
235           action is executed.
236
237           The base class for validators is the Workflow::Validator class.
238

WORKFLOW BASICS

240   Just a Bunch of States
241       A workflow is just a bunch of states with rules on how to move between
242       them. These are known as transitions and are triggered by some sort of
243       event. A state is just a description of object properties. You can
244       describe a surprisingly large number of processes as a series of states
245       and actions to move between them. The application shipped with this
246       distribution uses a fairly common application to illustrate: the
247       trouble ticket.
248
249       When you create a workflow you have one action available to you: create
250       a new ticket ('create issue'). The workflow has a state 'INITIAL' when
251       it is first created, but this is just a bootstrapping exercise since
252       the workflow must always be in some state.
253
254       The workflow action 'create issue' has a property 'resulting_state',
255       which just means: if you execute me properly the workflow will be in
256       the new state 'CREATED'.
257
258       All this talk of 'states' and 'transitions' can be confusing, but just
259       match them to what happens in real life -- you move from one action to
260       another and at each step ask: what happens next?
261
262       You create a trouble ticket: what happens next? Anyone can add comments
263       to it and attach files to it while administrators can edit it and
264       developers can start working on it. Adding comments does not really
265       change what the ticket is, it just adds information. Attachments are
266       the same, as is the admin editing the ticket.
267
268       But when someone starts work on the ticket, that is a different matter.
269       When someone starts work they change the answer to: what happens next?
270       Whenever the answer to that question changes, that means the workflow
271       has changed state.
272
273   Discover Information from the Workflow
274       In addition to declaring what the resulting state will be from an
275       action the action also has a number of 'field' properties that describe
276       that data it required to properly execute it.
277
278       This is an example of discoverability. This workflow system is setup so
279       you can ask it what you can do next as well as what is required to move
280       on. So to use our ticket example we can do this, creating the workflow
281       and asking it what actions we can execute right now:
282
283        my $wf = Workflow::$self->_factory()->create_workflow( 'Ticket' );
284        my @actions = $wf->get_current_actions;
285
286       We can also interrogate the workflow about what fields are necessary to
287       execute a particular action:
288
289        print "To execute the action 'create issue' you must provide:\n\n";
290        my @fields = $wf->get_action_fields( 'create issue' );
291        foreach my $field ( @fields ) {
292            print $field->name, " (Required? ", $field->is_required, ")\n",
293                  $field->description, "\n\n";
294        }
295
296   Provide Information to the Workflow
297       To allow the workflow to run into multiple environments we must have a
298       common way to move data between your application, the workflow and the
299       code that moves it from one state to another.
300
301       Whenever the Workflow::Factory creates a new workflow it associates the
302       workflow with a Workflow::Context object. The context is what moves the
303       data from your application to the workflow and the workflow actions.
304
305       For instance, the workflow has no idea what the 'current user' is. Not
306       only is it unaware from an application standpoint but it does not
307       presume to know where to get this information. So you need to tell it,
308       and you do so through the context.
309
310       The fact that the workflow system proscribes very little means it can
311       be used in lots of different applications and interfaces. If a system
312       is too closely tied to an interface (like the web) then you have to
313       create some potentially ugly hacks to create a more convenient avenue
314       for input to your system (such as an e-mail approving a document).
315
316       The Workflow::Context object is extremely simple to use -- you ask a
317       workflow for its context and just get/set parameters on it:
318
319        # Get the username from the Apache object
320        my $username = $r->connection->user;
321
322        # ...set it in the context
323        $wf->context->param( user => $username );
324
325        # somewhere else you'll need the username:
326
327        $news_object->{created_by} = $wf->context->param( 'user' );
328
329   Controlling What Gets Executed
330       A typical process for executing an action is:
331
332       ·   Get data from the user
333
334       ·   Fetch a workflow
335
336       ·   Set the data from the user to the workflow context
337
338       ·   Execute an action on the context
339
340       When you execute the action a number of checks occur. The action needs
341       to ensure:
342
343       ·   The data presented to it are valid -- date formats, etc. This is
344           done with a validator, more at Workflow::Validator
345
346       ·   The environment meets certain conditions -- user is an
347           administrator, etc. This is done with a condition, more at
348           Workflow::Condition
349
350       Once the action passes these checks and successfully executes we update
351       the permanent workflow storage with the new state, as long as the
352       application has declared it.
353

WORKFLOWS ARE OBSERVABLE

355   Purpose
356       It's useful to have your workflow generate events so that other parts
357       of a system can see what's going on and react. For instance, say you
358       have a new user creation process. You want to email the records of all
359       users who have a first name of 'Sinead' because you're looking for your
360       long-lost sister named 'Sinead'. You'd create an observer class like:
361
362        package FindSinead;
363
364        sub update {
365            my ( $class, $wf, $event, $new_state ) = @_;
366            return unless ( $event eq 'state change' );
367            return unless ( $new_state eq 'CREATED' );
368            my $context = $wf->context;
369            return unless ( $context->param( 'first_name' ) eq 'Sinead' );
370
371            my $user = $context->param( 'user' );
372            my $username = $user->username;
373            my $email    = $user->email;
374            my $mailer = get_mailer( ... );
375            $mailer->send( 'foo@bar.com','Found her!',
376                           "We found Sinead under '$username' at '$email' );
377        }
378
379       And then associate it with your workflow:
380
381        <workflow>
382            <type>SomeFlow</type>
383            <observer class="FindSinead" />
384            ...
385
386       Every time you create/fetch a workflow the associated observers are
387       attached to it.
388
389   Events Generated
390       You can attach listeners to workflows and catch events at a few points
391       in the workflow lifecycle; these are the events fired:
392
393       ·   create - Issued after a workflow is first created.
394
395           No additional parameters.
396
397       ·   fetch - Issued after a workflow is fetched from the persister.
398
399           No additional parameters.
400
401       ·   save - Issued after a workflow is successfully saved.
402
403           No additional parameters.
404
405       ·   execute - Issued after a workflow is successfully executed and
406           saved.
407
408           Adds the parameters $old_state, $action_name and $autorun.
409           $old_state includes the state of the workflow before the action was
410           executed, $action_name is the action name that was executed and
411           $autorun is set to 1 if the action just executed was started using
412           autorun.
413
414       ·   state change - Issued after a workflow is successfully executed,
415           saved and results in a state change. The event will not be fired if
416           you executed an action that did not result in a state change.
417
418           Adds the parameters $old_state, $action and $autorun.  $old_state
419           includes the state of the workflow before the action was executed,
420           $action is the action name that was executed and $autorun is set to
421           1 if the action just executed was autorun.
422
423       ·   add history - Issued after one or more history objects added to a
424           workflow object.
425
426           The additional argument is an arrayref of all Workflow::History
427           objects added to the workflow. (Note that these will not be
428           persisted until the workflow is persisted.)
429
430   Configuring
431       You configure the observers directly in the 'workflow' configuration
432       item. Each 'observer' may have either a 'class' or 'sub' entry within
433       it that defines the observer's location.
434
435       We load these classes at startup time. So if you specify an observer
436       that doesn't exist you see the error when the workflow system is
437       initialized rather than the system tries to use the observer.
438
439       For instance, the following defines two observers:
440
441        <workflow>
442          <type>ObservedItem</type>
443          <description>This is...</description>
444
445          <observer class="SomeObserver" />
446          <observer sub="SomeOtherObserver::Functions::other_sub" />
447
448       In the first declaration we specify the class ('SomeObserver') that
449       will catch observations using its "update()" method. In the second
450       we're naming exactly the subroutine ('other_sub()' in the class
451       'SomeOtherObserver::Functions') that will catch observations.
452
453       All configured observers get all events. It's up to each observer to
454       figure out what it wants to handle.
455

WORKFLOW METHODS

457       The following documentation is for the workflow object itself rather
458       than the entire system.
459
460   Object Methods
461       execute_action( $action_name, $autorun )
462
463       Execute the action $action_name. Typically this changes the state of
464       the workflow. If $action_name is not in the current state, fails one of
465       the conditions on the action, or fails one of the validators on the
466       action an exception is thrown. $autorun is used internally and is set
467       to 1 if the action was executed using autorun.
468
469       After the action has been successfully executed and the workflow saved
470       we issue a 'execute' observation with the old state, action name and an
471       autorun flag as additional parameters.  So if you wanted to write an
472       observer you could create a method with the signature:
473
474        sub update {
475            my ( $class, $workflow, $action, $old_state, $action_name, $autorun )
476               = @_;
477            if ( $action eq 'execute' ) { .... }
478        }
479
480       We also issue a 'change state' observation if the executed action
481       resulted in a new state. See "WORKFLOWS ARE OBSERVABLE" above for how
482       we use and register observers and Class::Observable for more general
483       information about observers as well as implementation details.
484
485       Returns: new state of workflow
486
487       get_current_actions( $group )
488
489       Returns a list of action names available from the current state for the
490       given environment. So if you keep your "context()" the same if you call
491       "execute_action()" with one of the action names you should not trigger
492       any condition error since the action has already been screened for
493       conditions.  If you want to divide actions in groups (for example state
494       change group, approval group, which have to be shown at different
495       places on the page) add group property to your action
496
497       <action name="terminate request"  group="state change"
498       class="MyApp::Action::Terminate" /> <action name="approve request"
499       group="approval"  class="MyApp::Action::Approve" />
500
501       my @actions = $wf->get_current_actions("approval");
502
503       $group should be string that reperesents desired group name. In
504       @actions you will get list of action names available from the current
505       state for the given environment limited by group.  $group is optional
506       parameter.
507
508       Returns: list of strings representing available actions
509
510       get_action_fields( $action_name )
511
512       Return a list of Workflow::Action::InputField objects for the given
513       $action_name. If $action_name not in the current state or not
514       accessible by the environment an exception is thrown.
515
516       Returns: list of Workflow::Action::InputField objects
517
518       add_history( @( \%params | $wf_history_object ) )
519
520       Adds any number of histories to the workflow, typically done by an
521       action in "execute_action()" or one of the observers of that action.
522       This history will not be saved until "execute_action()" is complete.
523
524       You can add a list of either hashrefs with history information in them
525       or full Workflow::History objects. Trying to add anything else will
526       result in an exception and none of the items being added.
527
528       Successfully adding the history objects results in a 'add history'
529       observation being thrown. See "WORKFLOWS ARE OBSERVABLE" above for
530       more.
531
532       Returns: nothing
533
534       get_history()
535
536       Returns list of history objects for this workflow. Note that some may
537       be unsaved if you call this during the "execute_action()" process.
538
539       get_unsaved_history()
540
541       Returns list of all unsaved history objects for this workflow.
542
543       clear_history()
544
545       Clears all transient history objects from the workflow object, not from
546       the long-term storage.
547
548       set( $property, $value )
549
550       Method used to overwrite Class::Accessor so only certain callers can
551       set properties caller has to be a Workflow namespace package.
552
553       Sets property to value or throws Workflow::Exception
554
555   Properties
556       Unless otherwise noted, properties are read-only.
557
558       Configuration Properties
559
560       Some properties are set in the configuration file for each workflow.
561       These remain static once the workflow is instantiated.
562
563       type
564
565       Type of workflow this is. You may have many individual workflows
566       associated with a type or you may have many different types running in
567       a single workflow engine.
568
569       description
570
571       Description (usually brief, hopefully with a URL...)  of this workflow.
572
573       time_zone
574
575       Workflow uses the DateTime module to create all date objects. The
576       time_zone parameter allows you to pass a time zone value directly to
577       the DateTime new method for all cases where Workflow needs to create a
578       date object.  See the DateTime module for acceptable values.
579
580       Dynamic Properties
581
582       You can get the following properties from any workflow object.
583
584       id
585
586       ID of this workflow. This will always be defined, since when the
587       Workflow::Factory creates a new workflow it first saves it to long-term
588       storage.
589
590       state
591
592       The current state of the workflow.
593
594       last_update (read-write)
595
596       Date of the workflow's last update.
597
598       context (read-write, see below)
599
600       A Workflow::Context object associated with this workflow. This should
601       never be undefined as the Workflow::Factory sets an empty context into
602       the workflow when it is instantiated.
603
604       If you add a context to a workflow and one already exists, the values
605       from the new workflow will overwrite values in the existing workflow.
606       This is a shallow merge, so with the following:
607
608        $wf->context->param( drinks => [ 'coke', 'pepsi' ] );
609        my $context = Workflow::Context->new();
610        $context->param( drinks => [ 'beer', 'wine' ] );
611        $wf->context( $context );
612        print 'Current drinks: ', join( ', ', @{ $wf->context->param( 'drinks' ) } );
613
614       You will see:
615
616        Current drinks: beer, wine
617
618   Internal Methods
619       init( $id, $current_state, \%workflow_config, \@wf_states )
620
621       THIS SHOULD ONLY BE CALLED BY THE Workflow::Factory. Do not call this
622       or the "new()" method yourself -- you will only get an exception. Your
623       only interface for creating and fetching workflows is through the
624       factory.
625
626       This is called by the inherited constructor and sets the $current_state
627       value to the property "state" and uses the other non-state values from
628       "\%config" to set parameters via the inherited "param()".
629
630       _get_action( $action_name )
631
632       Retrieves the action object associated with $action_name in the current
633       workflow state. This will throw an exception if:
634
635       ·   No workflow state exists with a name of the current state. (This is
636           usually some sort of configuration error and should be caught at
637           initialization time, so it should not happen.)
638
639       ·   No action $action_name exists in the current state.
640
641       ·   No action $action_name exists in the workflow universe.
642
643       ·   One of the conditions for the action in this state is not met.
644
645       _get_workflow_state( [ $state ] )
646
647       Return the Workflow::State object corresponding to $state, which
648       defaults to the current state.
649
650       _set_workflow_state( $wf_state )
651
652       Assign the Workflow::State object $wf_state to the workflow.
653
654       _get_next_state( $action_name )
655
656       Returns the name of the next state given the action $action_name.
657       Throws an exception if $action_name not contained in the current state.
658

CONFIGURATION AND ENVIRONMENT

660       The configuration of Workflow is done using the format of your choice,
661       currently XML and Perl is implemented, but additional formats can be
662       added, please refer to Workflow::Config, for implementation details.
663

DEPENDENCIES

665       Class::Accessor
666       Class::Factory
667       Class::Observable
668       DateTime
669       DateTime::Format::Strptime
670       Exception::Class
671       Log::Dispatch
672       Log::Log4perl
673       Safe
674       XML::Simple
675       DBI
676       Data::Dumper
677       Carp
678       File::Slurp
679
680   DEPENDENCIES FOR THE EXAMPLE APPLICATION
681       CGI
682       CGI::Cookie
683       DBD::SQLite
684       HTTP::Daemon
685       HTTP::Request
686       HTTP::Response
687       HTTP::Status
688       Template (Template Toolkit)
689
690       For Win32 systems you can get the Template Toolkit and DBD::SQLite PPDs
691       from TheoryX:
692
693       ·   <http://theoryx5.uwinnipeg.ca/cgi-bin/ppmserver?urn:/PPMServer58>
694

INCOMPATIBILITIES

696   XML::Simple
697       CPAN testers reports however do demonstrate a problem with one of the
698       dependencies of Workflow, namely XML::Simple.
699
700       The XML::Simple makes use of Lib::XML::SAX or XML::Parser, the default.
701
702       In addition an XML::Parser can makes use of plugin parser and some of
703       these might not be able to parse the XML utilized in Workflow. The
704       problem have been observed with XML::SAX::RTF.
705
706       The following diagnostic points to the problem:
707
708               No _parse_* routine defined on this driver (If it is a filter, remember to
709               set the Parent property. If you call the parse() method, make sure to set a
710               Source. You may want to call parse_uri, parse_string or parse_file instead.)
711
712       Your XML::SAX configuration is located in the file:
713
714               XML/SAX/ParserDetails.ini
715
716   Perl 5.8.x
717       CPAN testers reports indicate an issue with observers for Perl 5.8.8
718
719           #   Failed test 'One observation sent on workflow fetch to two observers'
720           #   at t/workflow.t line 79.
721           #          got: '4'
722           #     expected: '2'
723           # Looks like you failed 1 test of 35.
724           t/workflow.t .......................
725           Dubious, test returned 1 (wstat 256, 0x100)
726           Failed 1/35 subtests
727
728       The issue is being investigated further, so this information is to be
729       regarded as a warning before you dig too much into the issue.
730
731       See also:
732
733       <http://www.cpantesters.org/cpan/report/fc85ca1c-e46e-11e2-891c-ff8a40f4ab3d>
734

BUGS AND LIMITATIONS

736       Known bugs and limitations can be seen in RT:
737
738       <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Workflow>
739

BUG REPORTING

741       Bug reporting should be done either via Request Tracker (RT)
742
743       <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Workflow>
744
745       Or via email
746
747       "bug-test-timer at rt.cpan.org"
748
749       A list of currently known issues can be seen via examining the RT queue
750       for Workflow.
751
752       <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Workflow>
753

TEST

755       The test suite can be run using, Module::Build
756
757               % ./Build test
758
759       Some of the tests are reserved for the developers and are only run of
760       the environment variable TEST_AUTHOR is set to true.
761

TEST COVERAGE

763       This is the current test coverage of Workflow version 1.32, with the
764       TEST_AUTHOR flag enabled.
765
766               ---------------------------- ------ ------ ------ ------ ------ ------ ------
767               File                           stmt   bran   cond    sub    pod   time  total
768               ---------------------------- ------ ------ ------ ------ ------ ------ ------
769               blib/lib/Workflow.pm           79.8   50.0   50.0   87.5  100.0    9.9   71.6
770               blib/lib/Workflow/Action.pm    90.8   66.7    n/a   88.2  100.0    4.1   89.9
771               ...flow/Action/InputField.pm   97.0   92.9   87.5  100.0  100.0    5.9   95.8
772               ...Workflow/Action/Mailer.pm  100.0    n/a    n/a  100.0  100.0    0.1  100.0
773               ...b/Workflow/Action/Null.pm  100.0    n/a    n/a  100.0  100.0    0.2  100.0
774               blib/lib/Workflow/Base.pm      96.6   86.4  100.0  100.0  100.0    9.6   95.0
775               ...lib/Workflow/Condition.pm  100.0    n/a    n/a  100.0  100.0    0.8  100.0
776               ...low/Condition/Evaluate.pm   59.0   16.7   33.3   87.5  100.0    0.9   53.0
777               ...flow/Condition/HasUser.pm   57.7    0.0    0.0   71.4  100.0    0.1   51.2
778               blib/lib/Workflow/Config.pm    96.2   81.2   33.3  100.0  100.0    6.1   92.2
779               ...b/Workflow/Config/Perl.pm   96.8   75.0   66.7  100.0  100.0    4.1   91.0
780               ...ib/Workflow/Config/XML.pm   92.3   50.0   60.0  100.0  100.0    4.9   81.4
781               blib/lib/Workflow/Context.pm  100.0    n/a    n/a  100.0  100.0    0.4  100.0
782               ...lib/Workflow/Exception.pm   89.2   50.0    n/a   91.7  100.0    3.1   89.5
783               blib/lib/Workflow/Factory.pm   86.3   61.2   37.5   92.3  100.0   19.6   75.4
784               blib/lib/Workflow/History.pm  100.0   87.5    n/a  100.0  100.0    1.8   98.1
785               ...lib/Workflow/Persister.pm   90.5   75.0   57.1   88.9  100.0    1.9   87.5
786               ...Workflow/Persister/DBI.pm   75.3   51.2   25.0   83.3  100.0    7.4   67.5
787               ...er/DBI/AutoGeneratedId.pm   77.8   40.0    n/a  100.0  100.0    0.4   70.1
788               ...ersister/DBI/ExtraData.pm   25.9    0.0    0.0   71.4  100.0    0.1   22.9
789               ...rsister/DBI/SequenceId.pm   56.2    0.0    0.0   75.0  100.0    0.3   53.1
790               ...orkflow/Persister/File.pm   94.4   48.0   33.3  100.0  100.0    2.1   83.1
791               ...low/Persister/RandomId.pm  100.0    n/a  100.0  100.0  100.0    1.8  100.0
792               ...rkflow/Persister/SPOPS.pm   89.6   50.0    n/a  100.0  100.0    0.3   85.0
793               ...orkflow/Persister/UUID.pm  100.0    n/a    n/a  100.0  100.0    0.2  100.0
794               blib/lib/Workflow/State.pm     74.4   44.2   25.0   91.7  100.0   11.0   64.3
795               ...lib/Workflow/Validator.pm  100.0  100.0    n/a  100.0  100.0    1.1  100.0
796               ...dator/HasRequiredField.pm   90.0   50.0    n/a  100.0  100.0    0.6   86.7
797               ...dator/InEnumeratedType.pm  100.0  100.0    n/a  100.0  100.0    0.4  100.0
798               ...ator/MatchesDateFormat.pm   93.3   70.0   66.7  100.0  100.0    0.8   88.2
799               Total                          83.9   54.7   39.7   93.0  100.0  100.0   76.8
800               ---------------------------- ------ ------ ------ ------ ------ ------ ------
801
802       Activities to get improved coverage are ongoing.
803

QUALITY ASSURANCE

805       The Workflow project utilizes Perl::Critic in an attempt to avoid
806       common pitfalls and programming mistakes.
807
808       The static analysis performed by Perl::Critic is integrated into the
809       "TEST" tool chain and is performed either by running the test suite.
810
811               % ./Build test
812
813       Or by running the test file containing the Perl::Critic tests
814       explicitly.
815
816               % ./Build test --verbose 1 --test_files t/04_critic.t
817
818       Or
819
820               % perl t/critic.t
821
822       The test does however require that the TEST_AUTHOR flag is set since
823       this is regarded as a part of the developer tool chain and we do not
824       want to disturb users and CPAN testers with this.
825
826       The following policies are disabled
827
828       ·   Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers
829
830       ·   Perl::Critic::Policy::Subroutines::ProhibitExplicitReturnUndef
831
832       ·   Perl::Critic::Policy::NamingConventions::ProhibitAmbiguousNames
833
834       ·   Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma
835
836       The complete policy configuration can be found in t/perlcriticrc.
837
838       Currently a large number other policies are disabled, but these are
839       being addressed as ongoing work and they will either be listed here or
840       changes will be applied, which will address the Workflow code's
841       problematic areas from Perl::Critic perspective.
842

CODING STYLE

844       Currently the code is formatted using Perl::Tidy. The resource file can
845       be downloaded from the central repository.
846
847               notes/perltidyrc
848

PROJECT

850       The Workflow project is currently hosted with SourceForge.net and is
851       listed on Ohloh.
852
853       SF.net: <http://perl-workflow.sf.net>
854       Ohloh: <https://www.ohloh.net/p/perl-Workflow>
855
856   REPOSITORY
857       The code is kept under revision control using Subversion:
858
859       <https://perl-workflow.svn.sourceforge.net/svnroot/perl-workflow>
860
861   MAILING LIST
862       The Workflow project has a mailing list for discussion of issues and
863       development. The list is low-traffic.
864
865       <http://sourceforge.net/mail/?group_id=177533> (including archive)
866
867   RSS FEEDS
868       Commit log
869       <http://rss.gmane.org/messages/excerpts/gmane.comp.lang.perl.modules.workflow.scm>
870       Ohloh news <https://www.ohloh.net/p/perl-Workflow/messages.rss>
871       CPAN testers reports <http://cpantesters.perl.org/show/Workflow.rss> in
872       matrix:
873
874   OTHER RESOURCES
875       ·   AnnoCPAN: Annotated CPAN documentation
876
877           <http://annocpan.org/dist/Workflow>
878
879       ·   CPAN Ratings
880
881           <http://cpanratings.perl.org/d/Workflow>
882
883       ·   Search CPAN
884
885           <http://search.cpan.org/dist/Workflow>
886

SEE ALSO

888       ·   November 2010 talk 'Workflow' given at Nordic Perl Workshop 2010 in
889           Reykjavik, Iceland by jonasbn
890           <http://www.slideshare.net/jonasbn/workflow-npw2010>
891
892       ·   August 2010 talk 'Workflow' given at YAPC::Europe 2010 in Pisa,
893           Italy by jonasbn
894           <http://www.slideshare.net/jonasbn/workflow-yapceu2010>
895
896       ·   October 2004 talk 'Workflows in Perl' given to pgh.pm by Chris
897           Winters: <http://www.cwinters.com/pdf/workflow_pgh_pm.pdf>
898
900       Copyright (c) 2003 Chris Winters and Arvato Direct; Copyright (c)
901       2004-2017 Chris Winters. All rights reserved.
902
903       This library is free software; you can redistribute it and/or modify it
904       under the same terms as Perl itself.
905

AUTHORS

907       Jonas B. Nielsen (jonasbn) <jonasbn@cpan.org>, current maintainer.
908
909       Chris Winters <chris@cwinters.com>, original author.
910
911       The following folks have also helped out (listed here in no particular
912       order):
913
914       Patch from Oliver Welter resulting in release 1.46
915
916       Bug report from Slaven ReziX resulting in maintenance release 1.45
917
918       Feature and bug fix by dtikhonov resulting in 1.40 (first pull request
919       on Github)
920
921       Sergio Alves, patch to timezone handling for workflow history
922       deserialized using DBI persister resulting in 1.38
923
924       Heiko Schlittermann for context serialization patch resulting in 1.36
925
926       Scott Harding, for lazy evaluation of conditions and for nested
927       conditions, see Changes file: 1.35
928
929       Oliver Welter, patch implementing custom workflows, see Changes file:
930       1.35 and patch related to this in 1.37 and factory subclassing also in
931       1.35. Improvements in logging for condition validation in 1.43 and 1.44
932
933       Steven van der Vegt, patch for autorun in initial state and improved
934       exception handling for validators, see Changes file: 1.34_1
935
936       Andrew O'Brien, patch implementing dynamic reloaded of flows, see
937       Changes file: 1.33
938
939       Sergei Vyshenski, bug reports - addressed and included in 1.33, Sergei
940       also maintains the FreeBSD port
941
942       Alejandro Imass, improvements and clarifications, see Changes file:
943       1.33
944
945       Danny Sadinoff, patches to give better control of initial state and
946       history records for workflow, see Changes file: 1.33
947
948       Thomas Erskine, for patch adding new accessors and fixing several bugs
949       see Changes file 1.33
950
951       Ivan Paponov, for patch implementing action groups, see Changes file,
952       1.33
953
954       Robert Stockdale, for patch implementing dynamic names for conditions,
955       see Changes file, 1.32
956
957       Jim Brandt, for patch to Workflow::Config::XML. See Changes file, 0.27
958       and 0.30
959
960       Alexander Klink, for: patches resulting in 0.23, 0.24, 0.25, 0.26 and
961       0.27
962
963       Michael Bell, for patch resulting in 0.22
964
965       Martin Bartosch, for bug reporting and giving the solution not even
966       using a patch (0.19 to 0.20) and a patch resulting in 0.21
967
968       Randal Schwartz, for testing 0.18 and swiftly giving feedback (0.18 to
969       0.19)
970
971       Chris Brown, for a patch to Workflow::Config::Perl (0.17 to 0.18)
972
973       Dietmar Hanisch <Dietmar.Hanisch@Bertelsmann.de> - Provided most of the
974       good ideas for the module and an excellent example of everyday use.
975
976       Tom Moertel <tmoertel@cpan.org> gave me the idea for being able to
977       attach event listeners (observers) to the process.
978
979       Michael Roberts <michael@vivtek.com> graciously released the 'Workflow'
980       namespace on CPAN; check out his Workflow toolkit at
981       <http://www.vivtek.com/wftk.html>.
982
983       Michael Schwern <schwern@pobox.org> barked via RT about a dependency
984       problem and CPAN naming issue.
985
986       Jim Smith <jgsmith@tamu.edu> - Contributed patches (being able to
987       subclass Workflow::Factory) and good ideas.
988
989       Martin Winkler <mw@arsnavigandi.de> - Pointed out a bug and a few other
990       items.
991
992
993
994perl v5.30.0                      2019-07-26                       Workflow(3)
Impressum