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   167581 use Carp;
  4         5  
  4         215  
4 4     4   449 use Hash::MultiValue;
  4         1607  
  4         88  
5 4     4   15 use Types::Standard -types;
  4         4  
  4         41  
6 4     4   10569 use Moo;
  4         5  
  4         21  
7 4     4   2791 use namespace::clean;
  4         30134  
  4         16  
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 6518 my ( $self, $value ) = @_;
116 4         60 $self->_set_valid($value);
117 4         690 $self->log( "debug", "Setting valid for form ",
118             $self->name, " to $value." );
119 4         29 $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             # in case fill gets passed a list then convert to hashref
134             around fill => sub {
135             my ( $orig, $self ) = ( shift, shift );
136             my $values = @_ && ref( $_[0] ) ? $_[0] : {@_};
137             $orig->( $self, $values );
138             };
139              
140             #
141             # methods
142             #
143              
144             sub errors_hashed {
145 2     2 1 3164 my $self = shift;
146 2         3 my @hashed;
147              
148             $self->errors->each(
149 2     5   42 sub { push @hashed, +{ name => $_[0], label => $_[1] } } );
  5         53  
150              
151 2         24 return \@hashed;
152             }
153              
154             sub from_session {
155 1     1 1 1026 my ($self) = @_;
156              
157 1         6 $self->log( debug => "Reading form ", $self->name, " from session");
158              
159 1 50       9 if ( my $forms_ref = $self->session->read('form') ) {
160 1 50       32 if ( exists $forms_ref->{ $self->name } ) {
161 1         2 my $form = $forms_ref->{ $self->name };
162              
163             # set_valid causes write back to session so use private
164             # method instead. Also set_errors causes set_valid to be
165             # called so use private method there too.
166 1 50       7 $self->set_action( $form->{action} ) if $form->{action};
167 1 50       579 $self->set_fields( $form->{fields} ) if $form->{fields};
168 1 50       444 $self->_set_errors( $form->{errors} ) if $form->{errors};
169 1 50       56 $self->fill( $form->{values} ) if $form->{values};
170 1 50       58 $self->_set_valid( $form->{valid} ) if defined $form->{valid};
171              
172 1         14 return 1;
173             }
174             }
175 0         0 return 0;
176             }
177              
178             sub log {
179 10     10 1 20 my ($self, $level, @message) = @_;
180 10 50       57 $self->log_cb->($level, join('',@message)) if $self->has_log_cb;
181             }
182              
183             sub reset {
184 1     1 1 14961 my $self = shift;
185 1         7 $self->clear_fields;
186 1         424 $self->clear_errors;
187 1         286 $self->clear_values;
188 1         280 $self->clear_valid;
189 1         327 $self->set_pristine(1);
190 1         27 $self->to_session;
191             }
192              
193             sub to_session {
194 5     5 1 6 my $self = shift;
195 5         6 my ($forms_ref);
196              
197 5         11 $self->log( debug => "Writing form ", $self->name, " to session");
198              
199             # get current form information from session
200 5         45 $forms_ref = $self->session->read('form');
201              
202             # update our form
203 5         203 $forms_ref->{ $self->name } = {
204             action => $self->action,
205             name => $self->name,
206             fields => $self->fields,
207             errors => $self->errors->mixed,
208             values => $self->values->mixed,
209             valid => $self->valid,
210             };
211              
212             # update form information
213 5         345 $self->session->write( form => $forms_ref );
214             }
215              
216             =head1 ATTRIBUTES
217              
218             =head2 name
219              
220             The name of the form.
221              
222             Defaults to 'main',
223              
224             =head2 action
225              
226             The form action.
227              
228             =over
229              
230             =item writer: set_action
231              
232             =item predicate: has_action
233              
234             =back
235              
236             =head2 errors
237            
238             Errors stored in a L object.
239              
240             Get form errors:
241              
242             $errors = $form->errors;
243              
244             =over
245              
246             =item writer: set_errors
247              
248             Set form errors (this will overwrite all existing errors):
249            
250             $form->set_errors(
251             username => 'Minimum 8 characters',
252             username => 'Must contain at least one number',
253             email => 'Invalid email address',
254             );
255              
256             =item clearer: clear_errors
257              
258             =back
259              
260             B Avoid using C<< $form->errors->add() >> or C<< $form->errors->set() >>
261             since doing that means that L does not automatically get set to C<0>.
262             Instead use one of L or L methods.
263              
264             =head2 fields
265              
266             Get form fields:
267              
268             $fields = $form->fields;
269              
270             =over
271              
272             =item writer: set_fields
273              
274             $form->set_fields([qw/username email password verify/]);
275              
276             =item clearer: clear_fields
277              
278             =back
279              
280             =head2 log_cb
281              
282             A code reference that can be used to log things. Signature must be like:
283              
284             $log_cb->( $level, $message );
285              
286             Logging is via L method.
287              
288             =over
289              
290             =item predicate: has_log_cb
291              
292             =back
293              
294             =head2 pristine
295              
296             Determines whether a form is pristine or not.
297              
298             This can be used to fill the form with default values and suppress display
299             of errors.
300              
301             A form is pristine until it receives form field input from the request or
302             out of the session.
303              
304             =over
305              
306             =item writer: set_pristine
307              
308             =back
309              
310             =head2 session
311              
312             A session object. Must have methods C and C.
313              
314             Required.
315              
316             =head2 valid
317              
318             Determine whether form values are valid:
319              
320             $form->valid();
321              
322             Return values are 1 (valid), 0 (invalid) or C (unknown).
323              
324             =over
325              
326             =item writer: set_valid
327              
328             =item clearer: clear_valid
329              
330             =back
331              
332             The form status automatically changes to "invalid" when L is set
333             or either L or L are called.
334            
335             =head2 values
336              
337             Get form values as hash reference:
338              
339             $values = $form->values;
340              
341             =over
342              
343             =item writer: fill
344              
345             Fill form values:
346              
347             $form->fill({username => 'racke', email => 'racke@linuxia.de'});
348              
349             =item clearer: clear_values
350              
351             =back
352              
353             =head1 METHODS
354              
355             =head2 add_error
356              
357             Add an error:
358              
359             $form->add_error( $key, $value [, $value ... ]);
360              
361             =head2 errors_hashed
362              
363             Returns form errors as array reference filled with hash references
364             for each error.
365              
366             For example these L:
367              
368             { username => 'Minimum 8 characters',
369             email => 'Invalid email address' }
370              
371             will be returned as:
372              
373             [
374             { name => 'username', value => 'Minimum 8 characters' },
375             { name => 'email', value => 'Invalid email address' },
376             ]
377              
378             =head2 from_session
379              
380             Loads form data from session key C
.
381             Returns 1 if session contains data for this form, 0 otherwise.
382              
383             =head2 log $level, @message
384              
385             Log message via L.
386              
387             =head2 reset
388              
389             Reset form information (fields, errors, values, valid) and
390             updates session accordingly.
391              
392             =head2 set_error
393              
394             Set a specific error:
395              
396             $form->set_error( $key, $value [, $value ... ]);
397              
398             =head2 to_session
399              
400             Saves form name, form fields, form values and form errors into
401             session key C.
402              
403              
404             =head1 AUTHORS
405              
406             Original Dancer plugin by:
407              
408             Stefan Hornburg (Racke), C<< >>
409              
410             Initial port to Dancer2 by:
411              
412             Evan Brown (evanernest), C<< >>
413              
414             Rehacking to Dancer2's plugin2 and general rework:
415              
416             Peter Mottram (SysPete), C<< >>
417              
418             =head1 BUGS
419              
420             Please report any bugs or feature requests via GitHub issues:
421             L.
422              
423             We will be notified, and then you'll automatically be notified of progress
424             on your bug as we make changes.
425              
426             =head1 ACKNOWLEDGEMENTS
427              
428              
429             =head1 LICENSE AND COPYRIGHT
430              
431             Copyright 2011-2016 Stefan Hornburg (Racke).
432              
433             This program is free software; you can redistribute it and/or modify it
434             under the terms of either: the GNU General Public License as published
435             by the Free Software Foundation; or the Artistic License.
436              
437             See http://dev.perl.org/licenses/ for more information.
438              
439             =cut
440              
441             1;