File Coverage

blib/lib/HTML/FormFu/Role/Constraint/Others.pm
Criterion Covered Total %
statement 76 78 97.4
branch 39 54 72.2
condition 9 9 100.0
subroutine 7 7 100.0
pod 0 2 0.0
total 131 150 87.3


line stmt bran cond sub pod time code
1 26     26   16352 use strict;
  26         67  
  26         1547  
2              
3             package HTML::FormFu::Role::Constraint::Others;
4             $HTML::FormFu::Role::Constraint::Others::VERSION = '2.07';
5             # ABSTRACT: Base class for constraints needing others() method
6              
7 26     26   170 use Moose::Role;
  26         57  
  26         279  
8              
9 26         1458 use HTML::FormFu::Util qw(
10             DEBUG_CONSTRAINTS_OTHERS
11             debug
12 26     26   136278 );
  26         67  
13 26     26   177 use Clone ();
  26         58  
  26         863  
14 26     26   162 use List::Util 1.33 qw( any none );
  26         852  
  26         28533  
15              
16             has others => ( is => 'rw', traits => ['Chained'] );
17             has other_siblings => ( is => 'rw', traits => ['Chained'] );
18             has attach_errors_to => ( is => 'rw', traits => ['Chained'] );
19             has attach_errors_to_base => ( is => 'rw', traits => ['Chained'] );
20             has attach_errors_to_others => ( is => 'rw', traits => ['Chained'] );
21              
22             sub pre_process {
23 87     87 0 228 my ($self) = @_;
24              
25 87 100       3008 if ( $self->other_siblings ) {
26              
27 11         54 my $field = $self->field;
28 11         25 my $block = $field;
29              
30             # find the nearest parent that contains any field other than
31             # the one this constraint is attached to
32 11         33 while ( defined( my $parent = $block->parent ) ) {
33 11         29 $block = $parent;
34              
35 11 50       18 last if grep { $_ ne $field } @{ $block->get_fields };
  36         152  
  11         37  
36             }
37              
38 11         36 my @names;
39              
40 11         26 for my $sibling ( @{ $block->get_fields } ) {
  11         52  
41 36 100       119 next if $sibling == $field;
42              
43 25         87 push @names, $sibling->nested_name;
44             }
45              
46 11         391 $self->others( [@names] );
47             }
48             }
49              
50             after repeatable_repeat => sub {
51             my ( $self, $repeatable, $new_block ) = @_;
52              
53             my $block_fields = $new_block->get_fields;
54              
55             # rename any 'others' fields
56             {
57             my $others = $self->others;
58             if ( !ref $others ) {
59             $others = [$others];
60             }
61             my @new_others;
62              
63             for my $name (@$others) {
64             my $field = $repeatable->get_field_with_original_name( $name,
65             $block_fields );
66              
67             if ( defined $field ) {
68             DEBUG_CONSTRAINTS_OTHERS && debug(
69             sprintf
70             "Repeatable renaming constraint 'other' '%s' to '%s'",
71             $name, $field->nested_name,
72             );
73              
74             push @new_others, $field->nested_name;
75             }
76             else {
77             push @new_others, $name;
78             }
79             }
80              
81             $self->others( \@new_others );
82             }
83              
84             # rename any 'attach_errors_to' fields
85             if ( my $others = $self->attach_errors_to ) {
86             my @new_others;
87              
88             for my $name (@$others) {
89             my $field = $repeatable->get_field_with_original_name( $name,
90             $block_fields );
91              
92             if ( defined $field ) {
93             DEBUG_CONSTRAINTS_OTHERS && debug(
94             sprintf
95             "Repeatable renaming constraint 'attach_errors_to' '%s' to '%s'",
96             $name, $field->nested_name,
97             );
98              
99             push @new_others, $field->nested_name;
100             }
101             else {
102             push @new_others, $name;
103             }
104             }
105              
106             $self->attach_errors_to( \@new_others );
107             }
108             };
109              
110             sub mk_errors {
111 101     101 0 284 my ( $self, $args ) = @_;
112              
113 101         254 my $pass = $args->{pass};
114 101 100       309 my @failed = $args->{failed} ? @{ $args->{failed} } : ();
  84         233  
115 101 100       294 my @names = $args->{names} ? @{ $args->{names} } : ();
  84         278  
116              
117 101   100     3343 my $force = $self->force_errors || $self->parent->force_errors;
118              
119 101 50       387 DEBUG_CONSTRAINTS_OTHERS && debug( PASS => $pass );
120 101 50       485 DEBUG_CONSTRAINTS_OTHERS && debug( NAMES => \@names );
121 101 50       472 DEBUG_CONSTRAINTS_OTHERS && debug( 'FAILED NAMES' => \@failed );
122 101 50       270 DEBUG_CONSTRAINTS_OTHERS && debug( FORCE => $force );
123              
124 101 100 100     443 if ( $pass && !$force ) {
125 56 50       161 DEBUG_CONSTRAINTS_OTHERS
126             && debug(
127             'constraint passed, or force_errors is false - returning no errors'
128             );
129 56         350 return;
130             }
131              
132 45         139 my @can_error;
133             my @has_error;
134              
135 45 100       1665 if ( $self->attach_errors_to ) {
    100          
    100          
136 1         3 push @can_error, @{ $self->attach_errors_to };
  1         32  
137              
138 1 50       4 if ( !$pass ) {
139 1         3 push @has_error, @{ $self->attach_errors_to };
  1         31  
140             }
141             }
142             elsif ( $self->attach_errors_to_base ) {
143 11         52 push @can_error, $self->nested_name;
144              
145 11 100       44 if ( !$pass ) {
146 9         34 push @has_error, $self->nested_name;
147             }
148             }
149             elsif ( $self->attach_errors_to_others ) {
150             push @can_error, ref $self->others
151 2 50       57 ? @{ $self->others }
  0         0  
152             : $self->others;
153              
154 2 50       18 if ( !$pass ) {
155             push @has_error, ref $self->others
156 2 50       63 ? @{ $self->others }
  0         0  
157             : $self->others;
158             }
159             }
160             else {
161 31         111 push @can_error, @names;
162              
163 31 100       103 if ( !$pass ) {
164 21         61 push @has_error, @failed;
165             }
166             }
167              
168 45 50       164 DEBUG_CONSTRAINTS_OTHERS && debug( 'CAN ERROR' => \@can_error );
169 45 50       151 DEBUG_CONSTRAINTS_OTHERS && debug( 'HAS ERROR' => \@has_error );
170              
171 45         112 my @errors;
172              
173 45         127 for my $name (@can_error) {
174              
175 98 100 100     342 next unless $force || grep { $name eq $_ } @has_error;
  73         341  
176              
177 68 50       211 DEBUG_CONSTRAINTS_OTHERS && debug( 'CREATING ERROR' => $name );
178              
179 68 50       403 my $field = $self->form->get_field( { nested_name => $name } )
180             or die "others() field not found: '$name'";
181              
182 68         423 my $error = $self->mk_error;
183              
184 68         420 $error->parent($field);
185              
186 68 100       355 if ( !grep { $name eq $_ } @has_error ) {
  54         254  
187 31 50       90 DEBUG_CONSTRAINTS_OTHERS
188             && debug("setting '$name' error forced(1)");
189              
190 31         1011 $error->forced(1);
191             }
192              
193 68         244 push @errors, $error;
194             }
195              
196 45         385 return @errors;
197             }
198              
199             around clone => sub {
200             my ( $orig, $self, $args ) = @_;
201              
202             my $clone = $self->$orig($args);
203              
204             if ( ref $self->others ) {
205             $clone->others( Clone::clone( $self->others ) );
206             }
207              
208             return $clone;
209             };
210              
211             1;
212              
213             __END__
214              
215             =pod
216              
217             =encoding UTF-8
218              
219             =head1 NAME
220              
221             HTML::FormFu::Role::Constraint::Others - Base class for constraints needing others() method
222              
223             =head1 VERSION
224              
225             version 2.07
226              
227             =head1 METHODS
228              
229             =head2 others
230              
231             Arguments: \@nested_names
232              
233             =head2 other_siblings
234              
235             Arguments: $bool
236              
237             If true, the L</others> list will be automatically generated from the
238             C<nested_name> of all fields which are considered siblings of the field the
239             constraint is attached to.
240              
241             Sibling are found by searching up through the field's parental hierarchy for
242             the first block containing any other field. All fields attached at any depth
243             to this block are considered siblings.
244              
245             =head2 attach_errors_to_base
246              
247             If true, any error will cause the error message to be associated with the
248             field the constraint is attached to.
249              
250             Can be use in conjunction with L</attach_errors_to_others>.
251              
252             Is ignored if L</attach_errors_to> is set.
253              
254             =head2 attach_errors_to_others
255              
256             If true, any error will cause the error message to be associated with every
257             field named in L</others>.
258              
259             Can be use in conjunction with L</attach_errors_to_base>.
260              
261             Is ignored if L</attach_errors_to> is set.
262              
263             =head2 attach_errors_to
264              
265             Arguments: \@field_names
266              
267             Any error will cause the error message to be associated with every field
268             named in L</attach_errors_to>.
269              
270             Overrides L</attach_errors_to_base> and L</attach_errors_to_others>.
271              
272             =head1 SEE ALSO
273              
274             Is a sub-class of, and inherits methods from L<HTML::FormFu::Constraint>
275              
276             L<HTML::FormFu>
277              
278             =head1 AUTHOR
279              
280             Carl Franks C<cfranks@cpan.org>
281              
282             =head1 LICENSE
283              
284             This library is free software, you can redistribute it and/or modify it under
285             the same terms as Perl itself.
286              
287             =head1 AUTHOR
288              
289             Carl Franks <cpan@fireartist.com>
290              
291             =head1 COPYRIGHT AND LICENSE
292              
293             This software is copyright (c) 2018 by Carl Franks.
294              
295             This is free software; you can redistribute it and/or modify it under
296             the same terms as the Perl 5 programming language system itself.
297              
298             =cut