blib/lib/Dancer2/Template/TemplateFlute.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 13 | 15 | 86.6 |
branch | n/a | ||
condition | n/a | ||
subroutine | 5 | 5 | 100.0 |
pod | n/a | ||
total | 18 | 20 | 90.0 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Dancer2::Template::TemplateFlute; | ||||||
2 | |||||||
3 | =head1 NAME | ||||||
4 | |||||||
5 | Dancer2::Template::TemplateFlute - Template::Flute wrapper for Dancer2 | ||||||
6 | |||||||
7 | =head1 VERSION | ||||||
8 | |||||||
9 | Version 0.203 | ||||||
10 | |||||||
11 | =cut | ||||||
12 | |||||||
13 | our $VERSION = '0.203'; | ||||||
14 | |||||||
15 | 1 | 1 | 46749 | use Carp qw/croak/; | |||
1 | 2 | ||||||
1 | 43 | ||||||
16 | 1 | 1 | 477 | use Dancer2::Core::Types; | |||
1 | 7811 | ||||||
1 | 329 | ||||||
17 | 1 | 1 | 9 | use Module::Runtime qw/use_module/; | |||
1 | 5 | ||||||
1 | 5 | ||||||
18 | 1 | 1 | 36 | use Scalar::Util qw/blessed/; | |||
1 | 1 | ||||||
1 | 34 | ||||||
19 | 1 | 1 | 688 | use Template::Flute; | |||
0 | |||||||
0 | |||||||
20 | use Template::Flute::Iterator; | ||||||
21 | use Template::Flute::Utils; | ||||||
22 | use Template::Flute::I18N; | ||||||
23 | use Try::Tiny; | ||||||
24 | |||||||
25 | use Moo; | ||||||
26 | with 'Dancer2::Core::Role::Template'; | ||||||
27 | use namespace::clean; | ||||||
28 | |||||||
29 | has autodetect_disable => ( | ||||||
30 | is => 'ro', | ||||||
31 | isa => ArrayRef, | ||||||
32 | lazy => 1, | ||||||
33 | default => sub { | ||||||
34 | my $self = shift; | ||||||
35 | # do we need to add anything to default list? | ||||||
36 | my @autodetect_disable = (); | ||||||
37 | if ( my $autodetect = $self->config->{autodetect} ) { | ||||||
38 | push @autodetect_disable, @{ $autodetect->{disable} }; | ||||||
39 | } | ||||||
40 | return \@autodetect_disable; | ||||||
41 | }, | ||||||
42 | ); | ||||||
43 | |||||||
44 | has check_dangling => ( | ||||||
45 | is => 'ro', | ||||||
46 | isa => Bool, | ||||||
47 | lazy => 1, | ||||||
48 | default => sub { shift->config->{check_dangling} }, | ||||||
49 | ); | ||||||
50 | |||||||
51 | has disable_check_dangling => ( | ||||||
52 | is => 'ro', | ||||||
53 | isa => Bool, | ||||||
54 | lazy => 1, | ||||||
55 | default => sub { shift->config->{disable_check_dangling} }, | ||||||
56 | ); | ||||||
57 | |||||||
58 | has '+default_tmpl_ext' => ( | ||||||
59 | default => sub { shift->config->{extension} || 'html' }, | ||||||
60 | ); | ||||||
61 | |||||||
62 | has filters => ( | ||||||
63 | is => 'ro', | ||||||
64 | isa => HashRef, | ||||||
65 | lazy => 1, | ||||||
66 | default => sub { shift->config->{filters} || +{} }, | ||||||
67 | ); | ||||||
68 | |||||||
69 | has iterators => ( | ||||||
70 | is => 'rw', | ||||||
71 | isa => HashRef, | ||||||
72 | lazy => 1, | ||||||
73 | default => sub { shift->config->{iterators} || +{} }, | ||||||
74 | ); | ||||||
75 | |||||||
76 | has i18n_obj => ( | ||||||
77 | is => 'lazy', | ||||||
78 | isa => InstanceOf['Template::Flute::I18N'], | ||||||
79 | ); | ||||||
80 | |||||||
81 | sub _build_i18n_obj { | ||||||
82 | my $self = shift; | ||||||
83 | my $conf = $self->config; | ||||||
84 | my $localize; | ||||||
85 | if ( $conf and exists $conf->{i18n} and exists $conf->{i18n}->{class} ) { | ||||||
86 | my $class = $conf->{i18n}->{class}; | ||||||
87 | my %args; | ||||||
88 | if ( $conf->{i18n}->{options} ) { | ||||||
89 | |||||||
90 | # do a shallow copy and pass that | ||||||
91 | %args = %{ $conf->{i18n}->{options} }; | ||||||
92 | } | ||||||
93 | my $obj = try { | ||||||
94 | use_module($class)->new(%args); | ||||||
95 | } | ||||||
96 | catch { | ||||||
97 | croak "Failed to import class $class: $_"; | ||||||
98 | }; | ||||||
99 | my $method = $conf->{i18n}->{method} || 'localize'; | ||||||
100 | |||||||
101 | # store the closure in the object to avoid loading it up each time | ||||||
102 | $localize = sub { | ||||||
103 | my $to_translate = shift; | ||||||
104 | return $obj->$method($to_translate); | ||||||
105 | }; | ||||||
106 | } | ||||||
107 | |||||||
108 | # provide a common interface with Template::Flute::I18N | ||||||
109 | return Template::Flute::I18N->new($localize); | ||||||
110 | } | ||||||
111 | |||||||
112 | sub render ($$$) { | ||||||
113 | my ( $self, $template, $tokens ) = @_; | ||||||
114 | my ( %args, $flute, $html, $name, %parms, %template_iterators, | ||||||
115 | %iterators, $class ); | ||||||
116 | |||||||
117 | %args = ( | ||||||
118 | template_file => $template, | ||||||
119 | scopes => 1, | ||||||
120 | auto_iterators => 1, | ||||||
121 | values => $tokens, | ||||||
122 | filters => $self->filters, | ||||||
123 | autodetect => { disable => $self->autodetect_disable }, | ||||||
124 | #autodetect => { disable => [qw/Dancer2::Session::Abstract/] }, | ||||||
125 | ); | ||||||
126 | |||||||
127 | # determine whether we need to pass an adjust URI to Template::Flute | ||||||
128 | if ( my $request = $tokens->{request} ) { | ||||||
129 | $args{uri} = $request->base->path if $request->base->path ne '/'; | ||||||
130 | } | ||||||
131 | |||||||
132 | if ( my $i18n = $self->i18n_obj ) { | ||||||
133 | $args{i18n} = $i18n; | ||||||
134 | } | ||||||
135 | |||||||
136 | if ( my $email_cids = $tokens->{email_cids} ) { | ||||||
137 | $args{email_cids} = $email_cids; | ||||||
138 | |||||||
139 | # use the 'cids' tokens only if email_cids is defined | ||||||
140 | if ( my $cid_options = $tokens->{cids} ) { | ||||||
141 | $args{cids} = {%$cid_options}; | ||||||
142 | } | ||||||
143 | } | ||||||
144 | |||||||
145 | $flute = Template::Flute->new(%args); | ||||||
146 | |||||||
147 | # process HTML template to determine iterators used by template | ||||||
148 | $flute->process_template(); | ||||||
149 | |||||||
150 | # instantiate iterators where object isn't yet available | ||||||
151 | if ( %template_iterators = $flute->template()->iterators ) { | ||||||
152 | my $selector; | ||||||
153 | |||||||
154 | for my $name ( keys %template_iterators ) { | ||||||
155 | if ( my $value = $self->iterators->{$name} ) { | ||||||
156 | %parms = %$value; | ||||||
157 | |||||||
158 | $class = "Template::Flute::Iterator::$parms{class}"; | ||||||
159 | |||||||
160 | if ( $parms{file} ) { | ||||||
161 | $parms{file} = | ||||||
162 | Template::Flute::Utils::derive_filename( $template, | ||||||
163 | $parms{file}, 1 ); | ||||||
164 | } | ||||||
165 | |||||||
166 | if ( $selector = delete $parms{selector} ) { | ||||||
167 | if ( $selector eq '*' ) { | ||||||
168 | $parms{selector} = '*'; | ||||||
169 | } | ||||||
170 | elsif ( $tokens->{$selector} ) { | ||||||
171 | $parms{selector} = | ||||||
172 | { $selector => $tokens->{$selector} }; | ||||||
173 | } | ||||||
174 | } | ||||||
175 | |||||||
176 | eval "require $class"; | ||||||
177 | if ($@) { | ||||||
178 | croak "Failed to load class $class for iterator $name: $@\n"; | ||||||
179 | } | ||||||
180 | |||||||
181 | eval { $iterators{$name} = $class->new(%parms); }; | ||||||
182 | |||||||
183 | if ($@) { | ||||||
184 | croak "Failed to instantiate class $class for iterator $name: $@\n"; | ||||||
185 | } | ||||||
186 | |||||||
187 | $flute->specification->set_iterator( $name, $iterators{$name} ); | ||||||
188 | } | ||||||
189 | } | ||||||
190 | } | ||||||
191 | |||||||
192 | # check for forms | ||||||
193 | if ( my @forms = $flute->template->forms() ) { | ||||||
194 | if ( $tokens->{form} ) { | ||||||
195 | $self->_tf_manage_forms( $flute, $tokens, @forms ); | ||||||
196 | } | ||||||
197 | else { | ||||||
198 | $self->log_cb->( 'debug', | ||||||
199 | 'Missing form parameters for forms ' | ||||||
200 | . join( ", ", sort map { $_->name } @forms ) ); | ||||||
201 | } | ||||||
202 | } | ||||||
203 | elsif ( $tokens->{form} ) { | ||||||
204 | my $form_name = | ||||||
205 | blessed( $tokens->{form} ) ? $tokens->{form}->name : $tokens->{form}; | ||||||
206 | |||||||
207 | $self->log_cb->( 'debug', | ||||||
208 | "Form $form_name passed, " | ||||||
209 | . "but no forms found in the template $template." ); | ||||||
210 | } | ||||||
211 | |||||||
212 | $html = $flute->process(); | ||||||
213 | |||||||
214 | if ( | ||||||
215 | $self->check_dangling | ||||||
216 | or ( $tokens->{settings}->{environment} eq 'development' | ||||||
217 | && !$self->disable_check_dangling ) | ||||||
218 | ) | ||||||
219 | { | ||||||
220 | |||||||
221 | if ( my @warnings = $flute->specification->dangling ) { | ||||||
222 | foreach my $warn (@warnings) { | ||||||
223 | $self->log_cb->( | ||||||
224 | 'debug', | ||||||
225 | 'Found dangling element ' | ||||||
226 | . $warn->{type} . ' ' | ||||||
227 | . $warn->{name} . ' (', | ||||||
228 | $warn->{dump}, | ||||||
229 | ')' | ||||||
230 | ); | ||||||
231 | } | ||||||
232 | } | ||||||
233 | } | ||||||
234 | return $html; | ||||||
235 | } | ||||||
236 | |||||||
237 | sub _tf_manage_forms { | ||||||
238 | my ( $self, $flute, $tokens, @forms ) = @_; | ||||||
239 | |||||||
240 | # simple case: only one form passed and one in the flute | ||||||
241 | if ( ref( $tokens->{form} ) ne 'ARRAY' ) { | ||||||
242 | my $form_name = $tokens->{form}->name; | ||||||
243 | if ( @forms == 1 ) { | ||||||
244 | my $form = shift @forms; | ||||||
245 | if ( $form_name eq 'main' | ||||||
246 | or $form_name eq $form->name ) | ||||||
247 | { | ||||||
248 | $self->_tf_fill_forms( $flute, $tokens->{form}, $form, | ||||||
249 | $tokens ); | ||||||
250 | } | ||||||
251 | } | ||||||
252 | else { | ||||||
253 | my $found = 0; | ||||||
254 | foreach my $form (@forms) { | ||||||
255 | if ( $form_name eq $form->name ) { | ||||||
256 | $self->_tf_fill_forms( $flute, $tokens->{form}, $form, | ||||||
257 | $tokens ); | ||||||
258 | $found++; | ||||||
259 | } | ||||||
260 | } | ||||||
261 | if ( $found != 1 ) { | ||||||
262 | $self->log_cb->( | ||||||
263 | 'error', "Multiple form are not being managed correctly, found $found corresponding forms, but we expected just one!" | ||||||
264 | ); | ||||||
265 | } | ||||||
266 | } | ||||||
267 | } | ||||||
268 | else { | ||||||
269 | foreach my $passed_form ( @{ $tokens->{form} } ) { | ||||||
270 | foreach my $form (@forms) { | ||||||
271 | if ( $passed_form->name eq $form->name ) { | ||||||
272 | $self->_tf_fill_forms( $flute, $passed_form, $form, | ||||||
273 | $tokens ); | ||||||
274 | } | ||||||
275 | } | ||||||
276 | } | ||||||
277 | } | ||||||
278 | } | ||||||
279 | |||||||
280 | sub _tf_fill_forms { | ||||||
281 | my ( $self, $flute, $passed_form, $form, $tokens ) = @_; | ||||||
282 | |||||||
283 | # arguments: | ||||||
284 | # $flute is the template object. | ||||||
285 | |||||||
286 | # $passed_form is the Dancer::Plugin::Form object we got from the | ||||||
287 | # tokens, which is $tokens->{form} when we have just a single one. | ||||||
288 | |||||||
289 | # $form is the form object we got from the template itself, with | ||||||
290 | # $flute->template->forms | ||||||
291 | |||||||
292 | # $tokens is the hashref passed to the template. We need it for the | ||||||
293 | # iterators. | ||||||
294 | |||||||
295 | my ( $iter, $action ); | ||||||
296 | for my $name ( $form->iterators ) { | ||||||
297 | if ( ref( $tokens->{$name} ) eq 'ARRAY' ) { | ||||||
298 | $iter = Template::Flute::Iterator->new( $tokens->{$name} ); | ||||||
299 | $flute->specification->set_iterator( $name, $iter ); | ||||||
300 | } | ||||||
301 | } | ||||||
302 | if ( $action = $passed_form->action() ) { | ||||||
303 | $form->set_action($action); | ||||||
304 | } | ||||||
305 | $passed_form->set_fields( [ map { $_->{name} } @{ $form->fields() } ] ); | ||||||
306 | $form->fill( $passed_form->values ); | ||||||
307 | |||||||
308 | $passed_form->to_session; | ||||||
309 | } | ||||||
310 | |||||||
311 | =head1 DESCRIPTION | ||||||
312 | |||||||
313 | This class is an interface between Dancer2's template engine abstraction layer | ||||||
314 | and the L |
||||||
315 | |||||||
316 | In order to use this engine, use the template setting: | ||||||
317 | |||||||
318 | template: template_flute | ||||||
319 | |||||||
320 | The default template extension is ".html". | ||||||
321 | |||||||
322 | =head2 LAYOUT | ||||||
323 | |||||||
324 | Each layout needs a specification file and a template file. To embed | ||||||
325 | the content of your current view into the layout, put the following | ||||||
326 | into your specification file, e.g. F |
||||||
327 | |||||||
328 | |
||||||
329 | |
||||||
330 | |||||||
331 | |||||||
332 | This replaces the contents of the following block in your HTML | ||||||
333 | template, e.g. F |
||||||
334 | |||||||
335 | |
||||||
336 | Your content | ||||||
337 | |||||||
338 | |||||||
339 | =head2 ITERATORS | ||||||
340 | |||||||
341 | Iterators can be specified explicitly in the configuration file as below. | ||||||
342 | |||||||
343 | engines: | ||||||
344 | template: | ||||||
345 | template_flute: | ||||||
346 | iterators: | ||||||
347 | fruits: | ||||||
348 | class: JSON | ||||||
349 | file: fruits.json | ||||||
350 | |||||||
351 | =head2 FILTER OPTIONS | ||||||
352 | |||||||
353 | Filter options and classes can be specified in the configuration file as below. | ||||||
354 | |||||||
355 | engines: | ||||||
356 | template: | ||||||
357 | template_flute: | ||||||
358 | filters: | ||||||
359 | currency: | ||||||
360 | options: | ||||||
361 | int_curr_symbol: "$" | ||||||
362 | image: | ||||||
363 | class: "Flowers::Filters::Image" | ||||||
364 | |||||||
365 | =head2 ADJUSTING URIS | ||||||
366 | |||||||
367 | We automatically adjust links in the templates if the value of | ||||||
368 | C |
||||||
369 | |||||||
370 | =head2 EMBEDDING IMAGES IN EMAILS | ||||||
371 | |||||||
372 | If you pass a value named C |
||||||
373 | reference, all the images C |
||||||
374 | the CIDs, and the reference will be populated with an hashref, as | ||||||
375 | documented in L |
||||||
376 | |||||||
377 | Further options for the CIDs should be passed in an optional value | ||||||
378 | named C |
||||||
379 | |||||||
380 | |||||||
381 | =head2 DISABLE OBJECT AUTODETECTION | ||||||
382 | |||||||
383 | Sometimes you want to pass values to a template which are objects, but | ||||||
384 | don't have an accessor, so they should be treated like hashrefs instead. | ||||||
385 | |||||||
386 | You can specify classes with the following syntax: | ||||||
387 | |||||||
388 | engines: | ||||||
389 | template: | ||||||
390 | template_flute: | ||||||
391 | autodetect: | ||||||
392 | disable: | ||||||
393 | - My::Class1 | ||||||
394 | - My::Class2 | ||||||
395 | |||||||
396 | |||||||
397 | The class matching is checked by L |
||||||
398 | any parent class would do. | ||||||
399 | |||||||
400 | =head2 LOCALIZATION | ||||||
401 | |||||||
402 | Templates can be localized using the Template::Flute::I18N module. You | ||||||
403 | can define a class that provides a method which takes as first (and | ||||||
404 | only argument) the string to translate, and returns the translated | ||||||
405 | one. You have to provide the class and the method. If the class is not | ||||||
406 | provided, no localization is done. If no method is specified, | ||||||
407 | 'localize' will be used. The app will crash if the class doesn't | ||||||
408 | provide such method. | ||||||
409 | |||||||
410 | B | ||||||
411 | translate the string>. | ||||||
412 | |||||||
413 | Example configuration, assuming the class C |
||||||
414 | C |
||||||
415 | |||||||
416 | engines: | ||||||
417 | template: | ||||||
418 | template_flute: | ||||||
419 | i18n: | ||||||
420 | class: MyApp::Lexicon | ||||||
421 | method: try_to_translate | ||||||
422 | |||||||
423 | |||||||
424 | A class could be something like this: | ||||||
425 | |||||||
426 | package MyTestApp::Lexicon; | ||||||
427 | use Dancer2; | ||||||
428 | |||||||
429 | sub new { | ||||||
430 | my $class = shift; | ||||||
431 | debug "Loading up $class"; | ||||||
432 | my $self = { | ||||||
433 | dictionary => { | ||||||
434 | en => { | ||||||
435 | 'TRY' => 'Try', | ||||||
436 | }, | ||||||
437 | it => { | ||||||
438 | 'TRY' => 'Prova', | ||||||
439 | }, | ||||||
440 | } | ||||||
441 | }; | ||||||
442 | bless $self, $class; | ||||||
443 | } | ||||||
444 | |||||||
445 | sub dictionary { | ||||||
446 | return shift->{dictionary}; | ||||||
447 | } | ||||||
448 | |||||||
449 | sub try_to_translate { | ||||||
450 | my ($self, $string) = @_; | ||||||
451 | my $lang = session('lang') || var('lang'); | ||||||
452 | return $string unless $lang; | ||||||
453 | return $string unless $self->dictionary->{$lang}; | ||||||
454 | my $tr = $self->dictionary->{$lang}->{$string}; | ||||||
455 | defined $tr ? return $tr : return $string; | ||||||
456 | } | ||||||
457 | |||||||
458 | 1; | ||||||
459 | |||||||
460 | Optionally, you can pass the options to instantiate the class in the | ||||||
461 | configuration. Like this: | ||||||
462 | |||||||
463 | engines: | ||||||
464 | template: | ||||||
465 | template_flute: | ||||||
466 | i18n: | ||||||
467 | class: MyApp::Lexicon | ||||||
468 | method: localize | ||||||
469 | options: | ||||||
470 | append: 'X' | ||||||
471 | prepend: 'Y' | ||||||
472 | lexicon: 'path/to/po/files' | ||||||
473 | |||||||
474 | This will call | ||||||
475 | |||||||
476 | MyApp::Lexicon->new(append => 'X', prepend => 'Y', lexicon => 'path/to/po/files'); | ||||||
477 | |||||||
478 | when the engine is initialized, and will call the C |
||||||
479 | on it to get the translations. | ||||||
480 | |||||||
481 | =head2 DEBUG TOOLS | ||||||
482 | |||||||
483 | If you set C |
||||||
484 | will run a check (using the L |
||||||
485 | C |
||||||
486 | of the specifications which are not bound to any HTML elements. | ||||||
487 | |||||||
488 | In this case a debug message is issued (so keep in mind that with | ||||||
489 | higher logging level you are not going to see it). | ||||||
490 | |||||||
491 | Example configuration: | ||||||
492 | |||||||
493 | engines: | ||||||
494 | template: | ||||||
495 | template_flute: | ||||||
496 | check_dangling: 1 | ||||||
497 | |||||||
498 | When the environment is set to C |
||||||
499 | on by default. You can silence the logs by setting: | ||||||
500 | |||||||
501 | engines: | ||||||
502 | template: | ||||||
503 | template_flute: | ||||||
504 | disable_check_dangling: 1 | ||||||
505 | |||||||
506 | =head2 FORMS | ||||||
507 | |||||||
508 | Dancers::Template::TemplateFlute has a form plugin | ||||||
509 | L |
||||||
510 | L |
||||||
511 | |||||||
512 | The token C | ||||||
513 | L |
||||||
514 | L |
||||||
515 | |||||||
516 | =head3 Typical usage for a single form. | ||||||
517 | |||||||
518 | =head4 XML Specification | ||||||
519 | |||||||
520 | |
||||||
521 | |||||||
522 | |
||||||
523 | |
||||||
524 | |
||||||
525 | |||||||
526 | |||||||
527 | |||||||
528 | =head4 HTML | ||||||
529 | |||||||
530 | |||||||
531 | |||||||
532 | Info |
||||||
533 | |
||||||
534 | |
||||||
535 | |||||||
536 | |||||||
537 | |||||||
538 | |
||||||
539 | |||||||
540 | |||||||
541 | |||||||
542 | |
||||||
543 | |||||||
544 | |||||||
545 | |||||||
546 | |
||||||
547 | |||||||
548 | |||||||
549 | |||||||
550 | |||||||
551 | |||||||
552 | |||||||
553 | =head4 Code | ||||||
554 | |||||||
555 | any [qw/get post/] => '/register' => sub { | ||||||
556 | my $form = request->is_post | ||||||
557 | ? form('registration', source => 'body') | ||||||
558 | : form('registration', source => 'session' ); | ||||||
559 | my %values = %{$form->values}; | ||||||
560 | # VALIDATE, filter, etc. the values | ||||||
561 | template register => {form => $form }; | ||||||
562 | }; | ||||||
563 | |||||||
564 | =head3 Usage example for multiple forms | ||||||
565 | |||||||
566 | =head4 Specification | ||||||
567 | |||||||
568 | |
||||||
569 | |||||||
570 | |
||||||
571 | |
||||||
572 | |
||||||
573 | |||||||
574 | |||||||
575 | |
||||||
576 | |
||||||
577 | |||||||
578 | |||||||
579 | |||||||
580 | =head4 HTML | ||||||
581 | |||||||
582 | Register |
||||||
583 | |||||||
584 | |||||||
585 | Info |
||||||
586 | |
||||||
587 | |
||||||
588 | |||||||
589 | |||||||
590 | |||||||
591 | |
||||||
592 | |||||||
593 | |||||||
594 | |||||||
595 | |
||||||
596 | |||||||
597 | |||||||
598 | |||||||
599 | |
||||||
600 | |||||||
601 | |||||||
602 | |||||||
603 | |||||||
604 | |||||||
605 | Login |
||||||
606 | |||||||
607 | |||||||
608 | Info |
||||||
609 | |
||||||
610 | |
||||||
611 | |||||||
612 | |||||||
613 | |||||||
614 | |
||||||
615 | |||||||
616 | |||||||
617 | |||||||
618 | |
||||||
619 | |||||||
620 | |||||||
621 | |||||||
622 | |||||||
623 | |||||||
624 | |||||||
625 | |||||||
626 | =head4 Code | ||||||
627 | |||||||
628 | any [qw/get post/] => '/multiple' => sub { | ||||||
629 | my ( $login_form, $registration_form ); | ||||||
630 | debug to_dumper({params}); | ||||||
631 | |||||||
632 | if (params->{login}) { | ||||||
633 | $login_form = form('logintest', source => 'parameters'); | ||||||
634 | my %vals = %{$login->values}; | ||||||
635 | # VALIDATE %vals here | ||||||
636 | } | ||||||
637 | else { | ||||||
638 | # pick from session | ||||||
639 | $login_form = form('logintest', source => 'session'); | ||||||
640 | } | ||||||
641 | |||||||
642 | if (params->{register}) { | ||||||
643 | $registration_form = form('registrationtest', source => 'parameters'); | ||||||
644 | my %vals = %{$registration->values}; | ||||||
645 | # VALIDATE %vals here | ||||||
646 | } | ||||||
647 | else { | ||||||
648 | # pick from session | ||||||
649 | $registration_form = form('registrationtest', source => 'session'); | ||||||
650 | } | ||||||
651 | template multiple => { form => [ $login_form, $registration_form ] }; | ||||||
652 | }; | ||||||
653 | |||||||
654 | =head1 METHODS | ||||||
655 | |||||||
656 | =head2 default_tmpl_ext | ||||||
657 | |||||||
658 | Returns default template extension. | ||||||
659 | |||||||
660 | =head2 render TEMPLATE TOKENS | ||||||
661 | |||||||
662 | Renders template TEMPLATE with values from TOKENS. | ||||||
663 | |||||||
664 | =head1 SEE ALSO | ||||||
665 | |||||||
666 | L |
||||||
667 | |||||||
668 | =head1 AUTHOR | ||||||
669 | |||||||
670 | Author of the original Dancer module: | ||||||
671 | |||||||
672 | Stefan Hornburg (Racke), C<< |
||||||
673 | |||||||
674 | Conversion to Dancer2: | ||||||
675 | |||||||
676 | Peter Mottram (SysPete), C<< |
||||||
677 | |||||||
678 | Author of the original version of this Dancer2 module: | ||||||
679 | |||||||
680 | William Carr (mrmaloof), C<< |
||||||
681 | |||||||
682 | =head1 BUGS | ||||||
683 | |||||||
684 | Please report any bugs or feature requests via the GitHub issue tracker at: | ||||||
685 | L |
||||||
686 | |||||||
687 | =head1 SUPPORT | ||||||
688 | |||||||
689 | You can find documentation for this module with the perldoc command. | ||||||
690 | |||||||
691 | perldoc Dancer2::Template::TemplateFlute | ||||||
692 | |||||||
693 | You can also look for information at: | ||||||
694 | |||||||
695 | =over 4 | ||||||
696 | |||||||
697 | =item * AnnoCPAN: Annotated CPAN documentation | ||||||
698 | |||||||
699 | L |
||||||
700 | |||||||
701 | =item * CPAN Ratings | ||||||
702 | |||||||
703 | L |
||||||
704 | |||||||
705 | =item * meta::cpan | ||||||
706 | |||||||
707 | L |
||||||
708 | |||||||
709 | =back | ||||||
710 | |||||||
711 | =head1 LICENSE AND COPYRIGHT | ||||||
712 | |||||||
713 | Copyright 2011-2016 Stefan Hornburg (Racke) |
||||||
714 | |||||||
715 | This program is free software; you can redistribute it and/or modify it | ||||||
716 | under the terms of either: the GNU General Public License as published | ||||||
717 | by the Free Software Foundation; or the Artistic License. | ||||||
718 | |||||||
719 | See http://dev.perl.org/licenses/ for more information. | ||||||
720 | |||||||
721 | =cut | ||||||
722 | |||||||
723 | 1; |