File Coverage

blib/lib/Workflow/InputField.pm
Criterion Covered Total %
statement 72 73 98.6
branch 25 26 96.1
condition 7 8 87.5
subroutine 14 14 100.0
pod 6 6 100.0
total 124 127 97.6


line stmt bran cond sub pod time code
1             package Workflow::InputField;
2              
3 30     30   1402078 use warnings;
  30         75  
  30         1840  
4 30     30   179 use strict;
  30         115  
  30         704  
5 30     30   362 use v5.14.0;
  30         110  
6 30     30   337 use parent qw( Class::Accessor );
  30         124  
  30         241  
7 30     30   7822 use Log::Any;
  30         11166  
  30         357  
8 30     30   3377 use Module::Runtime qw( require_module );
  30         6482  
  30         1282  
9 30     30   6609 use Workflow::Exception qw( configuration_error );
  30         129  
  30         2230  
10 30     30   4442 use Syntax::Keyword::Try;
  30         19375  
  30         247  
11              
12             $Workflow::Action::InputField::VERSION = '2.09';
13              
14             my @PROPS = qw( name label description type requirement
15             source_class source_list class );
16             __PACKAGE__->mk_accessors(@PROPS);
17              
18             my %INCLUDED = ();
19              
20             sub new {
21 75     75 1 1702 my ( $class, $params ) = @_;
22 75         246 my $log = Log::Any->get_logger( category => $class );
23             $log->debug("Instantiating new field '$params->{name}'")
24 75 100       9839 if $params->{name};
25              
26 75         284 my $self = bless {}, $class;
27              
28             # Set all our parameters
29 75         148 foreach my $prop (@PROPS) {
30 600 100       3722 next unless ( $params->{$prop} );
31 236         3091 $self->$prop( $params->{$prop} );
32             }
33              
34             # ...ensure our name is defined
35 75 100       161 unless ( $self->name ) {
36             my $id_string = '['
37             . join(
38 0         0 '] [', map {"$_: $params->{$_}"}
39 1         15 sort keys %{$params}
  1         4  
40             ) . ']';
41 1         3 configuration_error "Field found without name: $id_string";
42             }
43              
44 74         892 my $name = $self->name;
45 74 100       691 unless ( $self->label ) {
46 9         79 $self->label($name);
47             }
48             my $requirement = ( defined $params->{is_required}
49 74 100 100     982 && $params->{is_required} eq 'yes' ) ? 'required' : 'optional';
50 74         226 $self->requirement($requirement);
51              
52             # ...ensure a class associated with the input source exists
53 74 100       726 if ( my $source_class = $self->source_class ) {
    100          
54 13         295 $log->debug("Possible values for '$name' from '$source_class'");
55 13 100       355 unless ( $INCLUDED{$source_class} ) {
56             try {
57             require_module( $source_class );
58             }
59 6         20 catch ($error) {
60             configuration_error "Failed to include source class ",
61             "'$source_class' used in field '$name': $error";
62             }
63 6         4849 $INCLUDED{$source_class}++;
64             }
65 13         83 $params->{values} = [ $source_class->get_possible_values($self) ];
66             } elsif ( $self->source_list ) {
67 13         266 $log->debug("Possible values for '$name' specified in config");
68 13         302 $params->{values} = [ split /\s*,\s*/, $self->source_list ];
69             }
70              
71 74   66     1844 my $values = $params->{values} || $params->{possible_values};
72 74 100       153 if ($values) {
73 26 50       85 my @add_values = ( ref $values eq 'ARRAY' ) ? @{$values} : ($values);
  26         78  
74 26         183 $log->debug( "Values to use as source for field '$name': ",
75             join ', ', @add_values );
76 26         129 $self->add_possible_values(@add_values);
77             }
78              
79             # Assign the default field type, subclasses may override...
80 74         243 $self->type('basic');
81              
82 74         829 $self->init($params);
83 74         361 return $self;
84             }
85              
86 74     74 1 93 sub init {return}
87              
88             sub is_required {
89 3     3 1 4 my ($self) = @_;
90 3 100       7 return ( $self->requirement eq 'required' ) ? 'yes' : 'no';
91             }
92              
93             sub is_optional {
94 3     3 1 6 my ($self) = @_;
95 3 100       7 return ( $self->requirement eq 'optional' ) ? 'yes' : 'no';
96             }
97              
98             sub get_possible_values {
99 2     2 1 3 my ($self) = @_;
100 2   100     8 $self->{_enumerated} ||= [];
101 2         2 return @{ $self->{_enumerated} };
  2         6  
102             }
103              
104             sub add_possible_values {
105 27     27 1 722 my ( $self, @values ) = @_;
106 27         58 foreach my $value (@values) {
107 132 100       337 my $this_value
108             = ( ref $value eq 'HASH' )
109             ? $value
110             : { label => $value, value => $value };
111 132         164 push @{ $self->{_enumerated} }, $this_value;
  132         287  
112             }
113 27         42 return @{ $self->{_enumerated} };
  27         89  
114             }
115              
116             1;
117              
118             __END__
119              
120             =pod
121              
122             =head1 NAME
123              
124             Workflow::InputField - Metadata about information required by an Action
125              
126             =head1 VERSION
127              
128             This documentation describes version 2.09 of this package
129              
130             =head1 SYNOPSIS
131              
132             # Declare the fields needed by your action in the configuration...
133              
134             action:
135             - name: CreateUser
136             field:
137             - name: username
138             is_required: yes
139             source_class: App::Field::ValidUsers
140             - name: email
141             is_required: yes
142             - name: office
143             source_list: Pittsburgh,Hong Kong,Moscow,Portland
144             ...
145              
146             =head1 DESCRIPTION
147              
148             A workflow Action can declare one or more input fields required to do
149             its job. Think of it as a way for the external world (your
150             application) to discover what information an action needs from it. The
151             application can request these fields from the workflow by action name
152             and present them to the user in whatever form appropriate for the
153             application. The sample command-line application shipped with this
154             distribution just cycles through them one at a time and presents a
155             query to the user for data entry.
156              
157             For instance, in the above declaration there are three fields,
158             'username', 'email' and 'office'. So your application might do:
159              
160             my @action_fields = $wf->get_action_fields( 'CreateUser' );
161             foreach my $field ( @action_fields ) {
162             print "Field ", $field->name, "\n",
163             $field->description, "\n",
164             "Required? ", $field->is_required, "\n";
165             my @enum = $field->get_possible_values;
166             if ( scalar @enum ) {
167             print "Possible values: \n";
168             foreach my $val ( @enum ) {
169             print " $val->{label} ($val->{value})\n";
170             }
171             }
172             print "Input? ";
173             my $response = <STDIN>;
174             chomp $response;
175             $wf->context->param( $field->name => $response );
176             }
177             $wf->execute_action( 'CreateUser' );
178              
179             =head1 METHODS
180              
181             =head2 Public Methods
182              
183             =head3 new( \%params )
184              
185             Typical constructor; will throw exception if 'name' is not defined or
186             if the property 'source_class' is defined but the class it specifies
187             is not available.
188              
189             You will usually not need to use or override this method unless you
190             derive your own input field class (see I<class> in L</"Properties">
191             below). For example, suppose you need to add extra properties to all
192             your fields like "index", "disabled", etc.
193              
194             In your actions definition YAML file, you can just add them and the
195             parser will pick them up. Pay close attention the custom InputField
196             "class" property.
197              
198             type: foo
199             action:
200             - name: Bar
201             class: your::action::class
202             field:
203             - name: id
204             index: '0'
205             type: integer
206             disabled: yes
207             is_required: yes
208             class: your::custom::inputfieldclass
209              
210             But you need to give them life by creating the accessors for these
211             extra properties. Just derive your custom fields class like so:
212              
213             package your::custom::inputfieldclass;
214              
215             use warnings;
216             use strict;
217              
218             use parent qw( Workflow::InputField );
219             use Workflow::Exception qw( workflow_error );
220              
221             # extra action class properties
222             my @EXTRA_PROPS = qw( index disabled );
223             __PACKAGE__->mk_accessors(@EXTRA_PROPS);
224              
225             sub new {
226             my ( $class, $params ) = @_;
227             my $self = $class->SUPER::new($params);
228             # set only our extra properties
229             foreach my $prop (@EXTRA_PROPS) {
230             next if ( $self->$prop );
231             $self->$prop( $params->{$prop} );
232             }
233             warn "INDEX IS NOW WORKING:".$self->index;
234             warn "AND SO IS DISABLED:".$self->disabled;
235             return $self;
236             }
237              
238             1;
239              
240              
241             =head3 is_required()
242              
243             Returns 'yes' if field is required, 'no' if optional.
244              
245             =head3 is_optional()
246              
247             Returns 'yes' if field is optional, 'no' if required.
248              
249             =head3 get_possible_values()
250              
251             Returns list of possible values for this field. Each possible value is
252             represented by a hashref with the keys 'label' and 'value' which makes
253             it easy to create dropdown lists in templates and the like.
254              
255             =head3 add_possible_values( @values )
256              
257             Adds possible values to be used for this field. Each item in
258             C<@values> may be a simple scalar or a hashref with the keys 'label'
259             and 'value'.
260              
261             =head3 init
262              
263             Init is a I<dummy> and just returns no special actions are taken
264              
265             =head2 Properties
266              
267             B<name> (required)
268              
269             Name of the field. This is what the action expects as the key in the
270             workflow context.
271              
272             B<label> (optional)
273              
274             Label of the field. If not set the value for C<name> is used.
275              
276             B<description> (optional)
277              
278             What does the field mean? This is not required for operation but it is
279             B<strongly> encouraged so your clients can create front ends to feed
280             you the information without much fuss.
281              
282             B<type> (optional)
283              
284             Field types are implementation dependant are they should be
285             intrinsically implemented by validators. In other words, you can use
286             any mnemonic value for your convinience like "integer", "text",
287             etc. but it won't affect anything unless you use a validator to
288             validate your action data. By default it is set to 'basic'.
289              
290             B<requirement> ('required'|'optional')
291              
292             If field is required, 'required', otherwise 'optional'.
293              
294             B<source_class> (optional)
295              
296             If set the field will call 'get_possible_values()' on the class when
297             the field is instantiated. This should return a list of either simple
298             scalars or a list of hashrefs with 'label' and 'value' keys.
299              
300             B<source_list> (optional)
301              
302             If set the field will use the specified comma-separated values as the
303             possible values for the field. The resulting list returned from
304             C<get_possible_values()> will have the same value for both the 'label'
305             and 'value' keys.
306              
307             B<class> (optional)
308              
309             You may specify a custom InputField class. It should C<use parent qw(
310             Workflow::Action );> and probably override the new() method which
311             should call SUPER::new($params). See L</"new( \%params )"> above for an
312             example.
313              
314             =head1 SEE ALSO
315              
316             =over
317              
318             =item * L<Workflow::Action>
319              
320             =back
321              
322             =head1 COPYRIGHT
323              
324             Copyright (c) 2003-2021 Chris Winters. All rights reserved.
325              
326             This library is free software; you can redistribute it and/or modify
327             it under the same terms as Perl itself.
328              
329             Please see the F<LICENSE>
330              
331             =head1 AUTHORS
332              
333             Please see L<Workflow>
334              
335             =cut