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