File Coverage

blib/lib/Dancer2/Plugin/TemplateFlute/Form.pm
Criterion Covered Total %
statement 50 51 98.0
branch 8 16 50.0
condition n/a
subroutine 12 12 100.0
pod 6 6 100.0
total 76 85 89.4


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::TemplateFlute::Form;
2              
3 4     4   149237 use Carp;
  4         5  
  4         187  
4 4     4   414 use Hash::MultiValue;
  4         1526  
  4         84  
5 4     4   14 use Types::Standard -types;
  4         4  
  4         38  
6 4     4   10427 use Moo;
  4         6  
  4         21  
7 4     4   2628 use namespace::clean;
  4         28437  
  4         17  
8              
9             =head1 NAME
10              
11             Dancer2::Plugin::TemplateFlute::Form - form object for Template::Flute
12              
13             =cut
14              
15             my $_coerce_to_hash_multivalue = sub {
16             if ( !defined $_[0] ) {
17             Hash::MultiValue->new;
18             }
19             elsif ( ref( $_[0] ) eq 'Hash::MultiValue' ) {
20             $_[0];
21             }
22             elsif ( ref( $_[0] ) eq 'HASH' ) {
23             Hash::MultiValue->from_mixed( $_[0] );
24             }
25             else {
26             croak "Unable to coerce to Hash::MultiValue";
27             }
28             };
29              
30             #
31             # attributes
32             #
33              
34             has action => (
35             is => 'ro',
36             isa => Str,
37             predicate => 1,
38             writer => 'set_action',
39             );
40              
41             has errors => (
42             is => 'ro',
43             lazy => 1,
44             isa => InstanceOf ['Hash::MultiValue'],
45             default => sub { Hash::MultiValue->new },
46             coerce => $_coerce_to_hash_multivalue,
47             clearer => 1,
48             writer => '_set_errors',
49             );
50              
51             sub add_error {
52             my $self = shift;
53             $self->errors->add(@_);
54             }
55              
56             sub set_error {
57             my $self = shift;
58             $self->errors->set(@_);
59             }
60              
61             sub set_errors {
62             my $self = shift;
63             $self->_set_errors(@_);
64             }
65              
66             after 'add_error', 'set_error', 'set_errors' => sub {
67             $_[0]->set_valid(0);
68             };
69              
70             has fields => (
71             is => 'ro',
72             lazy => 1,
73             isa => ArrayRef [Str],
74             default => sub { [] },
75             clearer => 1,
76             writer => 'set_fields',
77             );
78              
79             has log_cb => (
80             is => 'ro',
81             isa => CodeRef,
82             predicate => 1,
83             );
84              
85             has name => (
86             is => 'ro',
87             isa => Str,
88             default => 'main',
89             );
90              
91             has pristine => (
92             is => 'ro',
93             isa => Defined & Bool,
94             default => 1,
95             writer => 'set_pristine',
96             );
97              
98             has session => (
99             is => 'ro',
100             isa => HasMethods [ 'read', 'write' ],
101             required => 1,
102             );
103              
104             # We use a private writer since we want to have to_session called whenever
105             # the public set_valid method is called but we also have a need to be
106             # able to update this attribute without writing the form back to the session.
107             has valid => (
108             is => 'ro',
109             isa => Bool,
110             clearer => 1,
111             writer => '_set_valid',
112             );
113              
114             sub set_valid {
115 4     4 1 5706 my ( $self, $value ) = @_;
116 4         63 $self->_set_valid($value);
117 4         502 $self->log( "debug", "Setting valid for form ",
118             $self->name, " to $value." );
119 4         26 $self->to_session;
120             }
121              
122             has values => (
123             is => 'ro',
124             lazy => 1,
125             isa => InstanceOf ['Hash::MultiValue'],
126             default => sub { Hash::MultiValue->new },
127             coerce => $_coerce_to_hash_multivalue,
128             trigger => sub { $_[0]->set_pristine(0) if $_[1]->keys },
129             clearer => 1,
130             writer => 'fill',
131             );
132              
133             #
134             # methods
135             #
136              
137             sub errors_hashed {
138 2     2 1 2698 my $self = shift;
139 2         3 my @hashed;
140              
141             $self->errors->each(
142 2     5   40 sub { push @hashed, +{ name => $_[0], label => $_[1] } } );
  5         46  
143              
144 2         20 return \@hashed;
145             }
146              
147             sub from_session {
148 1     1 1 689 my ($self) = @_;
149              
150 1         5 $self->log( debug => "Reading form ", $self->name, " from session");
151              
152 1 50       9 if ( my $forms_ref = $self->session->read('form') ) {
153 1 50       31 if ( exists $forms_ref->{ $self->name } ) {
154 1         3 my $form = $forms_ref->{ $self->name };
155              
156             # set_valid causes write back to session so use private
157             # method instead. Also set_errors causes set_valid to be
158             # called so use private method there too.
159 1 50       6 $self->set_action( $form->{action} ) if $form->{action};
160 1 50       464 $self->set_fields( $form->{fields} ) if $form->{fields};
161 1 50       502 $self->_set_errors( $form->{errors} ) if $form->{errors};
162 1 50       53 $self->fill( $form->{values} ) if $form->{values};
163 1 50       56 $self->_set_valid( $form->{valid} ) if defined $form->{valid};
164              
165 1         11 return 1;
166             }
167             }
168 0         0 return 0;
169             }
170              
171             sub log {
172 10     10 1 18 my ($self, $level, @message) = @_;
173 10 50       47 $self->log_cb->($level, join('',@message)) if $self->has_log_cb;
174             }
175              
176             sub reset {
177 1     1 1 12610 my $self = shift;
178 1         6 $self->clear_fields;
179 1         419 $self->clear_errors;
180 1         316 $self->clear_values;
181 1         270 $self->clear_valid;
182 1         293 $self->set_pristine(1);
183 1         35 $self->to_session;
184             }
185              
186             sub to_session {
187 5     5 1 7 my $self = shift;
188 5         4 my ($forms_ref);
189              
190 5         13 $self->log( debug => "Writing form ", $self->name, " to session");
191              
192             # get current form information from session
193 5         33 $forms_ref = $self->session->read('form');
194              
195             # update our form
196 5         170 $forms_ref->{ $self->name } = {
197             action => $self->action,
198             name => $self->name,
199             fields => $self->fields,
200             errors => $self->errors->mixed,
201             values => $self->values->mixed,
202             valid => $self->valid,
203             };
204              
205             # update form information
206 5         285 $self->session->write( form => $forms_ref );
207             }
208              
209             =head1 ATTRIBUTES
210              
211             =head2 name
212              
213             The name of the form.
214              
215             Defaults to 'main',
216              
217             =head2 action
218              
219             The form action.
220              
221             =over
222              
223             =item writer: set_action
224              
225             =item predicate: has_action
226              
227             =back
228              
229             =head2 errors
230            
231             Errors stored in a L object.
232              
233             Get form errors:
234              
235             $errors = $form->errors;
236              
237             =over
238              
239             =item writer: set_errors
240              
241             Set form errors (this will overwrite all existing errors):
242            
243             $form->set_errors(
244             username => 'Minimum 8 characters',
245             username => 'Must contain at least one number',
246             email => 'Invalid email address',
247             );
248              
249             =item clearer: clear_errors
250              
251             =back
252              
253             B Avoid using C<< $form->errors->add() >> or C<< $form->errors->set() >>
254             since doing that means that L does not automatically get set to C<0>.
255             Instead use one of L or L methods.
256              
257             =head2 fields
258              
259             Get form fields:
260              
261             $fields = $form->fields;
262              
263             =over
264              
265             =item writer: set_fields
266              
267             $form->set_fields([qw/username email password verify/]);
268              
269             =item clearer: clear_fields
270              
271             =back
272              
273             =head2 log_cb
274              
275             A code reference that can be used to log things. Signature must be like:
276              
277             $log_cb->( $level, $message );
278              
279             Logging is via L method.
280              
281             =over
282              
283             =item predicate: has_log_cb
284              
285             =back
286              
287             =head2 pristine
288              
289             Determines whether a form is pristine or not.
290              
291             This can be used to fill the form with default values and suppress display
292             of errors.
293              
294             A form is pristine until it receives form field input from the request or
295             out of the session.
296              
297             =over
298              
299             =item writer: set_pristine
300              
301             =back
302              
303             =head2 session
304              
305             A session object. Must have methods C and C.
306              
307             Required.
308              
309             =head2 valid
310              
311             Determine whether form values are valid:
312              
313             $form->valid();
314              
315             Return values are 1 (valid), 0 (invalid) or C (unknown).
316              
317             =over
318              
319             =item writer: set_valid
320              
321             =item clearer: clear_valid
322              
323             =back
324              
325             The form status automatically changes to "invalid" when L is set
326             or either L or L are called.
327            
328             =head2 values
329              
330             Get form values as hash reference:
331              
332             $values = $form->values;
333              
334             =over
335              
336             =item writer: fill
337              
338             Fill form values:
339              
340             $form->fill({username => 'racke', email => 'racke@linuxia.de'});
341              
342             =item clearer: clear_values
343              
344             =back
345              
346             =head1 METHODS
347              
348             =head2 add_error
349              
350             Add an error:
351              
352             $form->add_error( $key, $value [, $value ... ]);
353              
354             =head2 errors_hashed
355              
356             Returns form errors as array reference filled with hash references
357             for each error.
358              
359             For example these L:
360              
361             { username => 'Minimum 8 characters',
362             email => 'Invalid email address' }
363              
364             will be returned as:
365              
366             [
367             { name => 'username', value => 'Minimum 8 characters' },
368             { name => 'email', value => 'Invalid email address' },
369             ]
370              
371             =head2 from_session
372              
373             Loads form data from session key C
.
374             Returns 1 if session contains data for this form, 0 otherwise.
375              
376             =head2 log $level, @message
377              
378             Log message via L.
379              
380             =head2 reset
381              
382             Reset form information (fields, errors, values, valid) and
383             updates session accordingly.
384              
385             =head2 set_error
386              
387             Set a specific error:
388              
389             $form->set_error( $key, $value [, $value ... ]);
390              
391             =head2 to_session
392              
393             Saves form name, form fields, form values and form errors into
394             session key C.
395              
396              
397             =head1 AUTHORS
398              
399             Original Dancer plugin by:
400              
401             Stefan Hornburg (Racke), C<< >>
402              
403             Initial port to Dancer2 by:
404              
405             Evan Brown (evanernest), C<< >>
406              
407             Rehacking to Dancer2's plugin2 and general rework:
408              
409             Peter Mottram (SysPete), C<< >>
410              
411             =head1 BUGS
412              
413             Please report any bugs or feature requests via GitHub issues:
414             L.
415              
416             We will be notified, and then you'll automatically be notified of progress
417             on your bug as we make changes.
418              
419             =head1 ACKNOWLEDGEMENTS
420              
421              
422             =head1 LICENSE AND COPYRIGHT
423              
424             Copyright 2011-2016 Stefan Hornburg (Racke).
425              
426             This program is free software; you can redistribute it and/or modify it
427             under the terms of either: the GNU General Public License as published
428             by the Free Software Foundation; or the Artistic License.
429              
430             See http://dev.perl.org/licenses/ for more information.
431              
432             =cut
433              
434             1;