1Mojolicious::Guides::ReUnsdeerriCnogn(t3r)ibuted Perl DoMcoujmoelnitcaitoiuosn::Guides::Rendering(3)
2
3
4
6 Mojolicious::Guides::Rendering - Rendering content
7
9 This document explains content generation with the Mojolicious
10 renderer.
11
13 Essentials every Mojolicious developer should know.
14
15 Renderer
16 The renderer is a tiny black box turning stash data into actual
17 responses utilizing multiple template systems and data encoding
18 modules.
19
20 {text => 'Hello.'} -> 200 OK, text/html, 'Hello.'
21 {json => {x => 3}} -> 200 OK, application/json, '{"x":3}'
22 {text => 'Oops.', status => '410'} -> 410 Gone, text/html, 'Oops.'
23
24 Templates can be automatically detected if enough information is
25 provided by the developer or routes. Template names are expected to
26 follow the "template.format.handler" scheme, with "template" defaulting
27 to "controller/action" or the route name, "format" defaulting to "html"
28 and "handler" to "ep".
29
30 {controller => 'users', action => 'list'} -> 'users/list.html.ep'
31 {template => 'foo', format => 'txt'} -> 'foo.txt.ep'
32 {template => 'foo', handler => 'epl'} -> 'foo.html.epl'
33
34 The "controller" value gets converted from "CamelCase" to "snake_case"
35 using "decamelize" in Mojo::Util and "-" characters replaced with "/".
36
37 {controller => 'My::Users', action => 'add'} -> 'my/users/add.html.ep'
38 {controller => 'my-users', action => 'show'} -> 'my/users/show.html.ep'
39
40 All templates should be in the "templates" directories of the
41 application, which can be customized with "paths" in
42 Mojolicious::Renderer, or one of the the "DATA" sections from "classes"
43 in Mojolicious::Renderer.
44
45 __DATA__
46
47 @@ time.html.ep
48 % use Time::Piece;
49 % my $now = localtime;
50 <!DOCTYPE html>
51 <html>
52 <head><title>Time</title></head>
53 <body>The time is <%= $now->hms %>.</body>
54 </html>
55
56 @@ hello.txt.ep
57 ...
58
59 The renderer can be easily extended to support additional template
60 systems with plugins, but more about that later.
61
62 Embedded Perl
63 Mojolicious includes a minimalistic but very powerful template system
64 out of the box called Embedded Perl or "ep" for short. It is based on
65 Mojo::Template and allows the embedding of Perl code right into actual
66 content using a small set of special tags and line start characters.
67 For all templates strict, warnings, utf8 and Perl 5.16 features are
68 automatically enabled.
69
70 <% Perl code %>
71 <%= Perl expression, replaced with XML escaped result %>
72 <%== Perl expression, replaced with result %>
73 <%# Comment, useful for debugging %>
74 <%% Replaced with "<%", useful for generating templates %>
75 % Perl code line, treated as "<% line =%>" (explained later)
76 %= Perl expression line, treated as "<%= line %>"
77 %== Perl expression line, treated as "<%== line %>"
78 %# Comment line, useful for debugging
79 %% Replaced with "%", useful for generating templates
80
81 Tags and lines work pretty much the same, but depending on context one
82 will usually look a bit better. Semicolons get automatically appended
83 to all expressions.
84
85 <% my $i = 10; %>
86 <ul>
87 <% for my $j (1 .. $i) { %>
88 <li>
89 <%= $j %>
90 </li>
91 <% } %>
92 </ul>
93
94 % my $i = 10;
95 <ul>
96 % for my $j (1 .. $i) {
97 <li>
98 %= $j
99 </li>
100 % }
101 </ul>
102
103 Aside from differences in whitespace handling, both examples generate
104 similar Perl code, a naive translation could look like this.
105
106 my $output = '';
107 my $i = 10;
108 $output .= '<ul>';
109 for my $j (1 .. $i) {
110 $output .= '<li>';
111 $output .= xml_escape scalar + $j;
112 $output .= '</li>';
113 }
114 $output .= '</ul>';
115 return $output;
116
117 An additional equal sign can be used to disable escaping of the
118 characters "<", ">", "&", "'" and """ in results from Perl expressions,
119 which is the default to prevent XSS attacks against your application.
120
121 <%= 'I ♥ Mojolicious!' %>
122 <%== '<p>I ♥ Mojolicious!</p>' %>
123
124 Only Mojo::ByteStream objects are excluded from automatic escaping.
125
126 <%= b('<p>I ♥ Mojolicious!</p>') %>
127
128 Whitespace characters around tags can be trimmed by adding an
129 additional equal sign to the end of a tag.
130
131 <% for (1 .. 3) { %>
132 <%= 'Trim all whitespace characters around this expression' =%>
133 <% } %>
134
135 Newline characters can be escaped with a backslash.
136
137 This is <%= 1 + 1 %> a\
138 single line
139
140 And a backslash in front of a newline character can be escaped with
141 another backslash.
142
143 This will <%= 1 + 1 %> result\\
144 in multiple\\
145 lines
146
147 A newline character gets appended automatically to every template,
148 unless the last character is a backslash. And empty lines at the end of
149 a template are ignored.
150
151 There is <%= 1 + 1 %> no newline at the end here\
152
153 At the beginning of the template, stash values that don't have invalid
154 characters in their name get automatically initialized as normal
155 variables, and the controller object as both $self and $c.
156
157 $c->stash(name => 'tester');
158
159 Hello <%= $name %> from <%= $c->tx->remote_address %>.
160
161 A prefix like "myapp.*" is commonly used for stash values that you
162 don't want to expose in templates.
163
164 $c->stash('myapp.name' => 'tester');
165
166 There are also many helper functions available, but more about that
167 later.
168
169 <%= dumper {foo => 'bar'} %>
170
172 Most commonly used features every Mojolicious developer should know
173 about.
174
175 Automatic rendering
176 The renderer can be manually started by calling the method "render" in
177 Mojolicious::Controller, but that's usually not necessary, because it
178 will get automatically called if nothing has been rendered after the
179 router finished its work. This also means you can have routes pointing
180 only to templates without actual actions.
181
182 $c->render;
183
184 There is one big difference though, by calling it manually you can make
185 sure that templates use the current controller object, and not the
186 default controller specified with the attribute "controller_class" in
187 Mojolicious.
188
189 $c->render_later;
190
191 You can also disable automatic rendering with the method "render_later"
192 in Mojolicious::Controller, which can be very useful to delay rendering
193 when a non-blocking operation has to be performed first.
194
195 Rendering templates
196 The renderer will always try to detect the right template, but you can
197 also use the "template" stash value to render a specific one.
198 Everything before the last slash will be interpreted as the
199 subdirectory path in which to find the template.
200
201 # foo/bar/baz.*.*
202 $c->render(template => 'foo/bar/baz');
203
204 Choosing a specific "format" and "handler" is just as easy.
205
206 # foo/bar/baz.txt.epl
207 $c->render(template => 'foo/bar/baz', format => 'txt', handler => 'epl');
208
209 Because rendering a specific template is the most common task it also
210 has a shortcut.
211
212 $c->render('foo/bar/baz');
213
214 If you're not sure in advance if a template actually exists, you can
215 also use the method "render_maybe" in Mojolicious::Controller to try
216 multiple alternatives.
217
218 $c->render_maybe('localized/baz') or $c->render('foo/bar/baz');
219
220 Rendering to strings
221 Sometimes you might want to use the rendered result directly instead of
222 generating a response, for example, to send emails, this can be done
223 with "render_to_string" in Mojolicious::Controller.
224
225 my $html = $c->render_to_string('mail');
226
227 No encoding will be performed, making it easy to reuse the result in
228 other templates or to generate binary data.
229
230 my $pdf = $c->render_to_string('invoice', format => 'pdf');
231 $c->render(data => $pdf, format => 'pdf');
232
233 All arguments passed will get localized automatically and are only
234 available during this render operation.
235
236 Template variants
237 To make your application look great on many different devices you can
238 also use the "variant" stash value to choose between different variants
239 of your templates.
240
241 # foo/bar/baz.html+phone.ep
242 # foo/bar/baz.html.ep
243 $c->render('foo/bar/baz', variant => 'phone');
244
245 This can be done very liberally since it only applies when a template
246 with the correct name actually exists and falls back to the generic one
247 otherwise.
248
249 Rendering inline templates
250 Some renderers such as "ep" allow templates to be passed "inline".
251
252 $c->render(inline => 'The result is <%= 1 + 1 %>.');
253
254 Since auto-detection depends on a path you might have to supply a
255 "handler" too.
256
257 $c->render(inline => "<%= shift->param('foo') %>", handler => 'epl');
258
259 Rendering text
260 Characters can be rendered to bytes with the "text" stash value, the
261 given content will be automatically encoded with "encoding" in
262 Mojolicious::Renderer.
263
264 $c->render(text => 'I ♥ Mojolicious!');
265
266 Rendering data
267 Bytes can be rendered with the "data" stash value, no encoding will be
268 performed.
269
270 $c->render(data => $bytes);
271
272 Rendering JSON
273 The "json" stash value allows you to pass Perl data structures to the
274 renderer which get directly encoded to JSON with Mojo::JSON.
275
276 $c->render(json => {foo => [1, 'test', 3]});
277
278 Status code
279 Response status codes can be changed with the "status" stash value.
280
281 $c->render(text => 'Oops.', status => 500);
282
283 Content type
284 The "Content-Type" header of the response is actually based on the MIME
285 type mapping of the "format" stash value.
286
287 # Content-Type: text/plain
288 $c->render(text => 'Hello.', format => 'txt');
289
290 # Content-Type: image/png
291 $c->render(data => $bytes, format => 'png');
292
293 These mappings can be easily extended or changed with "types" in
294 Mojolicious.
295
296 # Add new MIME type
297 $app->types->type(md => 'text/markdown');
298
299 Stash data
300 Any of the native Perl data types can be passed to templates as
301 references through the "stash" in Mojolicious::Controller.
302
303 $c->stash(description => 'web framework');
304 $c->stash(frameworks => ['Catalyst', 'Mojolicious', 'mojo.js']);
305 $c->stash(spinoffs => {minion => 'job queue'});
306
307 %= $description
308 %= $frameworks->[1]
309 %= $spinoffs->{minion}
310
311 Since everything is just Perl normal control structures just work.
312
313 % for my $framework (@$frameworks) {
314 <%= $framework %> is a <%= $description %>.
315 % }
316
317 % if (my $description = $spinoffs->{minion}) {
318 Minion is a <%= $description %>.
319 % }
320
321 For templates that might get rendered in different ways and where
322 you're not sure if a stash value will actually be set, you can just use
323 the helper "stash" in Mojolicious::Plugin::DefaultHelpers.
324
325 % if (my $spinoffs = stash 'spinoffs') {
326 Minion is a <%= $spinoffs->{minion} %>.
327 % }
328
329 Helpers
330 Helpers are little functions you can use in templates as well as
331 application and controller code.
332
333 # Template
334 %= dumper [1, 2, 3]
335
336 # Application
337 my $serialized = $app->dumper([1, 2, 3]);
338
339 # Controller
340 my $serialized = $c->dumper([1, 2, 3]);
341
342 We differentiate between default helpers, which are more general
343 purpose like "dumper" in Mojolicious::Plugin::DefaultHelpers, and tag
344 helpers like "link_to" in Mojolicious::Plugin::TagHelpers, which are
345 template specific and mostly used to generate HTML tags.
346
347 %= link_to Mojolicious => 'https://mojolicious.org'
348
349 In controllers you can also use the method "helpers" in
350 Mojolicious::Controller to fully qualify helper calls and ensure that
351 they don't conflict with existing methods you may already have.
352
353 my $serialized = $c->helpers->dumper([1, 2, 3]);
354
355 A list of all built-in helpers can be found in
356 Mojolicious::Plugin::DefaultHelpers and
357 Mojolicious::Plugin::TagHelpers.
358
359 Static files
360 Static files are automatically served from the "public" directories of
361 the application, which can be customized with "paths" in
362 Mojolicious::Static, or one of the "DATA" sections from "classes" in
363 Mojolicious::Static. And if that's not enough you can also serve them
364 manually with "reply->static" in Mojolicious::Plugin::DefaultHelpers
365 and "reply->file" in Mojolicious::Plugin::DefaultHelpers.
366
367 use Mojolicious::Lite -signatures;
368
369 get '/' => sub ($c) {
370 $c->reply->static('index.html');
371 };
372
373 get '/some_download' => sub ($c) {
374 $c->res->headers->content_disposition('attachment; filename=bar.png;');
375 $c->reply->static('foo/bar.png');
376 };
377
378 get '/leak' => sub ($c) {
379 $c->reply->file('/etc/passwd');
380 };
381
382 app->start;
383
384 Static assets
385 While Mojolicious does not have any special support for frontend
386 frameworks like Vue.js <https://vuejs.org> and React
387 <https://reactjs.org>, the "public/assets" directory is reserved for
388 static assets created by bundlers like Webpack <https://webpack.js.org>
389 and Rollup.js <https://rollupjs.org> ahead of time. Asset files can be
390 of any type, they just have to follow the
391 "[name].[checksum].[extensions]" naming scheme, like
392 "myapp.ab1234cd5678ef.js". You can then use "url_for_asset" in
393 Mojolicious::Controller or "asset_tag" in
394 Mojolicious::Plugin::TagHelpers to generate URLs without having to know
395 the checksum.
396
397 # "/assets/myapp.ab1234cd5678ef.js"
398 $c->url_for_asset('/myapp.js');
399
400 # "<script src="/assets/myapp.ab1234cd5678ef.js"></script>"
401 $c->asset_tag('/myapp.js');
402
403 If your application runs in "development" mode, all assets will be
404 served with a "Cache-Control: no-cache" header, to speed up development
405 by preventing browser caching. Additionally all assets following the
406 "[name].development.[extensions]" naming scheme, like
407 "myapp.development.js", have a higher precedence than assets with
408 checksums. That way you can just overwrite your assets during
409 development, instead of having to manually delete them each time they
410 are rebuilt with a different checksum.
411
412 # "/assets/foo/bar.development.js"
413 $c->url_for_asset('/foo/bar.js');
414
415 Webpack configuration <https://webpack.js.org/configuration/> example
416 ("webpack.config.js"):
417
418 import Path from '@mojojs/path';
419
420 const isDev = process.env.MOJO_MODE === 'development';
421
422 export default {
423 output: {
424 filename: isDev ? '[name].development.js' : '[name].[chunkhash].js',
425 path: Path.currentFile().sibling('public', 'assets').toString(),
426 publicPath: ''
427 },
428
429 // Add your own rules and entry point here
430 };
431
432 Rollup configuration <https://rollupjs.org/guide/en/#configuration-
433 files> example ("rollup.config.js"):
434
435 import Path from '@mojojs/path';
436
437 const isDev = process.env.MOJO_MODE === 'development';
438
439 export default {
440 output: {
441 entryFileNames: isDev ? '[name].development.[ext]' : '[name].[hash].[ext]',
442 dir: Path.currentFile().sibling('public', 'assets').toString(),
443 format: 'iife'
444 },
445
446 // Add your own rules and entry point here
447 };
448
449 Everything else is up to your bundler of choice, so you need to consult
450 its documentation for further information. And where you keep your
451 asset sources, such as ".vue" and ".jsx" files, is not important, as
452 long as your bundler can find them. Using a directory named "assets" or
453 "frontend" in your application root directory is a good best practice
454 though.
455
456 Content negotiation
457 For resources with different representations and that require truly
458 RESTful content negotiation you can also use "respond_to" in
459 Mojolicious::Plugin::DefaultHelpers instead of "render" in
460 Mojolicious::Controller.
461
462 # /hello (Accept: application/json) -> "json"
463 # /hello (Accept: application/xml) -> "xml"
464 # /hello.json -> "json"
465 # /hello.xml -> "xml"
466 # /hello?_format=json -> "json"
467 # /hello?_format=xml -> "xml"
468 $c->respond_to(
469 json => {json => {hello => 'world'}},
470 xml => {text => '<hello>world</hello>'}
471 );
472
473 The best possible representation will be automatically selected from
474 the "_format" "GET"/"POST" parameter, "format" stash value or "Accept"
475 request header and stored in the "format" stash value. To change MIME
476 type mappings for the "Accept" request header or the "Content-Type"
477 response header you can use "types" in Mojolicious.
478
479 $c->respond_to(
480 json => {json => {hello => 'world'}},
481 html => sub {
482 $c->content_for(head => '<meta name="author" content="sri">');
483 $c->render(template => 'hello', message => 'world')
484 }
485 );
486
487 Callbacks can be used for representations that are too complex to fit
488 into a single render call.
489
490 # /hello (Accept: application/json) -> "json"
491 # /hello (Accept: text/html) -> "html"
492 # /hello (Accept: image/png) -> "any"
493 # /hello.json -> "json"
494 # /hello.html -> "html"
495 # /hello.png -> "any"
496 # /hello?_format=json -> "json"
497 # /hello?_format=html -> "html"
498 # /hello?_format=png -> "any"
499 $c->respond_to(
500 json => {json => {hello => 'world'}},
501 html => {template => 'hello', message => 'world'},
502 any => {text => '', status => 204}
503 );
504
505 And if no viable representation could be found, the "any" fallback will
506 be used or an empty 204 response rendered automatically.
507
508 # /hello -> "html"
509 # /hello (Accept: text/html) -> "html"
510 # /hello (Accept: text/xml) -> "xml"
511 # /hello (Accept: text/plain) -> undef
512 # /hello.html -> "html"
513 # /hello.xml -> "xml"
514 # /hello.txt -> undef
515 # /hello?_format=html -> "html"
516 # /hello?_format=xml -> "xml"
517 # /hello?_format=txt -> undef
518 if (my $format = $c->accepts('html', 'xml')) {
519 ...
520 }
521
522 For even more advanced negotiation logic you can also use the helper
523 "accepts" in Mojolicious::Plugin::DefaultHelpers.
524
525 Rendering "exception" and "not_found" pages
526 By now you've probably already encountered the built-in 404 (Not Found)
527 and 500 (Server Error) pages, that get rendered automatically when you
528 make a mistake. Those are fallbacks for when your own exception
529 handling fails, which can be especially helpful during development. You
530 can also render them manually with the helpers "reply->exception" in
531 Mojolicious::Plugin::DefaultHelpers and "reply->not_found" in
532 Mojolicious::Plugin::DefaultHelpers.
533
534 use Mojolicious::Lite -signatures;
535 use Scalar::Util qw(looks_like_number);
536
537 get '/divide/:dividend/by/:divisor' => sub ($c) {
538
539 my $dividend = $c->param('dividend');
540 my $divisor = $c->param('divisor');
541
542 # 404
543 return $c->reply->not_found unless looks_like_number $dividend && looks_like_number $divisor;
544
545 # 500
546 return $c->reply->exception('Division by zero!') if $divisor == 0;
547
548 # 200
549 $c->render(text => $dividend / $divisor);
550 };
551
552 app->start;
553
554 To change the HTTP status code of the exception, you can use "rendered"
555 in Mojolicious::Controller.
556
557 return $c->reply->exception('Division by zero!')->rendered(400) if $divisor == 0;
558
559 You can also change the templates of those pages, since you most likely
560 want to show your users something more closely related to your
561 application in production. The renderer will always try to find
562 "exception.$mode.$format.*" or "not_found.$mode.$format.*" before
563 falling back to the built-in default templates.
564
565 use Mojolicious::Lite;
566
567 get '/dies' => sub { die 'Intentional error' };
568
569 app->start;
570 __DATA__
571
572 @@ exception.production.html.ep
573 <!DOCTYPE html>
574 <html>
575 <head><title>Server error</title></head>
576 <body>
577 <h1>Exception</h1>
578 <p><%= $exception->message %></p>
579 <h1>Stash</h1>
580 <pre><%= dumper $snapshot %></pre>
581 </body>
582 </html>
583
584 The default exception format is "html", but that can be changed at
585 application and controller level. By default there are handlers for
586 "html", "txt" and "json" available. There are also various exception
587 helpers in Mojolicious::Plugin::DefaultHelpers for you to overload to
588 change the default behavior.
589
590 use Mojolicious::Lite -signatures;
591
592 app->exception_format('json');
593
594 get '/json' => sub ($c) {
595 die 'Just a test';
596 };
597
598 get '/txt' => sub ($c) {
599 $c->exception_format('txt');
600 die 'Just a test';
601 };
602
603 app->start;
604
605 The hook "before_render" in Mojolicious makes even more advanced
606 customizations possible by allowing you to intercept and modify the
607 arguments passed to the renderer.
608
609 use Mojolicious::Lite -signatures;
610
611 hook before_render => sub ($c, $args) {
612
613 # Make sure we are rendering the exception template
614 return unless my $template = $args->{template};
615 return unless $template eq 'exception';
616
617 # Switch to JSON rendering if content negotiation allows it
618 return unless $c->accepts('json');
619 $args->{json} = {exception => $c->stash('exception')};
620 };
621
622 get '/' => sub { die "This sho...ALL GLORY TO THE HYPNOTOAD!\n" };
623
624 app->start;
625
626 Layouts
627 Most of the time when using "ep" templates you will want to wrap your
628 generated content in an HTML skeleton, thanks to layouts that's
629 absolutely trivial.
630
631 use Mojolicious::Lite;
632
633 get '/' => {template => 'foo/bar'};
634
635 app->start;
636 __DATA__
637
638 @@ foo/bar.html.ep
639 % layout 'mylayout';
640 Hello World!
641
642 @@ layouts/mylayout.html.ep
643 <!DOCTYPE html>
644 <html>
645 <head><title>MyApp</title></head>
646 <body><%= content %></body>
647 </html>
648
649 You just select the right layout template with the helper "layout" in
650 Mojolicious::Plugin::DefaultHelpers and place the result of the current
651 template with the helper "content" in
652 Mojolicious::Plugin::DefaultHelpers. You can also pass along normal
653 stash values to the "layout" helper.
654
655 use Mojolicious::Lite;
656
657 get '/' => {template => 'foo/bar'};
658
659 app->start;
660 __DATA__
661
662 @@ foo/bar.html.ep
663 % layout 'mylayout', title => 'Hi there';
664 Hello World!
665
666 @@ layouts/mylayout.html.ep
667 <!DOCTYPE html>
668 <html>
669 <head><title><%= $title %></title></head>
670 <body><%= content %></body>
671 </html>
672
673 Instead of the "layout" helper you could also just use the "layout"
674 stash value, or call "render" in Mojolicious::Controller with the
675 "layout" argument.
676
677 $c->render(template => 'mytemplate', layout => 'mylayout');
678
679 To set a "layout" stash value application-wide you can use "defaults"
680 in Mojolicious.
681
682 $app->defaults(layout => 'mylayout');
683
684 Layouts can also be used with "render_to_string" in
685 Mojolicious::Controller, but the "layout" value needs to be passed as a
686 render argument (not a stash value).
687
688 my $html = $c->render_to_string('reminder', layout => 'mail');
689
690 Partial templates
691 You can break up bigger templates into smaller, more manageable chunks.
692 These partial templates can also be shared with other templates. Just
693 use the helper "include" in Mojolicious::Plugin::DefaultHelpers to
694 include one template into another.
695
696 use Mojolicious::Lite;
697
698 get '/' => {template => 'foo/bar'};
699
700 app->start;
701 __DATA__
702
703 @@ foo/bar.html.ep
704 <!DOCTYPE html>
705 <html>
706 %= include '_header', title => 'Howdy'
707 <body>Bar</body>
708 </html>
709
710 @@ _header.html.ep
711 <head><title><%= $title %></title></head>
712
713 You can name partial templates however you like, but a leading
714 underscore is a commonly used naming convention.
715
716 Reusable template blocks
717 It's never fun to repeat yourself, that's why you can build reusable
718 template blocks in "ep" that work very similar to normal Perl
719 functions, with the "begin" and "end" keywords. Just be aware that both
720 keywords are part of the surrounding tag and not actual Perl code, so
721 there can only be whitespace after "begin" and before "end".
722
723 use Mojolicious::Lite;
724
725 get '/' => 'welcome';
726
727 app->start;
728 __DATA__
729
730 @@ welcome.html.ep
731 <% my $block = begin %>
732 % my $name = shift;
733 Hello <%= $name %>.
734 <% end %>
735 <%= $block->('Wolfgang') %>
736 <%= $block->('Baerbel') %>
737
738 A naive translation of the template to Perl code could look like this.
739
740 my $output = '';
741 my $block = sub ($name) {
742 my $output = '';
743 $output .= 'Hello ';
744 $output .= xml_escape scalar + $name;
745 $output .= '.';
746 return Mojo::ByteStream->new($output);
747 };
748 $output .= xml_escape scalar + $block->('Wolfgang');
749 $output .= xml_escape scalar + $block->('Baerbel');
750 return $output;
751
752 While template blocks cannot be shared between templates, they are most
753 commonly used to pass parts of a template to helpers.
754
755 Adding helpers
756 You should always try to keep your actions small and reuse as much code
757 as possible. Helpers make this very easy, they get passed the current
758 controller object as first argument, and you can use them to do pretty
759 much anything an action could do.
760
761 use Mojolicious::Lite -signatures;
762
763 helper debug => sub ($c, $str) {
764 $c->app->log->debug($str);
765 };
766
767 get '/' => sub ($c) {
768 $c->debug('Hello from an action!');
769 } => 'index';
770
771 app->start;
772 __DATA__
773
774 @@ index.html.ep
775 % debug 'Hello from a template!';
776
777 Helpers can also accept template blocks as last argument, this for
778 example, allows very pleasant to use tag helpers and filters. Wrapping
779 the helper result into a Mojo::ByteStream object can prevent accidental
780 double escaping.
781
782 use Mojolicious::Lite -signatures;
783 use Mojo::ByteStream;
784
785 helper trim_newline => sub ($c, $block) {
786 my $result = $block->();
787 $result =~ s/\n//g;
788 return Mojo::ByteStream->new($result);
789 };
790
791 get '/' => 'index';
792
793 app->start;
794 __DATA__
795
796 @@ index.html.ep
797 %= trim_newline begin
798 Some text.
799 %= 1 + 1
800 More text.
801 % end
802
803 Similar to stash values, you can use a prefix like "myapp.*" to keep
804 helpers from getting exposed in templates as functions, and to organize
805 them into namespaces as your application grows. Every prefix
806 automatically becomes a helper that returns a proxy object containing
807 the current controller object and on which you can call the nested
808 helpers.
809
810 use Mojolicious::Lite -signatures;
811
812 helper 'cache_control.no_caching' => sub ($c) { $c->res->headers->cache_control('private, max-age=0, no-cache') };
813 helper 'cache_control.five_minutes' => sub ($c) { $c->res->headers->cache_control('public, max-age=300') };
814
815 get '/news' => sub ($c) {
816 $c->cache_control->no_caching;
817 $c->render(text => 'Always up to date.');
818 };
819
820 get '/some_older_story' => sub ($c) {
821 $c->cache_control->five_minutes;
822 $c->render(text => 'This one can be cached for a bit.');
823 };
824
825 app->start;
826
827 While helpers can also be redefined, this should only be done very
828 carefully to avoid conflicts.
829
830 Content blocks
831 The helper "content_for" in Mojolicious::Plugin::DefaultHelpers allows
832 you to pass whole blocks of content from one template to another. This
833 can be very useful when your layout has distinct sections, such as
834 sidebars, where content should be inserted by the template.
835
836 use Mojolicious::Lite;
837
838 get '/' => 'foo';
839
840 app->start;
841 __DATA__
842
843 @@ foo.html.ep
844 % layout 'mylayout';
845 % content_for header => begin
846 <meta http-equiv="Content-Type" content="text/html">
847 % end
848 <div>Hello World!</div>
849 % content_for header => begin
850 <meta http-equiv="Pragma" content="no-cache">
851 % end
852
853 @@ layouts/mylayout.html.ep
854 <!DOCTYPE html>
855 <html>
856 <head><%= content 'header' %></head>
857 <body><%= content %></body>
858 </html>
859
860 Forms
861 To build HTML forms more efficiently you can use tag helpers like
862 "form_for" in Mojolicious::Plugin::TagHelpers, which can automatically
863 select a request method for you if a route name is provided. And since
864 most browsers only allow forms to be submitted with "GET" and "POST",
865 but not request methods like "PUT" or "DELETE", they are spoofed with
866 an "_method" query parameter.
867
868 use Mojolicious::Lite -signatures;
869
870 get '/' => 'form';
871
872 # PUT /nothing
873 # POST /nothing?_method=PUT
874 put '/nothing' => sub ($c) {
875
876 # Prevent double form submission with redirect
877 my $value = $c->param('whatever');
878 $c->flash(confirmation => "We did nothing with your value ($value).");
879 $c->redirect_to('form');
880 };
881
882 app->start;
883 __DATA__
884
885 @@ form.html.ep
886 <!DOCTYPE html>
887 <html>
888 <body>
889 % if (my $confirmation = flash 'confirmation') {
890 <p><%= $confirmation %></p>
891 % }
892 %= form_for nothing => begin
893 %= text_field whatever => 'I ♥ Mojolicious!'
894 %= submit_button
895 % end
896 </body>
897 </html>
898
899 The helpers "flash" in Mojolicious::Plugin::DefaultHelpers and
900 "redirect_to" in Mojolicious::Plugin::DefaultHelpers are often used
901 together to prevent double form submission, allowing users to receive a
902 confirmation message that will vanish if they decide to reload the page
903 they've been redirected to.
904
905 Form validation
906 You can use "validation" in Mojolicious::Plugin::DefaultHelpers to
907 validate "GET" and "POST" parameters submitted to your application. All
908 unknown fields will be ignored by default, so you have to decide which
909 should be required or optional before you can perform checks on their
910 values. Every check is performed right away, so you can use the results
911 immediately to build more advanced validation logic with methods like
912 "is_valid" in Mojolicious::Validator::Validation.
913
914 use Mojolicious::Lite -signatures;
915
916 get '/' => sub ($c) {
917
918 # Check if parameters have been submitted
919 my $v = $c->validation;
920 return $c->render('index') unless $v->has_data;
921
922 # Validate parameters ("pass_again" depends on "pass")
923 $v->required('user')->size(1, 20)->like(qr/^[a-z0-9]+$/);
924 $v->required('pass_again')->equal_to('pass') if $v->optional('pass')->size(7, 500)->is_valid;
925
926 # Check if validation failed
927 return $c->render('index') if $v->has_error;
928
929 # Render confirmation
930 $c->render('thanks');
931 };
932
933 app->start;
934 __DATA__
935
936 @@ index.html.ep
937 <!DOCTYPE html>
938 <html>
939 <head>
940 <style>
941 label.field-with-error { color: #dd7e5e }
942 input.field-with-error { background-color: #fd9e7e }
943 </style>
944 </head>
945 <body>
946 %= form_for index => begin
947 %= label_for user => 'Username (required, 1-20 characters, a-z/0-9)'
948 <br>
949 %= text_field 'user', id => 'user'
950 %= submit_button
951 <br>
952 %= label_for pass => 'Password (optional, 7-500 characters)'
953 <br>
954 %= password_field 'pass', id => 'pass'
955 <br>
956 %= label_for pass_again => 'Password again (equal to the value above)'
957 <br>
958 %= password_field 'pass_again', id => 'pass_again'
959 % end
960 </body>
961 </html>
962
963 @@ thanks.html.ep
964 <!DOCTYPE html>
965 <html><body>Thank you <%= validation->param('user') %>.</body></html>
966
967 Form elements generated with tag helpers from
968 Mojolicious::Plugin::TagHelpers will automatically remember their
969 previous values and add the class "field-with-error" for fields that
970 failed validation to make styling with CSS easier.
971
972 <label class="field-with-error" for="user">
973 Username (required, only characters e-t)
974 </label>
975 <input class="field-with-error" type="text" name="user" value="sri">
976
977 For a full list of available checks see also "CHECKS" in
978 Mojolicious::Validator.
979
980 Adding form validation checks
981 Validation checks can be registered with "add_check" in
982 Mojolicious::Validator and return a false value if they were
983 successful. A true value may be used to pass along additional
984 information which can then be retrieved with "error" in
985 Mojolicious::Validator::Validation.
986
987 use Mojolicious::Lite -signatures;
988
989 # Add "range" check
990 app->validator->add_check(range => sub ($v, $name, $value, $min, $max) {
991 return $value < $min || $value > $max;
992 });
993
994 get '/' => 'form';
995
996 post '/test' => sub ($c) {
997
998 # Validate parameters with custom check
999 my $v = $c->validation;
1000 $v->required('number')->range(3, 23);
1001
1002 # Render form again if validation failed
1003 return $c->render('form') if $v->has_error;
1004
1005 # Prevent double form submission with redirect
1006 $c->flash(number => $v->param('number'));
1007 $c->redirect_to('form');
1008 };
1009
1010 app->start;
1011 __DATA__
1012
1013 @@ form.html.ep
1014 <!DOCTYPE html>
1015 <html>
1016 <body>
1017 % if (my $number = flash 'number') {
1018 <p>Thanks, the number <%= $number %> was valid.</p>
1019 % }
1020 %= form_for test => begin
1021 % if (my $err = validation->error('number')) {
1022 <p>
1023 %= 'Value is required.' if $err->[0] eq 'required'
1024 %= 'Value needs to be between 3 and 23.' if $err->[0] eq 'range'
1025 </p>
1026 % }
1027 %= text_field 'number'
1028 %= submit_button
1029 % end
1030 </body>
1031 </html>
1032
1033 Cross-site request forgery
1034 CSRF is a very common attack on web applications that trick your logged
1035 in users to submit forms they did not intend to send, with something as
1036 mundane as a link. All you have to do, to protect your users from this,
1037 is to add an additional hidden field to your forms with "csrf_field" in
1038 Mojolicious::Plugin::TagHelpers, and validate it with "csrf_protect" in
1039 Mojolicious::Validator::Validation.
1040
1041 use Mojolicious::Lite -signatures;
1042
1043 get '/' => {template => 'target'};
1044
1045 post '/' => sub ($c) {
1046
1047 # Check CSRF token
1048 my $v = $c->validation;
1049 return $c->render(text => 'Bad CSRF token!', status => 403) if $v->csrf_protect->has_error('csrf_token');
1050
1051 my $city = $v->required('city')->param('city');
1052 $c->render(text => "Low orbit ion cannon pointed at $city!") unless $v->has_error;
1053 } => 'target';
1054
1055 app->start;
1056 __DATA__
1057
1058 @@ target.html.ep
1059 <!DOCTYPE html>
1060 <html>
1061 <body>
1062 %= form_for target => begin
1063 %= csrf_field
1064 %= label_for city => 'Which city to point low orbit ion cannon at?'
1065 %= text_field 'city', id => 'city'
1066 %= submit_button
1067 %= end
1068 </body>
1069 </html>
1070
1071 For Ajax requests and the like, you can also generate a token directly
1072 with the helper "csrf_token" in Mojolicious::Plugin::DefaultHelpers,
1073 and then pass it along with the "X-CSRF-Token" request header.
1074
1076 Less commonly used and more powerful features.
1077
1078 Template inheritance
1079 Inheritance takes the layout concept above one step further, the
1080 helpers "content" in Mojolicious::Plugin::DefaultHelpers and "extends"
1081 in Mojolicious::Plugin::DefaultHelpers allow you to build skeleton
1082 templates with named blocks that child templates can override.
1083
1084 use Mojolicious::Lite;
1085
1086 # first > mylayout
1087 get '/first' => {template => 'first', layout => 'mylayout'};
1088
1089 # third > second > first > mylayout
1090 get '/third' => {template => 'third', layout => 'mylayout'};
1091
1092 app->start;
1093 __DATA__
1094
1095 @@ layouts/mylayout.html.ep
1096 <!DOCTYPE html>
1097 <html>
1098 <head><title>Hello</title></head>
1099 <body><%= content %></body>
1100 </html>
1101
1102 @@ first.html.ep
1103 %= content header => begin
1104 Default header
1105 % end
1106 <div>Hello World!</div>
1107 %= content footer => begin
1108 Default footer
1109 % end
1110
1111 @@ second.html.ep
1112 % extends 'first';
1113 % content header => begin
1114 New header
1115 % end
1116
1117 @@ third.html.ep
1118 % extends 'second';
1119 % content footer => begin
1120 New footer
1121 % end
1122
1123 This chain could go on and on to allow a very high level of template
1124 reuse.
1125
1126 Custom responses
1127 Most response content, static as well as dynamic, gets served through
1128 Mojo::Asset::File and Mojo::Asset::Memory objects. For somewhat static
1129 content, like cached JSON data or temporary files, you can create your
1130 own and use the helper "reply->asset" in
1131 Mojolicious::Plugin::DefaultHelpers to serve them while allowing
1132 content negotiation to be performed with "Range", "If-Modified-Since"
1133 and "If-None-Match" headers.
1134
1135 use Mojolicious::Lite -signatures;
1136 use Mojo::Asset::File;
1137
1138 get '/leak' => sub ($c) {
1139 $c->res->headers->content_type('text/plain');
1140 $c->reply->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
1141 };
1142
1143 app->start;
1144
1145 For even more control you can also just skip the helper and use
1146 "rendered" in Mojolicious::Controller to tell the renderer when you're
1147 done generating a response.
1148
1149 use Mojolicious::Lite -signatures;
1150 use Mojo::Asset::File;
1151
1152 get '/leak' => sub ($c) {
1153 $c->res->headers->content_type('text/plain');
1154 $c->res->content->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
1155 $c->rendered(200);
1156 };
1157
1158 app->start;
1159
1160 Helper plugins
1161 Some helpers might be useful enough for you to share them between
1162 multiple applications, plugins make that very simple.
1163
1164 package Mojolicious::Plugin::DebugHelper;
1165 use Mojo::Base 'Mojolicious::Plugin', -signatures;
1166
1167 sub register ($self, $app, $conf) {
1168 $app->helper(debug => sub ($c, $str) {
1169 $c->app->log->debug($str);
1170 });
1171 }
1172
1173 1;
1174
1175 The "register" method will be called when you load the plugin. And to
1176 add your helper to the application, you can use "helper" in
1177 Mojolicious.
1178
1179 use Mojolicious::Lite -signatures;
1180
1181 plugin 'DebugHelper';
1182
1183 get '/' => sub ($c) {
1184 $c->debug('It works!');
1185 $c->render(text => 'Hello!');
1186 };
1187
1188 app->start;
1189
1190 A skeleton for a full CPAN compatible plugin distribution can be
1191 automatically generated.
1192
1193 $ mojo generate plugin DebugHelper
1194
1195 And if you have a "PAUSE" account (which can be requested at
1196 <http://pause.perl.org>), you are only a few commands away from
1197 releasing it to CPAN.
1198
1199 $ perl Makefile.PL
1200 $ make test
1201 $ make manifest
1202 $ make dist
1203 $ mojo cpanify -u USER -p PASS Mojolicious-Plugin-DebugHelper-0.01.tar.gz
1204
1205 Bundling assets with plugins
1206 Assets such as templates and static files can be easily bundled with
1207 your plugins, even if you plan to release them to CPAN.
1208
1209 $ mojo generate plugin AlertAssets
1210 $ mkdir Mojolicious-Plugin-AlertAssets/lib/Mojolicious/Plugin/AlertAssets
1211 $ cd Mojolicious-Plugin-AlertAssets/lib/Mojolicious/Plugin/AlertAssets
1212 $ mkdir public
1213 $ echo 'alert("Hello World!");' > public/alertassets.js
1214 $ mkdir templates
1215 $ echo '%= javascript "/alertassets.js"' > templates/alertassets.html.ep
1216
1217 Just give them reasonably unique names, ideally based on the name of
1218 your plugin, and append their respective directories to the list of
1219 search paths when "register" is called.
1220
1221 package Mojolicious::Plugin::AlertAssets;
1222 use Mojo::Base 'Mojolicious::Plugin', -signatures;
1223
1224 use Mojo::File qw(curfile);
1225
1226 sub register ($self, $app, $conf) {
1227
1228 # Append "templates" and "public" directories
1229 my $base = curfile->sibling('AlertAssets');
1230 push @{$app->renderer->paths}, $base->child('templates')->to_string;
1231 push @{$app->static->paths}, $base->child('public')->to_string;
1232 }
1233
1234 1;
1235
1236 Both will work just like normal "templates" and "public" directories
1237 once you've installed and loaded the plugin, with slightly lower
1238 precedence.
1239
1240 use Mojolicious::Lite;
1241
1242 plugin 'AlertAssets';
1243
1244 get '/alert_me';
1245
1246 app->start;
1247 __DATA__
1248
1249 @@ alert_me.html.ep
1250 <!DOCTYPE html>
1251 <html>
1252 <head>
1253 <title>Alert me!</title>
1254 %= include 'alertassets'
1255 </head>
1256 <body>You've been alerted.</body>
1257 </html>
1258
1259 And it works just the same for assets bundled in the "DATA" section of
1260 your plugin.
1261
1262 package Mojolicious::Plugin::AlertAssets;
1263 use Mojo::Base 'Mojolicious::Plugin', -signatures;
1264
1265 sub register ($self, $app, $conf) {
1266
1267 # Append class
1268 push @{$app->renderer->classes}, __PACKAGE__;
1269 push @{$app->static->classes}, __PACKAGE__;
1270 }
1271
1272 1;
1273 __DATA__
1274
1275 @@ alertassets.js
1276 alert("Hello World!");
1277
1278 @@ alertassets.html.ep
1279 %= javascript "/alertassets.js"
1280
1281 Post-processing dynamic content
1282 While post-processing tasks are generally very easy with the hook
1283 "after_dispatch" in Mojolicious, for content generated by the renderer
1284 it is a lot more efficient to use "after_render" in Mojolicious.
1285
1286 use Mojolicious::Lite -signatures;
1287 use IO::Compress::Gzip qw(gzip);
1288
1289 hook after_render => sub ($c, $output, $format) {
1290
1291 # Check if "gzip => 1" has been set in the stash
1292 return unless $c->stash->{gzip};
1293
1294 # Check if user agent accepts gzip compression
1295 return unless ($c->req->headers->accept_encoding // '') =~ /gzip/i;
1296 $c->res->headers->append(Vary => 'Accept-Encoding');
1297
1298 # Compress content with gzip
1299 $c->res->headers->content_encoding('gzip');
1300 gzip $output, \my $compressed;
1301 $$output = $compressed;
1302 };
1303
1304 get '/' => {template => 'hello', title => 'Hello', gzip => 1};
1305
1306 app->start;
1307 __DATA__
1308
1309 @@ hello.html.ep
1310 <!DOCTYPE html>
1311 <html>
1312 <head><title><%= title %></title></head>
1313 <body>Compressed content.</body>
1314 </html>
1315
1316 If you want to compress all dynamically generated content you can also
1317 activate "compress" in Mojolicious::Renderer.
1318
1319 Streaming
1320 You don't have to render all content at once, the method "write" in
1321 Mojolicious::Controller can also be used to stream a series of smaller
1322 chunks.
1323
1324 use Mojolicious::Lite -signatures;
1325
1326 get '/' => sub ($c) {
1327
1328 # Prepare body
1329 my $body = 'Hello World!';
1330 $c->res->headers->content_length(length $body);
1331
1332 # Start writing directly with a drain callback
1333 my $drain = sub ($c) {
1334 my $chunk = substr $body, 0, 1, '';
1335 $c->write($chunk, length $body ? __SUB__ : undef);
1336 };
1337 $c->$drain;
1338 };
1339
1340 app->start;
1341
1342 The drain callback will be executed whenever the entire previous chunk
1343 of data has actually been written.
1344
1345 HTTP/1.1 200 OK
1346 Date: Sat, 13 Sep 2014 16:48:29 GMT
1347 Content-Length: 12
1348 Server: Mojolicious (Perl)
1349
1350 Hello World!
1351
1352 Instead of providing a "Content-Length" header you can also call
1353 "finish" in Mojolicious::Controller and close the connection manually
1354 once you are done.
1355
1356 use Mojolicious::Lite -signatures;
1357
1358 get '/' => sub ($c) {
1359
1360 # Prepare body
1361 my $body = 'Hello World!';
1362
1363 # Start writing directly with a drain callback
1364 my $drain = sub ($c) {
1365 my $chunk = substr $body, 0, 1, '';
1366 length $chunk ? $c->write($chunk, __SUB__) : $c->finish;
1367 };
1368 $c->$drain;
1369 };
1370
1371 app->start;
1372
1373 While this is rather inefficient, as it prevents keep-alive, it is
1374 sometimes necessary for EventSource and similar applications.
1375
1376 HTTP/1.1 200 OK
1377 Date: Sat, 13 Sep 2014 16:48:29 GMT
1378 Connection: close
1379 Server: Mojolicious (Perl)
1380
1381 Hello World!
1382
1383 Chunked transfer encoding
1384 For very dynamic content you might not know the response content length
1385 in advance, that's where the chunked transfer encoding and
1386 "write_chunk" in Mojolicious::Controller come in handy. A common use
1387 would be to send the "head" section of an HTML document to the browser
1388 in advance and speed up preloading of referenced images and
1389 stylesheets.
1390
1391 use Mojolicious::Lite -signatures;
1392
1393 get '/' => sub ($c) {
1394 $c->write_chunk('<html><head><title>Example</title></head>' => sub ($c) {
1395 $c->finish('<body>Example</body></html>');
1396 });
1397 };
1398
1399 app->start;
1400
1401 The optional drain callback ensures that all previous chunks have been
1402 written before processing continues. To end the stream you can call
1403 "finish" in Mojolicious::Controller or write an empty chunk of data.
1404
1405 HTTP/1.1 200 OK
1406 Date: Sat, 13 Sep 2014 16:48:29 GMT
1407 Transfer-Encoding: chunked
1408 Server: Mojolicious (Perl)
1409
1410 29
1411 <html><head><title>Example</title></head>
1412 1b
1413 <body>Example</body></html>
1414 0
1415
1416 Especially in combination with long inactivity timeouts this can be
1417 very useful for Comet (long polling). Due to limitations in some web
1418 servers this might not work perfectly in all deployment environments.
1419
1420 Encoding
1421 Templates stored in files are expected to be "UTF-8" by default, but
1422 that can be easily changed with "encoding" in Mojolicious::Renderer.
1423
1424 $app->renderer->encoding('koi8-r');
1425
1426 All templates from the "DATA" section are bound to the encoding of the
1427 Perl script.
1428
1429 use Mojolicious::Lite;
1430
1431 get '/heart';
1432
1433 app->start;
1434 __DATA__
1435
1436 @@ heart.html.ep
1437 I ♥ Mojolicious!
1438
1439 Base64 encoded DATA files
1440 Base64 encoded static files such as images can be easily stored in the
1441 "DATA" section of your application, similar to templates.
1442
1443 use Mojolicious::Lite;
1444
1445 get '/' => {text => 'I ♥ Mojolicious!'};
1446
1447 app->start;
1448 __DATA__
1449
1450 @@ favicon.ico (base64)
1451 ...base64 encoded image...
1452
1453 Inflating DATA templates
1454 Templates stored in files get preferred over files from the "DATA"
1455 section, this allows you to include a default set of templates in your
1456 application that the user can later customize. The command
1457 Mojolicious::Command::Author::inflate will write all templates and
1458 static files from the "DATA" section into actual files in the
1459 "templates" and "public" directories.
1460
1461 $ ./myapp.pl inflate
1462
1463 Customizing the template syntax
1464 You can easily change the whole template syntax by loading
1465 Mojolicious::Plugin::EPRenderer with a custom configuration.
1466
1467 use Mojolicious::Lite;
1468
1469 plugin EPRenderer => {
1470 name => 'mustache',
1471 template => {
1472 tag_start => '{{',
1473 tag_end => '}}'
1474 }
1475 };
1476
1477 get '/:name' => {name => 'Anonymous'} => 'index';
1478
1479 app->start;
1480 __DATA__
1481
1482 @@ index.html.mustache
1483 Hello {{= $name }}.
1484
1485 Mojo::Template contains the whole list of available options.
1486
1487 Adding your favorite template system
1488 Maybe you would prefer a different template system than "ep", which is
1489 provided by Mojolicious::Plugin::EPRenderer, and there is not already a
1490 plugin on CPAN for your favorite one. All you have to do, is to add a
1491 new "handler" with "add_handler" in Mojolicious::Renderer when
1492 "register" is called.
1493
1494 package Mojolicious::Plugin::MyRenderer;
1495 use Mojo::Base 'Mojolicious::Plugin', -signatures;
1496
1497 sub register ($self, $app, $conf) {
1498
1499 # Add "mine" handler
1500 $app->renderer->add_handler(mine => sub ($renderer, $c, $output, $options) {
1501
1502 # Check for one-time use inline template
1503 my $inline_template = $options->{inline};
1504
1505 # Check for appropriate template in "templates" directories
1506 my $template_path = $renderer->template_path($options);
1507
1508 # Check for appropriate template in DATA sections
1509 my $data_template = $renderer->get_data_template($options);
1510
1511 # This part is up to you and your template system :)
1512 ...
1513
1514 # Pass the rendered result back to the renderer
1515 $$output = 'Hello World!';
1516
1517 # Or just die if an error occurs
1518 die 'Something went wrong with the template';
1519 });
1520 }
1521
1522 1;
1523
1524 An "inline" template, if provided by the user, will be passed along
1525 with the options. You can use "template_path" in Mojolicious::Renderer
1526 to search the "templates" directories of the application, and
1527 "get_data_template" in Mojolicious::Renderer to search the "DATA"
1528 sections.
1529
1530 use Mojolicious::Lite;
1531
1532 plugin 'MyRenderer';
1533
1534 # Render an inline template
1535 get '/inline' => {inline => '...', handler => 'mine'};
1536
1537 # Render a template from the DATA section
1538 get '/data' => {template => 'test'};
1539
1540 app->start;
1541 __DATA__
1542
1543 @@ test.html.mine
1544 ...
1545
1546 Adding a handler to generate binary data
1547 By default the renderer assumes that every "handler" generates
1548 characters that need to be automatically encoded, but this can be
1549 easily disabled if you're generating bytes instead.
1550
1551 use Mojolicious::Lite -signatures;
1552 use Storable qw(nfreeze);
1553
1554 # Add "storable" handler
1555 app->renderer->add_handler(storable => sub ($renderer, $c, $output, $options) {
1556
1557 # Disable automatic encoding
1558 delete $options->{encoding};
1559
1560 # Encode data from stash value
1561 $$output = nfreeze delete $c->stash->{storable};
1562 });
1563
1564 # Set "handler" value automatically if "storable" value is set already
1565 app->hook(before_render => sub ($c, $args) {
1566 $args->{handler} = 'storable' if exists $args->{storable} || exists $c->stash->{storable};
1567 });
1568
1569 get '/' => {storable => {i => '♥ mojolicious'}};
1570
1571 app->start;
1572
1573 The hook "before_render" in Mojolicious can be used to make stash
1574 values like "storable" special, so that they no longer require a
1575 "handler" value to be set explicitly.
1576
1577 # Explicit "handler" value
1578 $c->render(storable => {i => '♥ mojolicious'}, handler => 'storable');
1579
1580 # Implicit "handler" value (with "before_render" hook)
1581 $c->render(storable => {i => '♥ mojolicious'});
1582
1584 You can continue with Mojolicious::Guides now or take a look at the
1585 Mojolicious wiki <https://github.com/mojolicious/mojo/wiki>, which
1586 contains a lot more documentation and examples by many different
1587 authors.
1588
1590 If you have any questions the documentation might not yet answer, don't
1591 hesitate to ask in the Forum <https://forum.mojolicious.org>, on Matrix
1592 <https://matrix.to/#/#mojo:matrix.org>, or IRC
1593 <https://web.libera.chat/#mojo>.
1594
1595
1596
1597perl v5.36.0 2023-01-20 Mojolicious::Guides::Rendering(3)