| 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; |