File Coverage

lib/Workflow/Action/InputField.pm
Criterion Covered Total %
statement 69 71 97.1
branch 26 28 92.8
condition 7 8 87.5
subroutine 12 12 100.0
pod 6 6 100.0
total 120 125 96.0


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