File Coverage

blib/lib/HTML/FormFu/Role/Constraint/Others.pm
Criterion Covered Total %
statement 79 81 97.5
branch 39 54 72.2
condition 9 9 100.0
subroutine 8 8 100.0
pod 0 2 0.0
total 135 154 87.6


line stmt bran cond sub pod time code
1             package HTML::FormFu::Role::Constraint::Others;
2              
3 26     26   13227 use strict;
  26         42  
  26         1067  
4             our $VERSION = '2.05'; # VERSION
5              
6 26     26   105 use Moose::Role;
  26         34  
  26         204  
7 26     26   90856 use MooseX::Attribute::FormFuChained;
  26         49  
  26         694  
8              
9 26         1234 use HTML::FormFu::Util qw(
10             DEBUG_CONSTRAINTS_OTHERS
11             debug
12 26     26   93 );
  26         37  
13 26     26   116 use Clone ();
  26         43  
  26         647  
14 26     26   95 use List::Util 1.33 qw( any none );
  26         688  
  26         22157  
15              
16             has others => ( is => 'rw', traits => ['FormFuChained'] );
17             has other_siblings => ( is => 'rw', traits => ['FormFuChained'] );
18             has attach_errors_to => ( is => 'rw', traits => ['FormFuChained'] );
19             has attach_errors_to_base => ( is => 'rw', traits => ['FormFuChained'] );
20             has attach_errors_to_others => ( is => 'rw', traits => ['FormFuChained'] );
21              
22             sub pre_process {
23 87     87 0 114 my ($self) = @_;
24              
25 87 100       2498 if ( $self->other_siblings ) {
26              
27 11         59 my $field = $self->field;
28 11         19 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         35 while ( defined( my $parent = $block->parent ) ) {
33 11         21 $block = $parent;
34              
35 11 50       20 last if grep { $_ ne $field } @{ $block->get_fields };
  36         191  
  11         38  
36             }
37              
38 11         25 my @names;
39              
40 11         18 for my $sibling ( @{ $block->get_fields } ) {
  11         56  
41 36 100       108 next if $sibling == $field;
42              
43 25         68 push @names, $sibling->nested_name;
44             }
45              
46 11         328 $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 127 my ( $self, $args ) = @_;
112              
113 101         154 my $pass = $args->{pass};
114 101 100       497 my @failed = $args->{failed} ? @{ $args->{failed} } : ();
  84         198  
115 101 100       187 my @names = $args->{names} ? @{ $args->{names} } : ();
  84         195  
116              
117 101   100     2762 my $force = $self->force_errors || $self->parent->force_errors;
118              
119 101 50       280 DEBUG_CONSTRAINTS_OTHERS && debug( PASS => $pass );
120 101 50       189 DEBUG_CONSTRAINTS_OTHERS && debug( NAMES => \@names );
121 101 50       741 DEBUG_CONSTRAINTS_OTHERS && debug( 'FAILED NAMES' => \@failed );
122 101 50       177 DEBUG_CONSTRAINTS_OTHERS && debug( FORCE => $force );
123              
124 101 100 100     357 if ( $pass && !$force ) {
125 56 50       121 DEBUG_CONSTRAINTS_OTHERS
126             && debug(
127             'constraint passed, or force_errors is false - returning no errors'
128             );
129 56         275 return;
130             }
131              
132 45         64 my @can_error;
133             my @has_error;
134              
135 45 100       1302 if ( $self->attach_errors_to ) {
    100          
    100          
136 1         1 push @can_error, @{ $self->attach_errors_to };
  1         26  
137              
138 1 50       3 if ( !$pass ) {
139 1         1 push @has_error, @{ $self->attach_errors_to };
  1         26  
140             }
141             }
142             elsif ( $self->attach_errors_to_base ) {
143 11         31 push @can_error, $self->nested_name;
144              
145 11 100       28 if ( !$pass ) {
146 9         31 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       49 ? @{ $self->others }
  0         0  
152             : $self->others;
153              
154 2 50       5 if ( !$pass ) {
155             push @has_error, ref $self->others
156 2 50       49 ? @{ $self->others }
  0         0  
157             : $self->others;
158             }
159             }
160             else {
161 31         66 push @can_error, @names;
162              
163 31 100       73 if ( !$pass ) {
164 21         39 push @has_error, @failed;
165             }
166             }
167              
168 45 50       116 DEBUG_CONSTRAINTS_OTHERS && debug( 'CAN ERROR' => \@can_error );
169 45 50       109 DEBUG_CONSTRAINTS_OTHERS && debug( 'HAS ERROR' => \@has_error );
170              
171 45         84 my @errors;
172              
173 45         85 for my $name (@can_error) {
174              
175 98 100 100     526 next unless $force || grep { $name eq $_ } @has_error;
  73         260  
176              
177 68 50       175 DEBUG_CONSTRAINTS_OTHERS && debug( 'CREATING ERROR' => $name );
178              
179 68 50       327 my $field = $self->form->get_field( { nested_name => $name } )
180             or die "others() field not found: '$name'";
181              
182 68         331 my $error = $self->mk_error;
183              
184 68         239 $error->parent($field);
185              
186 68 100       114 if ( !grep { $name eq $_ } @has_error ) {
  54         196  
187 31 50       57 DEBUG_CONSTRAINTS_OTHERS && debug("setting '$name' error forced(1)");
188              
189 31         775 $error->forced(1);
190             }
191              
192 68         122 push @errors, $error;
193             }
194              
195 45         295 return @errors;
196             }
197              
198             around clone => sub {
199             my ( $orig, $self, $args ) = @_;
200              
201             my $clone = $self->$orig($args);
202              
203             if ( ref $self->others ) {
204             $clone->others( Clone::clone( $self->others ) );
205             }
206              
207             return $clone;
208             };
209              
210             1;
211              
212             __END__
213              
214             =head1 NAME
215              
216             HTML::FormFu::Role::Constraint::Others - Base class for constraints needing others() method
217              
218             =head1 VERSION
219              
220             version 2.05
221              
222             =head1 METHODS
223              
224             =head2 others
225              
226             Arguments: \@nested_names
227              
228             =head2 other_siblings
229              
230             Arguments: $bool
231              
232             If true, the L</others> list will be automatically generated from the
233             C<nested_name> of all fields which are considered siblings of the field the
234             constraint is attached to.
235              
236             Sibling are found by searching up through the field's parental hierarchy for
237             the first block containing any other field. All fields attached at any depth
238             to this block are considered siblings.
239              
240             =head2 attach_errors_to_base
241              
242             If true, any error will cause the error message to be associated with the
243             field the constraint is attached to.
244              
245             Can be use in conjunction with L</attach_errors_to_others>.
246              
247             Is ignored if L</attach_errors_to> is set.
248              
249             =head2 attach_errors_to_others
250              
251             If true, any error will cause the error message to be associated with every
252             field named in L</others>.
253              
254             Can be use in conjunction with L</attach_errors_to_base>.
255              
256             Is ignored if L</attach_errors_to> is set.
257              
258             =head2 attach_errors_to
259              
260             Arguments: \@field_names
261              
262             Any error will cause the error message to be associated with every field
263             named in L</attach_errors_to>.
264              
265             Overrides L</attach_errors_to_base> and L</attach_errors_to_others>.
266              
267             =head1 SEE ALSO
268              
269             Is a sub-class of, and inherits methods from L<HTML::FormFu::Constraint>
270              
271             L<HTML::FormFu>
272              
273             =head1 AUTHOR
274              
275             Carl Franks C<cfranks@cpan.org>
276              
277             =head1 LICENSE
278              
279             This library is free software, you can redistribute it and/or modify it under
280             the same terms as Perl itself.