File Coverage

lib/Workflow/Config.pm
Criterion Covered Total %
statement 52 54 96.3
branch 13 16 81.2
condition 1 3 33.3
subroutine 13 13 100.0
pod 5 5 100.0
total 84 91 92.3


line stmt bran cond sub pod time code
1              
2             use warnings;
3 21     21   816 use strict;
  21         36  
  21         753  
4 21     21   108 use base qw( Class::Factory );
  21         41  
  21         485  
5 21     21   105 use Data::Dumper qw( Dumper );
  21         41  
  21         11504  
6 21     21   41411 use Log::Log4perl qw( get_logger );
  21         131977  
  21         1738  
7 21     21   153 use Workflow::Exception qw( configuration_error );
  21         68  
  21         208  
8 21     21   1708  
  21         52  
  21         10952  
9             $Workflow::Config::VERSION = '1.60';
10              
11             # Map the valid type to the top-level XML tag or data
12             # structure to look for.
13             my %VALID_TYPES = (
14             action => 'actions',
15             condition => 'conditions',
16             persister => 'persister',
17             validator => 'validators',
18             workflow => 'workflow',
19             );
20              
21             my ( $class, $type ) = @_;
22             return $VALID_TYPES{$type};
23 210     210 1 402 }
24 210         746  
25             my @keys = sort keys %VALID_TYPES;
26              
27             return @keys;
28 2     2 1 923 }
29              
30 2         12 my ( $class, $type ) = @_;
31             return $VALID_TYPES{$type};
32             }
33              
34 101     101 1 336 # Class method that allows you to pass in any type of items in
35 101         354 # @items. So you can do:
36             #
37             # Workflow::Config->parse_all_files( 'condition', 'my_condition.xml', 'your_condition.perl' );
38              
39             my ( $class, $type, @files ) = @_;
40              
41             return () unless ( scalar @files );
42              
43             my %parsers = ();
44 123     123 1 2053 my %parse_types = map { $_ => 1 } $class->get_registered_types;
45              
46 123 100       363 my @configurations = ();
47              
48 122         254 foreach my $file (@files) {
49 122         622 next unless ($file);
  366         2342  
50             my ($file_type) = $file =~ /\.(\w+)$/;
51 122         309 unless ( $parse_types{$file_type} ) {
52             configuration_error
53 122         223 "Cannot parse configuration file '$file' of workflow ",
54 123 100       273 "type '$type'. The file has unknown configuration type ",
55 100         956 "'$file_type'; known configuration types are: ", "'",
56 100 100       319 join( ', ', keys %parse_types ), "'";
57 1         10 }
58             unless ( $parsers{$file_type} ) {
59             $parsers{$file_type} = $class->new($file_type);
60             }
61             push @configurations, $parsers{$file_type}->parse( $type, $file );
62             }
63 99 100       264 return @configurations;
64 98         376 }
65              
66 99         2750 my ( $self, $type, @items ) = @_;
67             my $class = ref($self) || $self;
68 121         1199 configuration_error "Class $class must implement 'parse()'";
69             }
70              
71             my ( $class, $type ) = @_;
72 1     1 1 994 unless ( $class->is_valid_config_type($type) ) {
73 1   33     6 configuration_error "When parsing a configuration file the ",
74 1         5 "configuration type (first argument) must be ", "one of: ",
75             join ', ', $class->get_valid_config_types;
76             }
77             }
78 121     121   266  
79 121 100       374 my (@items) = @_;
80 1         8 my @all = ();
81              
82             if ( !scalar @items ) {
83             return @all;
84             }
85              
86             foreach my $item (@items) {
87 119     119   258 next unless ($item);
88 119         198 push @all, ( ref $item eq 'ARRAY' ) ? @{$item} : $item;
89             }
90 119 50       277 return @all;
91 0         0 }
92              
93             __PACKAGE__->register_factory_type( perl => 'Workflow::Config::Perl' );
94 119         230 __PACKAGE__->register_factory_type( pl => 'Workflow::Config::Perl' );
95 128 50       257 __PACKAGE__->register_factory_type( xml => 'Workflow::Config::XML' );
96 128 50       443  
  0         0  
97             1;
98 119         296  
99              
100             =pod
101              
102             =head1 NAME
103              
104             Workflow::Config - Parse configuration files for the workflow components
105              
106             =head1 VERSION
107              
108             This documentation describes version 1.60 of this package
109              
110             =head1 SYNOPSIS
111              
112             # Reference multiple files
113              
114             my $parser = Workflow::Config->new( 'xml' );
115             my @config = $parser->parse(
116             'action', 'workflow_action.xml', 'other_actions.xml'
117             );
118              
119             # Read in one of the file contents from somewhere else
120             my $xml_contents = read_contents_from_db( 'other_actions.xml' );
121             my @config = $parser->parse(
122             'action', 'workflow_action.xml', \$xml_contents
123             );
124             _
125             # Reference multiple files of mixed types
126              
127             my @action_config = Workflow::Config->parse_all_files(
128             'action', 'my_actions.xml', 'your_actions.perl'
129             );
130              
131             =head1 DESCRIPTION
132              
133             Read in configurations for the various workflow components. Currently
134             the class understands XML (preferred) and serialized Perl data
135             structures as valid configuration file formats. (I tried to use INI
136             files but there was too much deeply nested information. Sorry.)
137              
138             =head1 CLASS METHODS
139              
140             =head3 parse_all_files( $workflow_config_type, @files )
141              
142             Runs through each file in C<@files> and processes it according to the valid
143              
144             =head1 SUBCLASSING
145              
146             =head2 Creating Your Own Parser
147              
148             If you want to store your configuration in a different format you can
149             create your own parser. All you need to do is:
150              
151             =over 4
152              
153             =item 1.
154              
155             subclass L<Workflow::Config>
156              
157             =item 2.
158              
159             implement the required methods (listed below)
160              
161             =item 3.
162              
163             register your parser with L<Workflow::Config>.
164              
165             =back
166              
167             For instance, if you wanted to use YAML for configuration files you
168             would do something like:
169              
170             # just a convention, you can use any namespace you want
171             package Workflow::Config::YAML;
172              
173             use strict;
174              
175             # Requirement 1: Subclass Workflow::Config
176             use base qw( Workflow::Config );
177              
178             # Requirement 2: Implement required methods
179             sub parse { ... }
180              
181             The third requirement is registration, which just tells
182             L<Workflow::Config> which parser to use for a particular type. To do
183             this you have two options.
184              
185             B<Registration option one>
186              
187             Register yourself in your own class, adding the following call
188             anywhere the end:
189              
190             # Option 1: Register ourselves by name
191             Workflow::Config->register_factory_type( yaml => 'Workflow::Config::YAML' );
192              
193             Now you just need to include the configuration class in your workflow
194             invocation script:
195              
196             use strict;
197             use Workflow::Factory qw( FACTORY );
198             use Workflow::Config::YAML; # <-- brings in the registration
199              
200             B<Registration option two>
201              
202             You can also just explicitly add the registration from your workflow
203             invocation script:
204              
205             use strict;
206             use Workflow::Factory qw( FACTORY );
207             use Workflow::Config;
208              
209             # Option 2: explicitly register your configuration parser
210             Workflow::Config->register_factory_type( yaml => 'Workflow::Config::YAML' );
211              
212             Whichever one you choose you can now parse (in this example) YAML
213             files alongside the built-in parsers for XML and Perl files:
214              
215             FACTORY->add_config_from_file(
216             workflow => 'workflow.yaml',
217             action => [ 'my_actions.yaml', 'other_actions.xml' ],
218             validator => 'validators.yaml',
219             condition => [ 'my_conditions.yaml', 'other_conditions.xml' ]
220             persister => 'persister.perl',
221             );
222              
223             =head2 Inherited Methods
224              
225             =head3 new( $parser_type )
226              
227             Instantiates an object of the correct type -- see L<Class::Factory>
228             for how this is implemented:
229              
230             # Parser of type 'Workflow::Config::XML'
231             my $xml_parser = Workflow::Config->new( 'xml' );
232              
233             # Parser of type 'Workflow::Config::Perl
234             my $perl_parser = Workflow::Config->new( 'perl' );
235              
236             =head3 is_valid_config_type( $config_type )
237              
238             Returns true if C<$config_type> is a valid configuration type, false
239             if not. Valid configuration types are: 'action', 'condition',
240             'validator', 'workflow'.
241              
242             =head3 get_valid_config_types()
243              
244             Returns list of strings representing the valid configuration types.
245              
246             =head3 get_config_type_tag( $class, $type )
247              
248             Returns string representing a valid configuration type, looking up the type
249             parameter in a lookuptable defined in Workflow::Config class.
250              
251             =head2 Required Object Methods
252              
253             =head3 parse( $workflow_config_type, @items )
254              
255             Parse each item in C<@items> to a hash reference based on the
256             configuration type C<$config_type> which must pass the
257             C<is_valid_config_type()> test. An 'item' is either a filename or a
258             scalar reference with the contents of a file. (You can mix and match
259             as seen in the L<SYNOPSIS>.)
260              
261             Should throw an exception if:
262              
263             =over 4
264              
265             =item *
266              
267             You pass an invalid workflow configuration type. Valid workflow
268             configuration types are registered in L<Workflow::Config> and are
269             available from C<get_valid_config_types()>; you can check whether a
270             particular type is valid with C<is_valid_config_type()>. (See above
271             for descriptions.)
272              
273             =item *
274              
275             You pass in a file that cannot be read or parsed because of
276             permissions, malformed XML, incorrect Perl data structure, etc. It
277             does B<not> do a validation check (e.g., to ensure that every 'action'
278             within a workflow state has a 'resulting_state' key).
279              
280             =back
281              
282             Returns: one hash reference for each member of C<@items>
283              
284             =head1 CONFIGURATION INFORMATION
285              
286             This gives you an idea of the configuration information in the various
287             workflow pieces:
288              
289             =head2 workflow
290              
291             workflow
292             class $
293             type $
294             description $
295             persister $
296             initial_state $
297             observer \@
298             sub $
299             class $
300             state \@
301             name $
302             description $
303             action \@
304             name $
305             resulting_state $
306             condition \@
307             name $
308              
309             =over 4
310              
311             =item *
312              
313             the 'class', 'type' and 'description' keys are at the top level
314              
315             =item *
316              
317             'persister' key holds a string declaring the name of a persister
318             as declared in the array of persisters
319              
320             =item *
321              
322             'initial_state' key holds a string declaring the name of the initial state.
323             by default, this value is 'INIITAL'.
324              
325             =item *
326              
327             'state' key holds array of one or more 'state' declarations; one of
328             them must be 'INITIAL' (or the value of initial_state, if it's defined)
329              
330             =item *
331              
332             each 'state' declaration holds 'description' and 'name' keys and
333             multiple 'action' declarations
334              
335             =item *
336              
337             each 'action' declaration holds 'name' and 'resulting_state' keys and
338             may hold a 'condition' key with one or more named conditions
339              
340             =back
341              
342             =head2 condition
343              
344             conditions:
345              
346             condition \@
347             name $
348             class $
349             param \@
350             name $
351             value $
352              
353             =over 4
354              
355             =item *
356              
357             array of one or more hashrefs with 'name' and 'class' keys
358              
359             =back
360              
361             =head2 validator
362              
363             validators:
364              
365             validator \@
366             name $
367             class $
368             param \@
369             name $
370             value $
371              
372             =over 4
373              
374             =item *
375              
376             array of one or more hashrefs with 'name' and 'class' keys, plus
377             possibly one or more 'param' hashrefs each with 'name' and 'value'
378             keys
379              
380             =back
381              
382             =head2 action
383              
384             actions:
385              
386             action \@
387             name $
388             class $
389             description $
390             type $
391             field \@
392             name $
393             is_required yes|no
394             type $
395             source_list \@ of $
396             source_class $
397             param \@
398             name $
399             value $
400             validator \@
401             name $
402             arg \@
403             value $
404              
405             =over 4
406              
407             =item *
408              
409             array of one or more action hashrefs with 'name', 'class' and
410             'description' keys
411              
412             =item *
413              
414             each 'action' may specify a 'type' (default value: 'default'); in case
415             a workflow specifies a 'type', actions specifying the same 'type' will
416             be preferred over actions with the 'default' type when multiple actions
417             by the same name exist.
418              
419             =item *
420              
421             each 'action' may have zero or more values used to fill it; each value
422             has a 'name', 'description' and 'necessity' ('required' or 'optional')
423              
424             =item *
425              
426             each 'action' may have any number of 'param' hashrefs, each with
427             'name' and 'value'
428              
429             =item *
430              
431             each 'action' may have any number of 'validator' hashrefs, each with a
432             'name' key and array of 'arg' declarations
433              
434             =back
435              
436             =head2 persister
437              
438             persister:
439             name $ # all persister classes
440             class $ # all persister classes
441             use_random yes|no # all persister classes
442             use_uuid yes|no # all persister classes
443              
444             driver $ # DBI persisters
445             dsn $ # DBI persisters
446             user $ # DBI persisters
447             password $ # DBI persisters
448             workflow_table $ # DBI persisters
449             history_table $ # DBI persisters
450             autocommit $ # DBI persisters
451             date_format $ # DBI persisters
452              
453             table $ # DBI/ExtraData persisters
454             data_field $ # DBI/ExtraData persisters
455             context_key $ # DBI/ExtraData persisters
456              
457             path $ # File persisters
458              
459             =over 4
460              
461             =item *
462              
463             'name' key holds a string declaring the name by which workflows may
464             refer to this persister configuration
465              
466             =item *
467              
468             'class' key names a Perl class name to use when instantiating the persister
469              
470             =item *
471              
472             'use_random' key holds a string declaring (through 'yes'/'no' value) to
473             use random values for the workflow identifier
474              
475             =item *
476              
477             'use_uuid' key holds a string declaring (through 'yes'/'no' value) to
478             use UUID (universally unique ID) values for the workflow identifier; UUIDs
479             take preference over random IDs
480              
481             =back
482              
483             For documentation of the other keys, please refer to the respective classes.
484              
485             =head1 COPYRIGHT
486              
487             Copyright (c) 2003-2022 Chris Winters. All rights reserved.
488              
489             This library is free software; you can redistribute it and/or modify
490             it under the same terms as Perl itself.
491              
492             Please see the F<LICENSE>
493              
494             =head1 AUTHORS
495              
496             Please see L<Workflow>
497              
498             =cut