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        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 = 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 = FACTORY->fetch_workflow( 'myworkflow', $id );
138        print "Current state: ", $workflow->state, "\n";
139

DESCRIPTION

141   Overview
142       This is a standalone workflow system. It is designed to fit into your
143       system rather than force your system to fit to it. You can save
144       workflow information to a database or the filesystem (or a custom
145       storage). The different components of a workflow system can be included
146       separately as libraries to allow for maximum reusibility.
147
148   User Point of View
149       As a user you only see two components, plus a third which is really
150       embedded into another:
151
152       ·   Workflow::Factory - The factory is your interface for creating new
153           workflows and fetching existing ones. You also feed all the
154           necessary configuration files and/or data structures to the factory
155           to initialize it.
156
157       ·   Workflow - When you get the workflow object from the workflow
158           factory you can only use it in a few ways -- asking for the current
159           state, actions available for the state, data required for a
160           particular action, and most importantly, executing a particular
161           action. Executing an action is how you change from one state to
162           another.
163
164       ·   Workflow::Context - This is a blackboard for data from your
165           application to the workflow system and back again. Each
166           instantiation of a Workflow has its own context, and actions
167           executed by the workflow can read data from and deposit data into
168           the context.
169
170   Developer Point of View
171       The workflow system has four basic components:
172
173       ·   workflow - The workflow is a collection of states; you define the
174           states, how to move from one state to another, and under what
175           conditions you can change states.
176
177           This is represented by the Workflow object. You normally do not
178           need to subclass this object for customization.
179
180       ·   action - The action is defined by you or in a separate library. The
181           action is triggered by moving from one state to another and has
182           access to the workflow and more importantly its context.
183
184           The base class for actions is the Workflow::Action class.
185
186       ·   condition - Within the workflow you can attach one or more
187           conditions to an action. These ensure that actions only get
188           executed when certain conditions are met. Conditions are completely
189           arbitrary: typically they will ensure the user has particular
190           access rights, but you can also specify that an action can only be
191           executed at certain times of the day, or from certain IP addresses,
192           and so forth. Each condition is created once at startup then passed
193           a context to check every time an action is checked to see if it can
194           be executed.
195
196           The base class for conditions is the Workflow::Condition class.
197
198       ·   validator - An action can specify one or more validators to ensure
199           that the data available to the action is correct. The data to check
200           can be as simple or complicated as you like. Each validator is
201           created once then passed a context and data to check every time an
202           action is executed.
203
204           The base class for validators is the Workflow::Validator class.
205

WORKFLOW BASICS

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

WORKFLOWS ARE OBSERVABLE

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

WORKFLOW METHODS

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

CONFIGURATION AND ENVIRONMENT

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

DEPENDENCIES

632       Class::Accessor
633       Class::Factory
634       Class::Observable
635       DateTime
636       DateTime::Format::Strptime
637       Exception::Class
638       Log::Dispatch
639       Log::Log4perl
640       Safe
641       XML::Simple
642       DBI
643       Data::Dumper
644       Carp
645       File::Slurp
646

INCOMPATIBILITIES

648       No special incompatibilies exist, CPAN testers reports however do
649       demonstrate a problem with one of the dependencies of Workflow, namely
650       XML::Simple.
651
652       The XML::Simple makes use of Lib::XML::SAX or XML::Parser, the default.
653
654       In addition an XML::Parser can makes use of plugin parser and some of
655       these might not be able to parse the XML utilized in Workflow. The
656       problem have been observed with XML::SAX::RTF.
657
658       The following diagnostic points to the problem:
659
660               No _parse_* routine defined on this driver (If it is a filter, remember to
661               set the Parent property. If you call the parse() method, make sure to set a
662               Source. You may want to call parse_uri, parse_string or parse_file instead.)
663
664       Your XML::SAX configuration is located in the file:
665
666               XML/SAX/ParserDetails.ini
667

BUGS AND LIMITATIONS

669       There are no known bugs and limitations at this time.
670

BUG REPORTING

672       Bug reporting should be done either via Request Tracker (RT)
673
674       <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Workflow>
675
676       Or via email
677
678       "bug-test-timer at rt.cpan.org"
679
680       A list of currently known issues can be seen via examining the RT queue
681       for Workflow.
682
683       <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Workflow>
684

TEST

686       The test suite can be run using, Module::Build
687
688               % ./Build test
689
690       Some of the tests are reserved for the developers and are only run of
691       the environment variable TEST_AUTHOR is set to true.
692

TEST COVERAGE

694       This is the current test coverage of Workflow version 1.32, with the
695       TEST_AUTHOR flag enabled.
696
697               ---------------------------- ------ ------ ------ ------ ------ ------ ------
698               File                           stmt   bran   cond    sub    pod   time  total
699               ---------------------------- ------ ------ ------ ------ ------ ------ ------
700               blib/lib/Workflow.pm           79.8   50.0   50.0   87.5  100.0    9.9   71.6
701               blib/lib/Workflow/Action.pm    90.8   66.7    n/a   88.2  100.0    4.1   89.9
702               ...flow/Action/InputField.pm   97.0   92.9   87.5  100.0  100.0    5.9   95.8
703               ...Workflow/Action/Mailer.pm  100.0    n/a    n/a  100.0  100.0    0.1  100.0
704               ...b/Workflow/Action/Null.pm  100.0    n/a    n/a  100.0  100.0    0.2  100.0
705               blib/lib/Workflow/Base.pm      96.6   86.4  100.0  100.0  100.0    9.6   95.0
706               ...lib/Workflow/Condition.pm  100.0    n/a    n/a  100.0  100.0    0.8  100.0
707               ...low/Condition/Evaluate.pm   59.0   16.7   33.3   87.5  100.0    0.9   53.0
708               ...flow/Condition/HasUser.pm   57.7    0.0    0.0   71.4  100.0    0.1   51.2
709               blib/lib/Workflow/Config.pm    96.2   81.2   33.3  100.0  100.0    6.1   92.2
710               ...b/Workflow/Config/Perl.pm   96.8   75.0   66.7  100.0  100.0    4.1   91.0
711               ...ib/Workflow/Config/XML.pm   92.3   50.0   60.0  100.0  100.0    4.9   81.4
712               blib/lib/Workflow/Context.pm  100.0    n/a    n/a  100.0  100.0    0.4  100.0
713               ...lib/Workflow/Exception.pm   89.2   50.0    n/a   91.7  100.0    3.1   89.5
714               blib/lib/Workflow/Factory.pm   86.3   61.2   37.5   92.3  100.0   19.6   75.4
715               blib/lib/Workflow/History.pm  100.0   87.5    n/a  100.0  100.0    1.8   98.1
716               ...lib/Workflow/Persister.pm   90.5   75.0   57.1   88.9  100.0    1.9   87.5
717               ...Workflow/Persister/DBI.pm   75.3   51.2   25.0   83.3  100.0    7.4   67.5
718               ...er/DBI/AutoGeneratedId.pm   77.8   40.0    n/a  100.0  100.0    0.4   70.1
719               ...ersister/DBI/ExtraData.pm   25.9    0.0    0.0   71.4  100.0    0.1   22.9
720               ...rsister/DBI/SequenceId.pm   56.2    0.0    0.0   75.0  100.0    0.3   53.1
721               ...orkflow/Persister/File.pm   94.4   48.0   33.3  100.0  100.0    2.1   83.1
722               ...low/Persister/RandomId.pm  100.0    n/a  100.0  100.0  100.0    1.8  100.0
723               ...rkflow/Persister/SPOPS.pm   89.6   50.0    n/a  100.0  100.0    0.3   85.0
724               ...orkflow/Persister/UUID.pm  100.0    n/a    n/a  100.0  100.0    0.2  100.0
725               blib/lib/Workflow/State.pm     74.4   44.2   25.0   91.7  100.0   11.0   64.3
726               ...lib/Workflow/Validator.pm  100.0  100.0    n/a  100.0  100.0    1.1  100.0
727               ...dator/HasRequiredField.pm   90.0   50.0    n/a  100.0  100.0    0.6   86.7
728               ...dator/InEnumeratedType.pm  100.0  100.0    n/a  100.0  100.0    0.4  100.0
729               ...ator/MatchesDateFormat.pm   93.3   70.0   66.7  100.0  100.0    0.8   88.2
730               Total                          83.9   54.7   39.7   93.0  100.0  100.0   76.8
731               ---------------------------- ------ ------ ------ ------ ------ ------ ------
732
733       Activities to get improved coverage are ongoing.
734

QUALITY ASSURANCE

736       The Workflow project utilizes Perl::Critic in an attempt to avoid
737       common pitfalls and programming mistakes.
738
739       The static analysis performed by Perl::Critic is integrated into the
740       "TEST" tool chain and is performed either by running the test suite.
741
742               % ./Build test
743
744       Or by running the test file containing the Perl::Critic tests
745       explicitly.
746
747               % ./Build test --verbose 1 --test_files t/04_critic.t
748
749       Or
750
751               % perl t/critic.t
752
753       The test does however require that the TEST_AUTHOR flag is set since
754       this is regarded as a part of the developer tool chain and we do not
755       want to disturb users and CPAN testers with this.
756
757       The following policies are disabled
758
759       ·   Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers
760
761       ·   Perl::Critic::Policy::Subroutines::ProhibitExplicitReturnUndef
762
763       ·   Perl::Critic::Policy::NamingConventions::ProhibitAmbiguousNames
764
765       ·   Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma
766
767       The complete policy configuration can be found in t/perlcriticrc.
768
769       Currently a large number other policies are disabled, but these are
770       being addressed as ongoing work and they will either be listed here or
771       changes will be applied, which will address the Workflow code's
772       problematic areas from Perl::Critic perspective.
773

CODING STYLE

775       Currently the code is formatted using Perl::Tidy. The resource file can
776       be downloaded from the central repository.
777
778               notes/perltidyrc
779

PROJECT

781       The Workflow project is currently hosted with SourceForge.net and is
782       listed on Ohloh.
783
784       SF.net: http://perl-workflow.sf.net <http://perl-workflow.sf.net>
785       Ohloh: https://www.ohloh.net/p/perl-Workflow
786       <https://www.ohloh.net/p/perl-Workflow>
787
788   REPOSITORY
789       The code is kept under revision control using Subversion:
790
791       https://perl-workflow.svn.sourceforge.net/svnroot/perl-workflow
792       <https://perl-workflow.svn.sourceforge.net/svnroot/perl-workflow>
793
794   MAILING LIST
795       The Workflow project has a mailing list for discussion of issues and
796       development. The list is low-traffic.
797
798       <http://sourceforge.net/mail/?group_id=177533>
799
800   RSS FEEDS
801       Commit log
802       <http://rss.gmane.org/messages/excerpts/gmane.comp.lang.perl.modules.workflow.scm>
803       Ohloh news https://www.ohloh.net/p/perl-Workflow/messages.rss
804       <https://www.ohloh.net/p/perl-Workflow/messages.rss>
805       CPAN testers reports <http://cpantesters.perl.org/show/Workflow.rss>
806
807   OTHER RESOURCES
808       ·   AnnoCPAN: Annotated CPAN documentation
809
810           <http://annocpan.org/dist/Workflow>
811
812       ·   CPAN Ratings
813
814           <http://cpanratings.perl.org/d/Workflow>
815
816       ·   Search CPAN
817
818           <http://search.cpan.org/dist/Workflow>
819

SEE ALSO

821       October 2004 talk 'Workflows in Perl' given to pgh.pm:
822       <http://www.cwinters.com/pdf/workflow_pgh_pm.pdf>
823
825       Copyright (c) 2003 Chris Winters and Arvato Direct; Copyright (c)
826       2004-2008 Chris Winters. All rights reserved.
827
828       This library is free software; you can redistribute it and/or modify it
829       under the same terms as Perl itself.
830

AUTHORS

832       Jonas B. Nielsen (jonasbn) <jonasbn@cpan.org>, current maintainer.
833
834       Chris Winters <chris@cwinters.com>, original author.
835
836       The following folks have also helped out:
837
838       Ivan Paponov, for patch implementing action groups, See Changes file,
839       0.32_7
840
841       Robert Stockdale, for patch implementing dynamic names for conditions,
842       See Changes file, 0.32_6
843
844       Jim Brandt, for patch to Workflow::Config::XML. See Changes file, 0.27
845       and 0.30
846
847       Alexander Klink, for: patches resulting in 0.23, 0.24, 0.25, 0.26 and
848       0.27
849
850       Michael Bell, for patch resulting in 0.22
851
852       Martin Bartosch, for bug reporting and giving the solution not even
853       using a patch (0.19 to 0.20) and a patch resulting in 0.21
854
855       Randal Schwartz, for testing 0.18 and swiftly giving feedback (0.18 to
856       0.19)
857
858       Chris Brown, for a patch to Workflow::Config::Perl (0.17 to 0.18)
859
860       Dietmar Hanisch <Dietmar.Hanisch@Bertelsmann.de> - Provided most of the
861       good ideas for the module and an excellent example of everyday use.
862
863       Tom Moertel <tmoertel@cpan.org> gave me the idea for being able to
864       attach event listeners (observers) to the process.
865
866       Michael Roberts <michael@vivtek.com> graciously released the 'Workflow'
867       namespace on CPAN; check out his Workflow toolkit at
868       <http://www.vivtek.com/wftk.html>.
869
870       Michael Schwern <schwern@pobox.org> barked via RT about a dependency
871       problem and CPAN naming issue.
872
873       Jim Smith <jgsmith@tamu.edu> - Contributed patches (being able to
874       subclass Workflow::Factory) and good ideas.
875
876       Martin Winkler <mw@arsnavigandi.de> - Pointed out a bug and a few other
877       items.
878
879
880
881perl v5.12.0                      2010-05-07                       Workflow(3)
Impressum