1Workflow(3) User Contributed Perl Documentation Workflow(3)
2
3
4
6 Workflow - Simple, flexible system to implement workflows
7
9 This documentation describes version 0.15 of Workflow
10
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
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
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
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
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
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
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
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
669 There are no known bugs and limitations at this time.
670
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
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
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
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
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
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
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
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)