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

NAME

6       Workflow - Simple, flexible system to implement workflows
7

SYNOPSIS

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

DESCRIPTION

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

WORKFLOW BASICS

204       Just a Bunch of States
205
206       A workflow is just a bunch of states with rules on how to move between
207       them. These are known as transitions and are triggered by some sort of
208       event. A state is just a description of object properties. You can
209       describe a surprisingly large number of processes as a series of states
210       and actions to move between them. The application shipped with this
211       distribution uses a fairly common application to illustrate: the trou‐
212       ble ticket.
213
214       When you create a workflow you have one action available to you: create
215       a new ticket ('create issue'). The workflow has a state 'INITIAL' when
216       it is first created, but this is just a bootstrapping exercise since
217       the workflow must always be in some state.
218
219       The workflow action 'create issue' has a property 'resulting_state',
220       which just means: if you execute me properly the workflow will be in
221       the new state 'CREATED'.
222
223       All this talk of 'states' and 'transitions' can be confusing, but just
224       match them to what happens in real life -- you move from one action to
225       another and at each step ask: what happens next?
226
227       You create a trouble ticket: what happens next? Anyone can add comments
228       to it and attach files to it while administrators can edit it and
229       developers can start working on it. Adding comments does not really
230       change what the ticket is, it just adds information. Attachments are
231       the same, as is the admin editing the ticket.
232
233       But when someone starts work on the ticket, that is a different matter.
234       When someone starts work they change the answer to: what happens next?
235       Whenever the answer to that question changes, that means the workflow
236       has changed state.
237
238       Discover Information from the Workflow
239
240       In addition to declaring what the resulting state will be from an
241       action the action also has a number of 'field' properties that describe
242       that data it required to properly execute it.
243
244       This is an example of discoverability. This workflow system is setup so
245       you can ask it what you can do next as well as what is required to move
246       on. So to use our ticket example we can do this, creating the workflow
247       and asking it what actions we can execute right now:
248
249        my $wf = Workflow::Factory->create_workflow( 'Ticket' );
250        my @actions = $wf->get_current_actions;
251
252       We can also interrogate the workflow about what fields are necessary to
253       execute a particular action:
254
255        print "To execute the action 'create issue' you must provide:\n\n";
256        my @fields = $wf->get_action_fields( 'create issue' );
257        foreach my $field ( @fields ) {
258            print $field->name, " (Required? ", $field->is_required, ")\n",
259                  $field->description, "\n\n";
260        }
261
262       Provide Information to the Workflow
263
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 pre‐
274       sume to know where to get this information. So you need to tell it, and
275       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
298       A typical process for executing an action is:
299
300       ·   Get data from the user
301
302       ·   Fetch a workflow
303
304       ·   Set the data from the user to the workflow context
305
306       ·   Execute an action on the context
307
308       When you execute the action a number of checks occur. The action needs
309       to ensure:
310
311       ·   The data presented to it are valid -- date formats, etc. This is
312           done with a validator, more at Workflow::Validator
313
314       ·   The environment meets certain conditions -- user is an administra‐
315           tor, etc. This is done with a condition, more at Workflow::Condi‐
316           tion
317
318       Once the action passes these checks and successfully executes we update
319       the permanent workflow storage with the new state, as long as the
320       application has declared it.
321

WORKFLOWS ARE OBSERVABLE

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

WORKFLOW METHODS

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

SEE ALSO

599       Workflow::Context
600
601       Workflow::Factory
602
603       Workflow::State
604
605       October 2004 talk 'Workflows in Perl' given to pgh.pm:
606       <http://www.cwinters.com/pdf/workflow_pgh_pm.pdf>
607
609       Copyright (c) 2003 Chris Winters and Arvato Direct; 2004, 2005, 2006
610       Chris Winters. All rights reserved.
611
612       This library is free software; you can redistribute it and/or modify it
613       under the same terms as Perl itself.
614

AUTHORS

616       Jonas B. Nielsen (jonasbn) <jonasbn@cpan.org>, current maintainer.
617
618       Chris Winters <chris@cwinters.com>, original author.
619
620       The following folks have also helped out:
621
622       Alexander Klink, for: patches resulting in 0.23, 0.24 and 0.25
623
624       Michael Bell, for patch resulting in 0.22
625
626       Martin Bartosch, for bug reporting and giving the solution not even
627       using a patch (0.19 to 0.20) and a patch resulting in 0.21
628
629       Randal Schwartz, for testing 0.18 and swiftly giving feedback (0.18 to
630       0.19)
631
632       Chris Brown, for a patch to Workflow::Config::Perl (0.17 to 0.18)
633
634       Dietmar Hanisch <Dietmar.Hanisch@Bertelsmann.de> - Provided most of the
635       good ideas for the module and an excellent example of everyday use.
636
637       Tom Moertel <tmoertel@cpan.org> gave me the idea for being able to
638       attach event listeners (observers) to the process.
639
640       Michael Roberts <michael@vivtek.com> graciously released the 'Workflow'
641       namespace on CPAN; check out his Workflow toolkit at
642       <http://www.vivtek.com/wftk.html>.
643
644       Michael Schwern <schwern@pobox.org> barked via RT about a dependency
645       problem and CPAN naming issue.
646
647       Jim Smith <jgsmith@tamu.edu> - Contributed patches (being able to sub‐
648       class Workflow::Factory) and good ideas.
649
650       Martin Winkler <mw@arsnavigandi.de> - Pointed out a bug and a few other
651       items.
652
653
654
655perl v5.8.8                       2007-04-25                       Workflow(3)
Impressum