File Coverage

blib/lib/Form/Factory/Action.pm
Criterion Covered Total %
statement 153 192 79.6
branch 26 36 72.2
condition 0 10 0.0
subroutine 20 24 83.3
pod 15 15 100.0
total 214 277 77.2


line stmt bran cond sub pod time code
1             package Form::Factory::Action;
2             $Form::Factory::Action::VERSION = '0.022';
3 1     1   4 use Moose::Role;
  1         1  
  1         7  
4              
5 1     1   2126 use Carp ();
  1         1  
  1         13  
6 1     1   337 use Form::Factory::Feature::Functional;
  1         277  
  1         38  
7 1     1   516 use Form::Factory::Result::Gathered;
  1         263  
  1         36  
8 1     1   509 use Form::Factory::Result::Single;
  1         274  
  1         1783  
9              
10             #requires qw( run );
11              
12             # ABSTRACT: Role implemented by actions
13              
14              
15             has form_interface => (
16             is => 'ro',
17             does => 'Form::Factory::Interface',
18             required => 1,
19             );
20              
21              
22             has globals => (
23             is => 'ro',
24             isa => 'HashRef[Str]',
25             required => 1,
26             default => sub { {} },
27             );
28              
29              
30             has results => (
31             is => 'rw',
32             isa => 'Form::Factory::Result',
33             required => 1,
34             lazy => 1,
35             default => sub { Form::Factory::Result::Gathered->new },
36             handles => [ qw(
37             is_valid is_success is_failure
38             is_validated is_outcome_known
39              
40             all_messages regular_messages field_messages
41             info_messages warning_messages error_messages
42             regular_info_messages regular_warning_messages regular_error_messages
43             field_info_messages field_warning_messages field_error_messages
44              
45             content
46             ) ],
47             );
48              
49              
50             has result => (
51             is => 'rw',
52             isa => 'Form::Factory::Result::Single',
53             required => 1,
54             lazy => 1,
55             default => sub { Form::Factory::Result::Single->new },
56             handles => [ qw(
57             success failure
58              
59             add_message
60             info warning error
61             field_info field_warning field_error
62             ) ],
63             );
64              
65              
66             has features => (
67             is => 'ro',
68             isa => 'ArrayRef',
69             required => 1,
70             lazy => 1,
71             initializer => '_init_features',
72             default => sub { [] },
73             );
74              
75             sub _meta_features {
76 14     14   27 my $self = shift;
77 14         53 my $all_features = $self->meta->get_all_features;
78              
79 14         24 my @features;
80 14         50 for my $feature_name (keys %$all_features) {
81 7         14 my $feature_options = $all_features->{ $feature_name };
82 7         32 my $feature_class = Form::Factory->feature_class($feature_name);
83              
84 7         286 my $feature = $feature_class->new(
85             %$feature_options,
86             action => $self,
87             );
88 7         17 push @features, $feature;
89             }
90              
91 14         50 return \@features;
92             }
93              
94             sub _init_features {
95 14     14   728 my ($self, $features, $set, $attr) = @_;
96 14         48 push @$features, @{ $self->_meta_features };
  14         49  
97 14         53 $set->($features);
98             }
99              
100              
101             has controls => (
102             is => 'ro',
103             isa => 'HashRef',
104             required => 1,
105             lazy => 1,
106             builder => '_build_controls',
107             );
108              
109             sub _build_controls {
110 14     14   32 my $self = shift;
111 14         505 my $interface = $self->form_interface;
112 14         406 my $features = $self->features;
113              
114 14         24 my %controls;
115 14         61 my @meta_controls = $self->meta->get_controls;
116 14         167 for my $meta_control (@meta_controls) {
117              
118             # Construct any deferred options
119 75         72 my %options = %{ $meta_control->options };
  75         2840  
120 75         178 OPTION: for my $key (keys %options) {
121 27         40 my $value = $options{$key};
122              
123 27 100       118 next OPTION unless blessed $value;
124 1 50       11 next OPTION unless $value->isa('Form::Factory::Processor::DeferredValue');
125              
126 1         34 $options{$key} = $value->code->($self, $key);
127             }
128              
129             # Build the control constructor arguments
130 75         236 my $control_name = $meta_control->name;
131 75 100       2651 my %control_args = (
132             control => $meta_control->control,
133             options => {
134             name => $control_name,
135             action => $self,
136             ($meta_control->has_documentation
137             ? (documentation => $meta_control->documentation) : ()),
138             %options,
139             },
140             );
141              
142             # Let any BuildControl features modify the constructor arguments
143 75         1327 my %feature_classes;
144 75         2532 my $meta_features = $meta_control->features;
145 75         179 for my $feature_name (keys %$meta_features) {
146 46         894 my $feature_class = Form::Factory->control_feature_class($feature_name);
147 46         150 $feature_classes{$feature_name} = $feature_class;
148              
149 46 100       333 next unless $feature_class->does('Form::Factory::Feature::Role::BuildControl');
150              
151 1         257 $feature_class->build_control(
152             $meta_features->{$feature_name}, $self, $control_name, \%control_args
153             );
154             }
155              
156             # Construct the control
157 75         1799 my $control = $interface->new_control(
158             $control_args{control} => $control_args{options},
159             );
160              
161             # Construct and attach the features for the control
162 75         90 my @init_control_features;
163 75         200 for my $feature_name (keys %$meta_features) {
164 46         819 my $feature_class = $feature_classes{$feature_name};
165              
166 46         1969 my $feature = $feature_class->new(
167 46         46 %{ $meta_features->{$feature_name} },
168             action => $self,
169             control => $control,
170             );
171 46         1940 push @$features, $feature;
172 46         51 push @{ $control->features }, $feature;
  46         1490  
173              
174 46 100       129 push @init_control_features, $feature
175             if $feature->does('Form::Factory::Feature::Role::InitializeControl');
176             }
177              
178             # Have InitializeControl features work on the constructed control
179 75         1892 for my $feature (@init_control_features) {
180 8         36 $feature->initialize_control;
181             }
182              
183             # Add the control to the list
184 75         566 $controls{ $meta_control->name } = $control;
185             }
186              
187 14         503 return \%controls;
188             }
189              
190              
191             sub stash {
192 0     0 1 0 my ($self, $moniker) = @_;
193              
194 0         0 my $controls = $self->controls;
195 0         0 my %controls;
196 0         0 for my $control_name (keys %$controls) {
197 0         0 my $control = $controls->{ $control_name };
198 0         0 $controls{$control_name}{value} = $control->value;
199             }
200              
201 0         0 my %stash = (
202             globals => $self->globals,
203             controls => \%controls,
204             results => $self->results,
205             result => $self->result,
206             );
207              
208 0         0 $self->form_interface->stash($moniker => \%stash);
209             }
210              
211              
212             sub unstash {
213 0     0 1 0 my ($self, $moniker) = @_;
214              
215 0         0 my $stash = $self->form_interface->unstash($moniker);
216 0 0       0 return unless defined $stash;
217              
218 0   0     0 my $globals = $stash->{globals} || {};
219 0         0 for my $key (keys %$globals) {
220 0         0 $self->globals->{$key} = $globals->{$key};
221             }
222              
223 0         0 my $controls = $self->controls;
224 0   0     0 my $controls_state = $stash->{controls} || {};
225 0         0 for my $control_name (keys %$controls) {
226 0         0 my $state = $controls_state->{$control_name};
227 0         0 my $control = $controls->{$control_name};
228 0         0 eval { $control->value($state->{value}) };
  0         0  
229             }
230              
231 0   0     0 $self->results($stash->{results} || Form::Factory::Result::Gathered->new);
232 0   0     0 $self->result($stash->{result} || Form::Factory::Result::Single->new);
233             }
234              
235              
236             sub clear {
237 21     21 1 45674 my ($self) = @_;
238              
239 21         48 %{ $self->globals } = ();
  21         646  
240              
241 21         612 my $controls = $self->controls;
242 21         137 for my $control_name (keys %$controls) {
243 141         138 my $control = $controls->{ $control_name };
244 141         199 delete $control->{value};
245             }
246              
247 21         648 $self->results->clear_all;
248 21         624 $self->result(Form::Factory::Result::Single->new);
249             }
250              
251              
252             sub render {
253 1     1 1 1409 my $self = shift;
254 1         2 my %params = @_;
255 0         0 my @names = defined $params{controls} ? @{ delete $params{controls} }
  8         38  
256 1 50       12 : map { $_->name }
257             $self->meta->get_controls
258             ;
259              
260 1         34 $params{results} = $self->results;
261              
262 1         26 my $controls = $self->controls;
263 1         36 $self->form_interface->render_control($controls->{$_}, %params) for @names;
264 1         39 return;
265             }
266              
267              
268             sub setup_and_render {
269 0     0 1 0 my ($self, %options) = @_;
270              
271 0         0 $self->unstash($options{moniker});
272 0 0       0 my %globals = %{ $options{globals} || {} };
  0         0  
273 0         0 for my $key (keys %globals) {
274 0         0 $self->globals->{$key} = $globals{$key};
275             }
276 0         0 $self->render(%options);
277 0         0 $self->results->clear_all;
278 0         0 $self->stash($options{moniker});
279              
280 0         0 return;
281             }
282              
283              
284             sub render_control {
285 1     1 1 4494 my ($self, $name, $options, %params) = @_;
286              
287 1 50       2 my %options = %{ $options || {} };
  1         8  
288 1         4 $options{action} = $self;
289              
290 1         29 $params{results} = $self->results;
291              
292 1         28 my $interface = $self->form_interface;
293 1         9 my $control = $interface->new_control($name => \%options);
294              
295 1         9 $interface->render_control($control, %params);
296              
297 1         7 return $control;
298             }
299              
300              
301             sub consume {
302 36     36 1 46840 my $self = shift;
303 36         120 my %params = @_;
304 15         58 my @names = defined $params{controls} ? @{ delete $params{controls} }
  125         349  
305 36 100       224 : map { $_->name }
306             $self->meta->get_controls
307             ;
308              
309 36         1257 my $controls = $self->controls;
310 36         1111 $self->form_interface->consume_control($controls->{$_}, %params) for @names;
311             }
312              
313              
314             sub consume_control {
315 2     2 1 4309 my ($self, $name, $options, %params) = @_;
316              
317 2 50       3 my %options = %{ $options || {} };
  2         11  
318 2         5 $options{action} = $self;
319              
320 2         56 $params{results} = $self->results;
321              
322 2         53 my $interface = $self->form_interface;
323 2         11 my $control = $interface->new_control($name => \%options);
324              
325 2         15 $interface->consume_control($control, %params);
326              
327 2         8 return $control;
328             }
329              
330              
331             {
332             sub _run_features {
333 82     82   107 my $self = shift;
334 82         129 my $method = shift;
335 82         153 my %params = @_;
336 82         2823 my $features = $self->features;
337              
338             # Only run the requested control-specific features
339 82 100       245 if (defined $params{controls}) {
340 30         38 my %names = map { $_ => 1 } @{ $params{controls} };
  30         92  
  30         61  
341              
342 30         51 for my $feature (@$features) {
343 210 50       3436 next unless $feature->does('Form::Factory::Feature::Role::Control');
344 210 100       6382 next unless $feature->does(
345             Form::Factory::_class_name_from_name('Feature::Role', $method)
346             );
347 90 100       6007 next unless $names{ $feature->control->name };
348              
349 15         79 $feature->$method;
350             }
351             }
352              
353             # Run all features now
354             else {
355 52         170 for my $feature (@$features) {
356 104 100       3211 next unless $feature->does(
357             Form::Factory::_class_name_from_name('Feature::Role', $method)
358             );
359              
360 51         2092 $feature->$method;
361             }
362             }
363             }
364             }
365              
366             sub clean {
367 36     36 1 195 my $self = shift;
368 36         155 $self->_run_features(clean => @_);
369             }
370              
371              
372             sub check {
373 36     36 1 1656 my $self = shift;
374 36         98 $self->_run_features(check => @_);
375              
376 36         292 $self->gather_results;
377             }
378              
379              
380             sub process {
381 7     7 1 12 my $self = shift;
382 7 100       39 return unless $self->is_valid;
383              
384 5         30 $self->set_attributes_from_controls;
385              
386 5         1204 $self->_run_features('pre_process');
387 5         83 $self->run;
388 5         13 $self->_run_features('post_process');
389              
390 5         77 $self->gather_results;
391              
392 5         29 my @errors = $self->error_messages;
393 5         155 $self->result->is_success(@errors == 0);
394             }
395              
396              
397             sub consume_and_clean_and_check_and_process {
398 7     7 1 13632 my $self = shift;
399 7         34 $self->consume(@_);
400 7         37 $self->clean;
401 7         116 $self->check;
402 7         35 $self->process;
403             }
404              
405              
406             sub set_attribute_from_control {
407 0     0 1 0 my ($self, $name) = @_;
408 0         0 my $meta = $self->meta;
409              
410 0         0 my $control = $self->control->{$name};
411 0         0 my $attribute = $meta->find_attribute_by_name($name);
412 0         0 $control->set_attribute_value($self, $attribute);
413             }
414              
415              
416             sub set_attributes_from_controls {
417 5     5 1 6 my $self = shift;
418 5         28 my $meta = $self->meta;
419              
420 5         252 my $controls = $self->controls;
421 5         29 while (my ($name, $control) = each %$controls) {
422 21         5671 my $attribute = $meta->find_attribute_by_name($name);
423 21 50       829 Carp::croak("attribute for control $name not found on $self")
424             unless defined $attribute;
425 21         79 $control->set_attribute_value($self, $attribute);
426             }
427             }
428              
429              
430             sub gather_results {
431 41     41 1 74 my $self = shift;
432 41         1323 my $controls = $self->controls;
433 157         5607 $self->results->gather_results(
434             $self->result,
435 41         1335 map { $_->result } @{ $self->features }
  41         1352  
436             );
437             }
438              
439              
440             1;
441              
442             __END__
443              
444             =pod
445              
446             =encoding UTF-8
447              
448             =head1 NAME
449              
450             Form::Factory::Action - Role implemented by actions
451              
452             =head1 VERSION
453              
454             version 0.022
455              
456             =head1 DESCRIPTION
457              
458             This is the role implemented by all form actions. Rather than doing so directly, you should use L<Form::Factory::Processor> as demonstrated in the L</SYNOPSIS>.
459              
460             =head2 SYNOPSIS
461              
462             package MyApp::Action::Foo;
463             use Form::Factory::Processor;
464              
465             has_control bar => (
466             type => 'text',
467             );
468              
469             sub run {
470             my $self = shift;
471              
472             # Do something...
473             }
474              
475             =head1 ATTRIBUTES
476              
477             All form actions have the following attributes.
478              
479             =head2 form_interface
480              
481             This is the L<Form::Factory::Interface> that constructed this action. If you need to get at the implementation directly for some reason, here it is. This is mostly used by the action itself when calling the L</render> and L</consume> methods.
482              
483             =head2 globals
484              
485             This is a hash of extra parameters to keep with the form. Normally, these are saved with a call to L</stash> and recovered with a call to L</unstash>.
486              
487             =head2 results
488              
489             This is a L<Form::Factory::Result::Gathered> object that tracks the general success, failure, messages, and output from the execution of this action.
490              
491             Actions delegate a number of methods to this object. See L</RESULTS>.
492              
493             =head2 result
494              
495             This is a L<Form::Factory::Result::Single> used to record general outcome.
496              
497             Actions delegate a number of methods to this object. See L</RESULTS>.
498              
499             =head2 features
500              
501             This is a list of L<Form::Factory::Feature> objects used to modify the object. This will always contain the features that are attached to the class itself. Additional features may be added here.
502              
503             =head2 controls
504              
505             This is a hash of controls attached to this action. For every C<has_control> line in the action class, there should be a matching control in this hash.
506              
507             =head1 METHODS
508              
509             =head2 stash
510              
511             $action->stash($moniker);
512              
513             Given a C<$moniker> (a key under which to store the information related to this form), this will stash the form's stashable information under that name using the L<Form::Factory::Stasher> associated with the L</form_interface>.
514              
515             The globals, values of controls, and the results are stashed. This allows those values to be recovered across requests or between process calls or whatever the implementation requires.
516              
517             =head2 unstash
518              
519             $action->unstash($moniker);
520              
521             Given a C<$moniker> previously named in a call to L</stash>, it restores the previously stashed state. This is a no-op if nothing is stashed under this moniker.
522              
523             =head2 clear
524              
525             This method clears the stashable state of the action.
526              
527             =head2 render
528              
529             $action->render(%options);
530              
531             Renders the form using the associated L</form_interface>. You may specify the following options:
532              
533             =over
534              
535             =item controls
536              
537             This is a list of control names to render. If not given, all controls will be rendered.
538              
539             =back
540              
541             Any other options will be passed on to the L<Form::Factory::Interface/render_control> method.
542              
543             =head2 setup_and_render
544              
545             $self->setup_and_render(%options);
546              
547             This performs the most common steps to prepare for a render and render:
548              
549             =over
550              
551             =item 1
552              
553             Unstashes from the given C<moniker>.
554              
555             =item 2
556              
557             Adds the given C<globals> to the globals.
558              
559             =item 3
560              
561             Renders the action.
562              
563             =item 4
564              
565             Clears the results.
566              
567             =item 5
568              
569             Stashes what we've done back into the given C<moniker>.
570              
571             =back
572              
573             =head2 render_control
574              
575             my $control = $action->render_control($name, \%options);
576              
577             Creates and renders a one time control. This is mostly useful for attaching buttons to a form. The control is not added to the list of controls on the action and will not be processed later.
578              
579             This method returns the control object that was just rendered.
580              
581             =head2 consume
582              
583             $action->consume(%options);
584              
585             This consumes any input from user and places it into the controls of the form. You may pass the following options:
586              
587             =over
588              
589             =item controls
590              
591             This is a list of names of controls to consume. Any not listed here will not be consumed. If this option is missing, all control values are consumed.
592              
593             =back
594              
595             Any additional options will be passed to the L<Form::Factory::Interface/consume_control> method call.
596              
597             =head2 consume_control
598              
599             my $control = $action->consume_control($name, \%options, %params);
600              
601             Consumes the value of a one time control. This is useful for testing to see if a form submitted using a one-time control has been submitted or not.
602              
603             This method returns the control object that was consumed.
604              
605             =head2 clean
606              
607             $action->clean(%options);
608              
609             Takes the values consumed from the user and cleans them up. For example, if you allow users to type in a phone number, this can be used to clear out any unwanted or incorrect punctuation and format the phone number properly.
610              
611             This method runs through all the requested L<Form::Factory::Feature> objects in L</features> and runs the L<Form::Factory::Feature/clean> method for each.
612              
613             The following options are used:
614              
615             =over
616              
617             =item controls
618              
619             This is the list of controls to clean. If not given, all features will be run. If given, only the control-features (those implementing L<Form::Factory::Feature::Role::Control> attached to the named controls) will be run. Any form-features or unlisted control-features will not be run.
620              
621             =back
622              
623             =head2 check
624              
625             $action->check(%options);
626              
627             The C<check> method is responsible for verifying the correctness of the input. It assumes that L</clean> has already run.
628              
629             It runs the L<Form::Factory::Feature/check> method of all the selected L</features> attached to the action. It also sets the C<is_valid> flag to true if no errors have been recored yet or to false if they have.
630              
631             The following options are used:
632              
633             =over
634              
635             =item controls
636              
637             This is the list of controls to check. If not given, all features will be run. If given, only the control-features (those implementing L<Form::Factory::Feature::Role::Control> attached to the named controls) will be run. Any form-features or unlisted control-features will not be run.
638              
639             =back
640              
641             =head2 process
642              
643             $action->process;
644              
645             This does nothing if the action did not validate.
646              
647             In the case the action is valid, this will use L</set_attributes_from_controls> to copy the control values to the action attributes, run the L<Form::Factory::Feature/pre_process> methods for all form-features and control-features, call the L</run> method, run the L<Form::Factory::Feature/post_process> methods for all form-features and control-features, and set the C<is_success> flag to true if no errors are recorded or false if there are.
648              
649             =head2 consume_and_clean_and_check_and_process
650              
651             This is a shortcut for taking all the usual workflow actions in a single call:
652              
653             $action->consume(@_);
654             $action->clean;
655             $action->check
656             $action->process;
657              
658             =head1 ROLE METHODS
659              
660             This method must be implemented by any action implementation.
661              
662             =head2 run
663              
664             This is the method that actually does the work. It takes no arguments and is expected to return nothing. You should draw your input from the action attributes (not the controls) and report your results to L</result>.
665              
666             =head1 HELPER METHODS
667              
668             These methods are primarily intended for internal use.
669              
670             =head2 set_attribute_from_control
671              
672             Given the name of a control, this will copy the current value in the control to the attribute.
673              
674             =head2 set_attributes_from_controls
675              
676             This method is used by L</process> to copy the values out of the controls into the form action attributes. This assumes that such a copy will work because the L</clean> and L</check> phases should have already run and passed without error.
677              
678             =head2 gather_results
679              
680             Gathers results for all the associated L</controls> and L</result> into L</results>. This is called just before deciding if the action has validated correctly or if the action has succeeded.
681              
682             =head1 RESULTS
683              
684             Actions are tied closely to L<Form::Factory::Result>s. As such, a number of methods are delegated to result classes.
685              
686             The following methods are delegated to L</results> in L<Form::Factory::Result::Gathered>.
687              
688             =over
689              
690             =item is_valid
691              
692             =item is_success
693              
694             =item is_failure
695              
696             =item is_validated
697              
698             =item is_outcome_known
699              
700             =item all_messages
701              
702             =item regular_messages
703              
704             =item field_messages
705              
706             =item info_messages
707              
708             =item warning_messages
709              
710             =item error_messages
711              
712             =item regular_info_messages
713              
714             =item regular_warning_messages
715              
716             =item regular_error_messages
717              
718             =item field_info_messages
719              
720             =item field_warning_messages
721              
722             =item field_error_messages
723              
724             =item content
725              
726             =back
727              
728             These methods are delegated to L<result> in L<Form::Factory::Result::Single>.
729              
730             =over
731              
732             =item success
733              
734             =item failure
735              
736             =item add_message
737              
738             =item info
739              
740             =item warning
741              
742             =item error
743              
744             =item field_info
745              
746             =item field_warning
747              
748             =item field_error
749              
750             =back
751              
752             =head1 WORKFLOW
753              
754             =head2 TYPICAL CASE
755              
756             The action workflow typically goes like this. There are two phases.
757              
758             =head3 PHASE 1: SHOW THE FORM
759              
760             Phase 1 is responsible for showing the form to the user. This phase might be skipped altogether in situations where automatic processing is taking place where the robot doing the work already knows what inputs are expected for the action. However, typically, you do something like this:
761              
762             my $action = $interface->new_action('Foo');
763             $action->unstash('foo');
764             $action->render;
765             $action->render_control(button => {
766             name => 'submit',
767             label => 'Do It',
768             });
769             $action->stash('foo');
770              
771             This tells the interface that you want to prepare a form object for class "Foo."
772              
773             The call to L</unstash> then pulls in any state saved from the user's prior entry. This will cause any errors that occurred on a previous validation or process execution to show up (assuming that your interface does that work for you). This also means that any previously stashed values entered should reappear in the form so that a failure to save or something won't cause the field information to be lost forever.
774              
775             The call to L</render> causes all of the controls of the form to be rendered for input.
776              
777             The call to L</render_control> causes a button to appear in the form.
778              
779             The call to L</stash> returns the form's stashable information back to the stash, since L</unstash> typically removes it.
780              
781             =head3 PHASE 2: PROCESSING THE INPUT
782              
783             Once the user has submitted the form, you will want to process the input and perform the action. This typically looks like this:
784              
785             my $action = $interface->new_action('Foo');
786             $action->unstash('foo');
787             $action->consume_and_clean_and_check_and_process( request => $q->Vars );
788              
789             if ($action->is_valid and $action->is_success) {
790             # Go on to the next thing
791             }
792             else {
793             $action->stash('foo');
794             # Go render the form again and show the errors
795             }
796              
797             We request an instance of the form again and then call L</unstash> to recover any stashed setup. We then call the L</consume_and_clean_and_check_and_process> method, which will consume all the input. Here we use something that looks like a L<CGI> request for the source of input, but it should be whatever is appropriate to your environment and the L<Form::Factory::Interface> implementation used.
798              
799             At the end, we check to see if the action checked out and then that the L</run> method ran without problems. If so, we can show the success page or the record view or whatever is appropriate after filling this form.
800              
801             If there are errors, we should perform the rendering action for L</PHASE 1: SHOW THE FORM> again after doing a L</stash> to make sure the information is ready to be recovered.
802              
803             =head2 VARATIONS
804              
805             =head3 PER-CONTROL CLEANING AND CHECKING
806              
807             Ajax or GUIs will generally want to give their feedback as early as possible. As such, whenever the user finishes entering a value or the application thinks that validation is needed, the app might perform:
808              
809             $action->check( controls => [ qw( some_control ) ] );
810             $action->clean( controls => [ qw( some_control ) ] );
811              
812             unless ($action->is_valid) {
813             # Report $action->results->error_messages
814             }
815              
816             You will still run the steps above, but can do a check and clean on a subset of controls when you need to do so to give the user early feedback.
817              
818             =head1 AUTHOR
819              
820             Andrew Sterling Hanenkamp <hanenkamp@cpan.org>
821              
822             =head1 COPYRIGHT AND LICENSE
823              
824             This software is copyright (c) 2015 by Qubling Software LLC.
825              
826             This is free software; you can redistribute it and/or modify it under
827             the same terms as the Perl 5 programming language system itself.
828              
829             =cut