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 1.51 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 $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
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
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
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
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
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.
483
484 Returns: new state of workflow
485
486 get_current_actions( $group )
487
488 Returns a list of action names available from the current state for the
489 given environment. So if you keep your "context()" the same if you call
490 "execute_action()" with one of the action names you should not trigger
491 any condition error since the action has already been screened for
492 conditions. If you want to divide actions in groups (for example state
493 change group, approval group, which have to be shown at different
494 places on the page) add group property to your action
495
496 <action name="terminate request" group="state change"
497 class="MyApp::Action::Terminate" /> <action name="approve request"
498 group="approval" class="MyApp::Action::Approve" />
499
500 my @actions = $wf->get_current_actions("approval");
501
502 $group should be string that reperesents desired group name. In
503 @actions you will get list of action names available from the current
504 state for the given environment limited by group. $group is optional
505 parameter.
506
507 Returns: list of strings representing available actions
508
509 get_action_fields( $action_name )
510
511 Return a list of Workflow::Action::InputField objects for the given
512 $action_name. If $action_name not in the current state or not
513 accessible by the environment an exception is thrown.
514
515 Returns: list of Workflow::Action::InputField objects
516
517 add_history( @( \%params | $wf_history_object ) )
518
519 Adds any number of histories to the workflow, typically done by an
520 action in "execute_action()" or one of the observers of that action.
521 This history will not be saved until "execute_action()" is complete.
522
523 You can add a list of either hashrefs with history information in them
524 or full Workflow::History objects. Trying to add anything else will
525 result in an exception and none of the items being added.
526
527 Successfully adding the history objects results in a 'add history'
528 observation being thrown. See "WORKFLOWS ARE OBSERVABLE" above for
529 more.
530
531 Returns: nothing
532
533 get_history()
534
535 Returns list of history objects for this workflow. Note that some may
536 be unsaved if you call this during the "execute_action()" process.
537
538 get_unsaved_history()
539
540 Returns list of all unsaved history objects for this workflow.
541
542 clear_history()
543
544 Clears all transient history objects from the workflow object, not from
545 the long-term storage.
546
547 set( $property, $value )
548
549 Method used to overwrite Class::Accessor so only certain callers can
550 set properties caller has to be a Workflow namespace package.
551
552 Sets property to value or throws Workflow::Exception
553
554 Properties
555 Unless otherwise noted, properties are read-only.
556
557 Configuration Properties
558
559 Some properties are set in the configuration file for each workflow.
560 These remain static once the workflow is instantiated.
561
562 type
563
564 Type of workflow this is. You may have many individual workflows
565 associated with a type or you may have many different types running in
566 a single workflow engine.
567
568 description
569
570 Description (usually brief, hopefully with a URL...) of this workflow.
571
572 time_zone
573
574 Workflow uses the DateTime module to create all date objects. The
575 time_zone parameter allows you to pass a time zone value directly to
576 the DateTime new method for all cases where Workflow needs to create a
577 date object. See the DateTime module for acceptable values.
578
579 Dynamic Properties
580
581 You can get the following properties from any workflow object.
582
583 id
584
585 ID of this workflow. This will always be defined, since when the
586 Workflow::Factory creates a new workflow it first saves it to long-term
587 storage.
588
589 state
590
591 The current state of the workflow.
592
593 last_update (read-write)
594
595 Date of the workflow's last update.
596
597 context (read-write, see below)
598
599 A Workflow::Context object associated with this workflow. This should
600 never be undefined as the Workflow::Factory sets an empty context into
601 the workflow when it is instantiated.
602
603 If you add a context to a workflow and one already exists, the values
604 from the new workflow will overwrite values in the existing workflow.
605 This is a shallow merge, so with the following:
606
607 $wf->context->param( drinks => [ 'coke', 'pepsi' ] );
608 my $context = Workflow::Context->new();
609 $context->param( drinks => [ 'beer', 'wine' ] );
610 $wf->context( $context );
611 print 'Current drinks: ', join( ', ', @{ $wf->context->param( 'drinks' ) } );
612
613 You will see:
614
615 Current drinks: beer, wine
616
617 Internal Methods
618 init( $id, $current_state, \%workflow_config, \@wf_states )
619
620 THIS SHOULD ONLY BE CALLED BY THE Workflow::Factory. Do not call this
621 or the "new()" method yourself -- you will only get an exception. Your
622 only interface for creating and fetching workflows is through the
623 factory.
624
625 This is called by the inherited constructor and sets the $current_state
626 value to the property "state" and uses the other non-state values from
627 "\%config" to set parameters via the inherited "param()".
628
629 _get_action( $action_name )
630
631 Retrieves the action object associated with $action_name in the current
632 workflow state. This will throw an exception if:
633
634 • No workflow state exists with a name of the current state. (This is
635 usually some sort of configuration error and should be caught at
636 initialization time, so it should not happen.)
637
638 • No action $action_name exists in the current state.
639
640 • No action $action_name exists in the workflow universe.
641
642 • One of the conditions for the action in this state is not met.
643
644 _get_workflow_state( [ $state ] )
645
646 Return the Workflow::State object corresponding to $state, which
647 defaults to the current state.
648
649 _set_workflow_state( $wf_state )
650
651 Assign the Workflow::State object $wf_state to the workflow.
652
653 _get_next_state( $action_name )
654
655 Returns the name of the next state given the action $action_name.
656 Throws an exception if $action_name not contained in the current state.
657
658 add_observer( @observers )
659
660 Adds one or more observers to a "Workflow" instance. An observer is a
661 function. See "notify_observers" for its calling convention.
662
663 This function is used internally by "Workflow::Factory" to implement
664 observability as documented in the section "WORKFLOWS ARE OBSERVABLE"
665
666 notify_observers( @arguments )
667
668 Calls all observer functions registered through "add_observer" with the
669 workflow as the first argument and @arguments as the remaining
670 arguments:
671
672 $observer->( $wf, @arguments );
673
674 Used by various parts of the library to notify observers of workflow
675 instance related events.
676
678 The configuration of Workflow is done using the format of your choice,
679 currently XML and Perl is implemented, but additional formats can be
680 added, please refer to Workflow::Config, for implementation details.
681
683 Class::Accessor
684 Class::Factory
685 DateTime
686 DateTime::Format::Strptime
687 Exception::Class
688 Log::Log4perl
689 Safe
690 XML::Simple
691 DBI
692 Data::Dumper
693 Carp
694 File::Slurp
695 Data::UUID
696
697 DEPENDENCIES FOR THE EXAMPLE APPLICATION
698 CGI
699 CGI::Cookie
700 DBD::SQLite
701 HTTP::Daemon
702 HTTP::Request
703 HTTP::Response
704 HTTP::Status
705 Template (Template Toolkit)
706
707 For Win32 systems you can get the Template Toolkit and DBD::SQLite PPDs
708 from TheoryX:
709
710 • <http://theoryx5.uwinnipeg.ca/cgi-bin/ppmserver?urn:/PPMServer58>
711
713 XML::Simple
714 CPAN testers reports however do demonstrate a problem with one of the
715 dependencies of Workflow, namely XML::Simple.
716
717 The XML::Simple makes use of Lib::XML::SAX or XML::Parser, the default.
718
719 In addition an XML::Parser can makes use of plugin parser and some of
720 these might not be able to parse the XML utilized in Workflow. The
721 problem have been observed with XML::SAX::RTF.
722
723 The following diagnostic points to the problem:
724
725 No _parse_* routine defined on this driver (If it is a filter, remember to
726 set the Parent property. If you call the parse() method, make sure to set a
727 Source. You may want to call parse_uri, parse_string or parse_file instead.)
728
729 Your XML::SAX configuration is located in the file:
730
731 XML/SAX/ParserDetails.ini
732
734 Known bugs and limitations can be seen in the Github issue tracker:
735
736 <https://github.com/jonasbn/perl-workflow/issues>
737
739 Bug reporting should be done either via Github issues
740
741 <https://github.com/jonasbn/perl-workflow/issues>
742
743 A list of currently known issues can be seen via the same URL.
744
746 The test suite can be run using, Module::Build
747
748 % ./Build test
749
750 Some of the tests are reserved for the developers and are only run of
751 the environment variable TEST_AUTHOR is set to true.
752
754 This is the current test coverage of Workflow version 1.32, with the
755 TEST_AUTHOR flag enabled.
756
757 ---------------------------- ------ ------ ------ ------ ------ ------ ------
758 File stmt bran cond sub pod time total
759 ---------------------------- ------ ------ ------ ------ ------ ------ ------
760 blib/lib/Workflow.pm 79.8 50.0 50.0 87.5 100.0 9.9 71.6
761 blib/lib/Workflow/Action.pm 90.8 66.7 n/a 88.2 100.0 4.1 89.9
762 ...flow/Action/InputField.pm 97.0 92.9 87.5 100.0 100.0 5.9 95.8
763 ...Workflow/Action/Mailer.pm 100.0 n/a n/a 100.0 100.0 0.1 100.0
764 ...b/Workflow/Action/Null.pm 100.0 n/a n/a 100.0 100.0 0.2 100.0
765 blib/lib/Workflow/Base.pm 96.6 86.4 100.0 100.0 100.0 9.6 95.0
766 ...lib/Workflow/Condition.pm 100.0 n/a n/a 100.0 100.0 0.8 100.0
767 ...low/Condition/Evaluate.pm 59.0 16.7 33.3 87.5 100.0 0.9 53.0
768 ...flow/Condition/HasUser.pm 57.7 0.0 0.0 71.4 100.0 0.1 51.2
769 blib/lib/Workflow/Config.pm 96.2 81.2 33.3 100.0 100.0 6.1 92.2
770 ...b/Workflow/Config/Perl.pm 96.8 75.0 66.7 100.0 100.0 4.1 91.0
771 ...ib/Workflow/Config/XML.pm 92.3 50.0 60.0 100.0 100.0 4.9 81.4
772 blib/lib/Workflow/Context.pm 100.0 n/a n/a 100.0 100.0 0.4 100.0
773 ...lib/Workflow/Exception.pm 89.2 50.0 n/a 91.7 100.0 3.1 89.5
774 blib/lib/Workflow/Factory.pm 86.3 61.2 37.5 92.3 100.0 19.6 75.4
775 blib/lib/Workflow/History.pm 100.0 87.5 n/a 100.0 100.0 1.8 98.1
776 ...lib/Workflow/Persister.pm 90.5 75.0 57.1 88.9 100.0 1.9 87.5
777 ...Workflow/Persister/DBI.pm 75.3 51.2 25.0 83.3 100.0 7.4 67.5
778 ...er/DBI/AutoGeneratedId.pm 77.8 40.0 n/a 100.0 100.0 0.4 70.1
779 ...ersister/DBI/ExtraData.pm 25.9 0.0 0.0 71.4 100.0 0.1 22.9
780 ...rsister/DBI/SequenceId.pm 56.2 0.0 0.0 75.0 100.0 0.3 53.1
781 ...orkflow/Persister/File.pm 94.4 48.0 33.3 100.0 100.0 2.1 83.1
782 ...low/Persister/RandomId.pm 100.0 n/a 100.0 100.0 100.0 1.8 100.0
783 ...rkflow/Persister/SPOPS.pm 89.6 50.0 n/a 100.0 100.0 0.3 85.0
784 ...orkflow/Persister/UUID.pm 100.0 n/a n/a 100.0 100.0 0.2 100.0
785 blib/lib/Workflow/State.pm 74.4 44.2 25.0 91.7 100.0 11.0 64.3
786 ...lib/Workflow/Validator.pm 100.0 100.0 n/a 100.0 100.0 1.1 100.0
787 ...dator/HasRequiredField.pm 90.0 50.0 n/a 100.0 100.0 0.6 86.7
788 ...dator/InEnumeratedType.pm 100.0 100.0 n/a 100.0 100.0 0.4 100.0
789 ...ator/MatchesDateFormat.pm 93.3 70.0 66.7 100.0 100.0 0.8 88.2
790 Total 83.9 54.7 39.7 93.0 100.0 100.0 76.8
791 ---------------------------- ------ ------ ------ ------ ------ ------ ------
792
793 Activities to get improved coverage are ongoing.
794
796 The Workflow project utilizes Perl::Critic in an attempt to avoid
797 common pitfalls and programming mistakes.
798
799 The static analysis performed by Perl::Critic is integrated into the
800 "TEST" tool chain and is performed either by running the test suite.
801
802 % ./Build test
803
804 Or by running the test file containing the Perl::Critic tests
805 explicitly.
806
807 % ./Build test --verbose 1 --test_files t/04_critic.t
808
809 Or
810
811 % perl t/critic.t
812
813 The test does however require that the TEST_AUTHOR flag is set since
814 this is regarded as a part of the developer tool chain and we do not
815 want to disturb users and CPAN testers with this.
816
817 The following policies are disabled
818
819 • Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers
820
821 • Perl::Critic::Policy::Subroutines::ProhibitExplicitReturnUndef
822
823 • Perl::Critic::Policy::NamingConventions::ProhibitAmbiguousNames
824
825 • Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma
826
827 The complete policy configuration can be found in t/perlcriticrc.
828
829 Currently a large number other policies are disabled, but these are
830 being addressed as ongoing work and they will either be listed here or
831 changes will be applied, which will address the Workflow code's
832 problematic areas from Perl::Critic perspective.
833
835 Currently the code is formatted using Perl::Tidy. The resource file can
836 be downloaded from the central repository.
837
838 notes/perltidyrc
839
841 The Workflow project is currently hosted on GitHub
842
843 GitHub: <htts://github.com/jonasbn/perl-workflow>
844
845 REPOSITORY
846 The code is kept under revision control using Git:
847
848 <https://github.com/jonasbn/perl-workflow/tree/master/>
849
850 OTHER RESOURCES
851 • CPAN Ratings
852
853 <http://cpanratings.perl.org/d/Workflow>
854
855 • MetaCPAN
856
857 <https://metacpan.org/release/Workflow>
858
860 • November 2010 talk 'Workflow' given at Nordic Perl Workshop 2010 in
861 Reykjavik, Iceland by jonasbn
862 <http://www.slideshare.net/jonasbn/workflow-npw2010>
863
864 • August 2010 talk 'Workflow' given at YAPC::Europe 2010 in Pisa,
865 Italy by jonasbn
866 <http://www.slideshare.net/jonasbn/workflow-yapceu2010>
867
869 Copyright (c) 2003 Chris Winters and Arvato Direct; Copyright (c)
870 2004-2021 Chris Winters. All rights reserved.
871
872 This library is free software; you can redistribute it and/or modify it
873 under the same terms as Perl itself.
874
876 Jonas B. (jonasbn) <jonasbn@cpan.org>, current maintainer.
877
878 Chris Winters <chris@cwinters.com>, original author.
879
880 The following folks have also helped out (listed here in no particular
881 order):
882
883 Several PRs (13 to be exact) from Erik Huelsmann resulting in release
884 1.49. Yet another batch of PRs resulted in release 1.50
885
886 PR from Mohammad S Anwar correcting some POD errors, included in
887 release 1.49
888
889 Bug report from Petr Pisar resulted in release 1.48
890
891 Bug report from Tina Mueller (tinita) resulted in release 1.47
892
893 Bug report from Slaven ReziX resulting in maintenance release 1.45
894
895 Feature and bug fix by dtikhonov resulting in 1.40 (first pull request
896 on Github)
897
898 Sergio Alves, patch to timezone handling for workflow history
899 deserialized using DBI persister resulting in 1.38
900
901 Heiko Schlittermann for context serialization patch resulting in 1.36
902
903 Scott Harding, for lazy evaluation of conditions and for nested
904 conditions, see Changes file: 1.35
905
906 Oliver Welter, patch implementing custom workflows, see Changes file:
907 1.35 and patch related to this in 1.37 and factory subclassing also in
908 1.35. Improvements in logging for condition validation in 1.43 and 1.44
909 and again a patch resulting in release 1.46
910
911 Steven van der Vegt, patch for autorun in initial state and improved
912 exception handling for validators, see Changes file: 1.34_1
913
914 Andrew O'Brien, patch implementing dynamic reloaded of flows, see
915 Changes file: 1.33
916
917 Sergei Vyshenski, bug reports - addressed and included in 1.33, Sergei
918 also maintains the FreeBSD port
919
920 Alejandro Imass, improvements and clarifications, see Changes file:
921 1.33
922
923 Danny Sadinoff, patches to give better control of initial state and
924 history records for workflow, see Changes file: 1.33
925
926 Thomas Erskine, for patch adding new accessors and fixing several bugs
927 see Changes file 1.33
928
929 Ivan Paponov, for patch implementing action groups, see Changes file,
930 1.33
931
932 Robert Stockdale, for patch implementing dynamic names for conditions,
933 see Changes file, 1.32
934
935 Jim Brandt, for patch to Workflow::Config::XML. See Changes file, 0.27
936 and 0.30
937
938 Alexander Klink, for: patches resulting in 0.23, 0.24, 0.25, 0.26 and
939 0.27
940
941 Michael Bell, for patch resulting in 0.22
942
943 Martin Bartosch, for bug reporting and giving the solution not even
944 using a patch (0.19 to 0.20) and a patch resulting in 0.21
945
946 Randal Schwartz, for testing 0.18 and swiftly giving feedback (0.18 to
947 0.19)
948
949 Chris Brown, for a patch to Workflow::Config::Perl (0.17 to 0.18)
950
951 Dietmar Hanisch <Dietmar.Hanisch@Bertelsmann.de> - Provided most of the
952 good ideas for the module and an excellent example of everyday use.
953
954 Tom Moertel <tmoertel@cpan.org> gave me the idea for being able to
955 attach event listeners (observers) to the process.
956
957 Michael Roberts <michael@vivtek.com> graciously released the 'Workflow'
958 namespace on CPAN; check out his Workflow toolkit at
959 <http://www.vivtek.com/wftk.html>.
960
961 Michael Schwern <schwern@pobox.org> barked via RT about a dependency
962 problem and CPAN naming issue.
963
964 Jim Smith <jgsmith@tamu.edu> - Contributed patches (being able to
965 subclass Workflow::Factory) and good ideas.
966
967 Martin Winkler <mw@arsnavigandi.de> - Pointed out a bug and a few other
968 items.
969
970
971
972perl v5.32.1 2021-01-31 Workflow(3)