1Mojolicious::Guides::TuUtsoerriaClo(n3t)ributed Perl DocMuomjeonltiactiioouns::Guides::Tutorial(3)
2
3
4
6 Mojolicious::Guides::Tutorial - Get started with Mojolicious
7
9 A quick example-driven introduction to the wonders of
10 Mojolicious::Lite. Almost everything you'll learn here also applies to
11 full Mojolicious applications.
12
13 This is only the first of the Mojolicious::Guides. Other guides delve
14 deeper into topics like growing a Mojolicious::Lite prototype into a
15 well-structured Mojolicious application, routing, rendering and more.
16 It is highly encouraged that readers continue on to the remaining
17 guides after reading this one.
18
19 Hello World
20 A simple Hello World application can look like this, strict, warnings,
21 utf8 and Perl 5.10 features are automatically enabled and a few
22 functions imported, when you use Mojolicious::Lite, turning your script
23 into a full featured web application.
24
25 #!/usr/bin/env perl
26 use Mojolicious::Lite;
27
28 get '/' => sub {
29 my $c = shift;
30 $c->render(text => 'Hello World!');
31 };
32
33 app->start;
34
35 With Mojolicious::Command::Author::generate::lite_app there is also a
36 helper command to generate a small example application.
37
38 $ mojo generate lite_app myapp.pl
39
40 Commands
41 Many different commands are automatically available from the command
42 line. CGI and PSGI environments can even be detected and will usually
43 just work without commands.
44
45 $ ./myapp.pl daemon
46 Server available at http://127.0.0.1:3000
47
48 $ ./myapp.pl daemon -l http://*:8080
49 Server available at http://127.0.0.1:8080
50
51 $ ./myapp.pl cgi
52 ...CGI output...
53
54 $ ./myapp.pl get /
55 Hello World!
56
57 $ ./myapp.pl
58 ...List of available commands (or automatically detected environment)...
59
60 A call to "start" in Mojolicious ("app->start"), which starts the
61 command system, should be the last expression in your application,
62 because its return value can be significant.
63
64 # Use @ARGV to pick a command
65 app->start;
66
67 # Start the "daemon" command
68 app->start('daemon', '-l', 'http://*:8080');
69
70 Reloading
71 Your application will automatically reload itself if you start it with
72 the morbo development web server, so you don't have to restart the
73 server after every change.
74
75 $ morbo ./myapp.pl
76 Server available at http://127.0.0.1:3000
77
78 For more information about how to deploy your application see also
79 "DEPLOYMENT" in Mojolicious::Guides::Cookbook.
80
81 Routes
82 Routes are basically just fancy paths that can contain different kinds
83 of placeholders and usually lead to an action, if they match the path
84 part of the request URL. The first argument passed to all actions ($c)
85 is a Mojolicious::Controller object, containing both the HTTP request
86 and response.
87
88 use Mojolicious::Lite;
89
90 # Route leading to an action that renders some text
91 get '/foo' => sub {
92 my $c = shift;
93 $c->render(text => 'Hello World!');
94 };
95
96 app->start;
97
98 Response content is often generated by actions with "render" in
99 Mojolicious::Controller, but more about that later.
100
101 GET/POST parameters
102 All "GET" and "POST" parameters sent with the request are accessible
103 via "param" in Mojolicious::Controller.
104
105 use Mojolicious::Lite;
106
107 # /foo?user=sri
108 get '/foo' => sub {
109 my $c = shift;
110 my $user = $c->param('user');
111 $c->render(text => "Hello $user.");
112 };
113
114 app->start;
115
116 Stash and templates
117 The "stash" in Mojolicious::Controller is used to pass data to
118 templates, which can be inlined in the "DATA" section. A few stash
119 values like "template", "text" and "data" are reserved and will be used
120 by "render" in Mojolicious::Controller to decide how a response should
121 be generated.
122
123 use Mojolicious::Lite;
124
125 # Route leading to an action that renders a template
126 get '/foo' => sub {
127 my $c = shift;
128 $c->stash(one => 23);
129 $c->render(template => 'magic', two => 24);
130 };
131
132 app->start;
133 __DATA__
134
135 @@ magic.html.ep
136 The magic numbers are <%= $one %> and <%= $two %>.
137
138 For more information about templates see also "Embedded Perl" in
139 Mojolicious::Guides::Rendering.
140
141 HTTP
142 "req" in Mojolicious::Controller and "res" in Mojolicious::Controller
143 give you full access to all HTTP features and information.
144
145 use Mojolicious::Lite;
146
147 # Access request information
148 get '/agent' => sub {
149 my $c = shift;
150 my $host = $c->req->url->to_abs->host;
151 my $ua = $c->req->headers->user_agent;
152 $c->render(text => "Request by $ua reached $host.");
153 };
154
155 # Echo the request body and send custom header with response
156 post '/echo' => sub {
157 my $c = shift;
158 $c->res->headers->header('X-Bender' => 'Bite my shiny metal ass!');
159 $c->render(data => $c->req->body);
160 };
161
162 app->start;
163
164 You can test the more advanced examples right from the command line
165 with Mojolicious::Command::get.
166
167 $ ./myapp.pl get -v -M POST -c 'test' /echo
168
169 JSON
170 JSON is the most commonly used data-interchange format for web
171 services. Mojolicious loves JSON and comes with the possibly fastest
172 pure-Perl implementation Mojo::JSON built right in, which is accessible
173 through "json" in Mojo::Message as well as the reserved stash value
174 "json".
175
176 use Mojolicious::Lite;
177
178 # Modify the received JSON document and return it
179 put '/reverse' => sub {
180 my $c = shift;
181 my $hash = $c->req->json;
182 $hash->{message} = reverse $hash->{message};
183 $c->render(json => $hash);
184 };
185
186 app->start;
187
188 You can send JSON documents from the command line with
189 Mojolicious::Command::get.
190
191 $ ./myapp.pl get -M PUT -c '{"message":"Hello Mojo!"}' /reverse
192
193 Built-in "exception" and "not_found" pages
194 During development you will encounter these pages whenever you make a
195 mistake, they are gorgeous and contain a lot of valuable information
196 that will aid you in debugging your application.
197
198 use Mojolicious::Lite;
199
200 # Not found (404)
201 get '/missing' => sub { shift->render(template => 'does_not_exist') };
202
203 # Exception (500)
204 get '/dies' => sub { die 'Intentional error' };
205
206 app->start;
207
208 You can even use CSS selectors with Mojolicious::Command::get to
209 extract only the information you're actually interested in.
210
211 $ ./myapp.pl get /dies '#error'
212
213 And don't worry about revealing too much information on these pages,
214 they are only available during development, and will be replaced
215 automatically with pages that don't reveal any sensitive information in
216 a production environment.
217
218 Route names
219 All routes can have a name associated with them, this allows automatic
220 template detection and backreferencing with "url_for" in
221 Mojolicious::Controller, on which many methods and helpers like
222 "link_to" in Mojolicious::Plugin::TagHelpers rely.
223
224 use Mojolicious::Lite;
225
226 # Render the template "index.html.ep"
227 get '/' => sub {
228 my $c = shift;
229 $c->render;
230 } => 'index';
231
232 # Render the template "hello.html.ep"
233 get '/hello';
234
235 app->start;
236 __DATA__
237
238 @@ index.html.ep
239 <%= link_to Hello => 'hello' %>.
240 <%= link_to Reload => 'index' %>.
241
242 @@ hello.html.ep
243 Hello World!
244
245 Nameless routes get an automatically generated one assigned that is
246 simply equal to the route itself without non-word characters.
247
248 Layouts
249 Templates can have layouts too, you just select one with the helper
250 "layout" in Mojolicious::Plugin::DefaultHelpers and place the result of
251 the current template with the helper "content" in
252 Mojolicious::Plugin::DefaultHelpers.
253
254 use Mojolicious::Lite;
255
256 get '/with_layout';
257
258 app->start;
259 __DATA__
260
261 @@ with_layout.html.ep
262 % title 'Green';
263 % layout 'green';
264 Hello World!
265
266 @@ layouts/green.html.ep
267 <!DOCTYPE html>
268 <html>
269 <head><title><%= title %></title></head>
270 <body><%= content %></body>
271 </html>
272
273 The stash or helpers like "title" in
274 Mojolicious::Plugin::DefaultHelpers can be used to pass additional data
275 to the layout.
276
277 Blocks
278 Template blocks can be used like normal Perl functions and are always
279 delimited by the "begin" and "end" keywords, they are the foundation
280 for many helpers.
281
282 use Mojolicious::Lite;
283
284 get '/with_block' => 'block';
285
286 app->start;
287 __DATA__
288
289 @@ block.html.ep
290 % my $link = begin
291 % my ($url, $name) = @_;
292 Try <%= link_to $url => begin %><%= $name %><% end %>.
293 % end
294 <!DOCTYPE html>
295 <html>
296 <head><title>Sebastians frameworks</title></head>
297 <body>
298 %= $link->('http://mojolicious.org', 'Mojolicious')
299 %= $link->('http://catalystframework.org', 'Catalyst')
300 </body>
301 </html>
302
303 Helpers
304 Helpers are little functions you can create with the keyword "helper"
305 in Mojolicious::Lite and reuse throughout your whole application, from
306 actions to templates.
307
308 use Mojolicious::Lite;
309
310 # A helper to identify visitors
311 helper whois => sub {
312 my $c = shift;
313 my $agent = $c->req->headers->user_agent || 'Anonymous';
314 my $ip = $c->tx->remote_address;
315 return "$agent ($ip)";
316 };
317
318 # Use helper in action and template
319 get '/secret' => sub {
320 my $c = shift;
321 my $user = $c->whois;
322 $c->app->log->debug("Request from $user");
323 };
324
325 app->start;
326 __DATA__
327
328 @@ secret.html.ep
329 We know who you are <%= whois %>.
330
331 A list of all built-in ones can be found in
332 Mojolicious::Plugin::DefaultHelpers and
333 Mojolicious::Plugin::TagHelpers.
334
335 Placeholders
336 Route placeholders allow capturing parts of a request path until a "/"
337 or "." separator occurs, similar to the regular expression "([^/.]+)".
338 Results are accessible via "stash" in Mojolicious::Controller and
339 "param" in Mojolicious::Controller.
340
341 use Mojolicious::Lite;
342
343 # /foo/test
344 # /foo/test123
345 get '/foo/:bar' => sub {
346 my $c = shift;
347 my $bar = $c->stash('bar');
348 $c->render(text => "Our :bar placeholder matched $bar");
349 };
350
351 # /testsomething/foo
352 # /test123something/foo
353 get '/<:bar>something/foo' => sub {
354 my $c = shift;
355 my $bar = $c->param('bar');
356 $c->render(text => "Our :bar placeholder matched $bar");
357 };
358
359 app->start;
360
361 To separate them from the surrounding text, you can surround your
362 placeholders with "<" and ">", which also makes the colon prefix
363 optional.
364
365 Relaxed Placeholders
366 Relaxed placeholders allow matching of everything until a "/" occurs,
367 similar to the regular expression "([^/]+)".
368
369 use Mojolicious::Lite;
370
371 # /hello/test
372 # /hello/test.html
373 get '/hello/#you' => 'groovy';
374
375 app->start;
376 __DATA__
377
378 @@ groovy.html.ep
379 Your name is <%= $you %>.
380
381 Wildcard placeholders
382 Wildcard placeholders allow matching absolutely everything, including
383 "/" and ".", similar to the regular expression "(.+)".
384
385 use Mojolicious::Lite;
386
387 # /hello/test
388 # /hello/test123
389 # /hello/test.123/test/123
390 get '/hello/*you' => 'groovy';
391
392 app->start;
393 __DATA__
394
395 @@ groovy.html.ep
396 Your name is <%= $you %>.
397
398 HTTP methods
399 Routes can be restricted to specific request methods with different
400 keywords like "get" in Mojolicious::Lite and "any" in
401 Mojolicious::Lite.
402
403 use Mojolicious::Lite;
404
405 # GET /hello
406 get '/hello' => sub {
407 my $c = shift;
408 $c->render(text => 'Hello World!');
409 };
410
411 # PUT /hello
412 put '/hello' => sub {
413 my $c = shift;
414 my $size = length $c->req->body;
415 $c->render(text => "You uploaded $size bytes to /hello.");
416 };
417
418 # GET|POST|PATCH /bye
419 any ['GET', 'POST', 'PATCH'] => '/bye' => sub {
420 my $c = shift;
421 $c->render(text => 'Bye World!');
422 };
423
424 # * /whatever
425 any '/whatever' => sub {
426 my $c = shift;
427 my $method = $c->req->method;
428 $c->render(text => "You called /whatever with $method.");
429 };
430
431 app->start;
432
433 Optional placeholders
434 All placeholders require a value, but by assigning them default values
435 you can make capturing optional.
436
437 use Mojolicious::Lite;
438
439 # /hello
440 # /hello/Sara
441 get '/hello/:name' => {name => 'Sebastian', day => 'Monday'} => sub {
442 my $c = shift;
443 $c->render(template => 'groovy', format => 'txt');
444 };
445
446 app->start;
447 __DATA__
448
449 @@ groovy.txt.ep
450 My name is <%= $name %> and it is <%= $day %>.
451
452 Default values that don't belong to a placeholder simply get merged
453 into the stash all the time.
454
455 Restrictive placeholders
456 A very easy way to make placeholders more restrictive are alternatives,
457 you just make a list of possible values.
458
459 use Mojolicious::Lite;
460
461 # /test
462 # /123
463 any '/:foo' => [foo => ['test', '123']] => sub {
464 my $c = shift;
465 my $foo = $c->param('foo');
466 $c->render(text => "Our :foo placeholder matched $foo");
467 };
468
469 app->start;
470
471 All placeholders get compiled to a regular expression internally, this
472 process can also be customized. Just make sure not to use "^" and "$",
473 or capturing groups "(...)", non-capturing groups "(?:...)" are fine
474 though.
475
476 use Mojolicious::Lite;
477
478 # /1
479 # /123
480 any '/:bar' => [bar => qr/\d+/] => sub {
481 my $c = shift;
482 my $bar = $c->param('bar');
483 $c->render(text => "Our :bar placeholder matched $bar");
484 };
485
486 app->start;
487
488 You can take a closer look at all the generated regular expressions
489 with the command Mojolicious::Command::routes.
490
491 $ ./myapp.pl routes -v
492
493 Under
494 Authentication and code shared between multiple routes can be realized
495 easily with routes generated by "under" in Mojolicious::Lite. All
496 following routes are only evaluated if the callback returned a true
497 value.
498
499 use Mojolicious::Lite;
500
501 # Authenticate based on name parameter
502 under sub {
503 my $c = shift;
504
505 # Authenticated
506 my $name = $c->param('name') || '';
507 return 1 if $name eq 'Bender';
508
509 # Not authenticated
510 $c->render(template => 'denied');
511 return undef;
512 };
513
514 # Only reached when authenticated
515 get '/' => 'index';
516
517 app->start;
518 __DATA__
519
520 @@ denied.html.ep
521 You are not Bender, permission denied.
522
523 @@ index.html.ep
524 Hi Bender.
525
526 Prefixing multiple routes is another good use for it.
527
528 use Mojolicious::Lite;
529
530 # /foo
531 under '/foo';
532
533 # /foo/bar
534 get '/bar' => {text => 'foo bar'};
535
536 # /foo/baz
537 get '/baz' => {text => 'foo baz'};
538
539 # / (reset)
540 under '/' => {msg => 'whatever'};
541
542 # /bar
543 get '/bar' => {inline => '<%= $msg %> works'};
544
545 app->start;
546
547 You can also group related routes with "group" in Mojolicious::Lite,
548 which allows nesting of routes generated with "under" in
549 Mojolicious::Lite.
550
551 use Mojolicious::Lite;
552
553 # Global logic shared by all routes
554 under sub {
555 my $c = shift;
556 return 1 if $c->req->headers->header('X-Bender');
557 $c->render(text => "You're not Bender.");
558 return undef;
559 };
560
561 # Admin section
562 group {
563
564 # Local logic shared only by routes in this group
565 under '/admin' => sub {
566 my $c = shift;
567 return 1 if $c->req->headers->header('X-Awesome');
568 $c->render(text => "You're not awesome enough.");
569 return undef;
570 };
571
572 # GET /admin/dashboard
573 get '/dashboard' => {text => 'Nothing to see here yet.'};
574 };
575
576 # GET /welcome
577 get '/welcome' => {text => 'Hi Bender.'};
578
579 app->start;
580
581 Formats
582 Formats can be automatically detected from file extensions like
583 ".html", they are used to find the right template and generate the
584 correct "Content-Type" header.
585
586 use Mojolicious::Lite;
587
588 # /detection
589 # /detection.html
590 # /detection.txt
591 get '/detection' => sub {
592 my $c = shift;
593 $c->render(template => 'detected');
594 };
595
596 app->start;
597 __DATA__
598
599 @@ detected.html.ep
600 <!DOCTYPE html>
601 <html>
602 <head><title>Detected</title></head>
603 <body>HTML was detected.</body>
604 </html>
605
606 @@ detected.txt.ep
607 TXT was detected.
608
609 The default format is "html", and restrictive placeholders can be used
610 to limit possible values.
611
612 use Mojolicious::Lite;
613
614 # /hello.json
615 # /hello.txt
616 get '/hello' => [format => ['json', 'txt']] => sub {
617 my $c = shift;
618 return $c->render(json => {hello => 'world'})
619 if $c->stash('format') eq 'json';
620 $c->render(text => 'hello world');
621 };
622
623 app->start;
624
625 Or you can just disable format detection with a special type of
626 restrictive placeholder.
627
628 use Mojolicious::Lite;
629
630 # /hello
631 get '/hello' => [format => 0] => {text => 'No format detection.'};
632
633 # Disable detection and allow the following routes to re-enable it on demand
634 under [format => 0];
635
636 # /foo
637 get '/foo' => {text => 'No format detection again.'};
638
639 # /bar.txt
640 get '/bar' => [format => 'txt'] => {text => ' Just one format.'};
641
642 app->start;
643
644 Content negotiation
645 For resources with different representations and that require truly
646 RESTful content negotiation you can also use "respond_to" in
647 Mojolicious::Plugin::DefaultHelpers.
648
649 use Mojolicious::Lite;
650
651 # /hello (Accept: application/json)
652 # /hello (Accept: application/xml)
653 # /hello.json
654 # /hello.xml
655 # /hello?format=json
656 # /hello?format=xml
657 get '/hello' => sub {
658 my $c = shift;
659 $c->respond_to(
660 json => {json => {hello => 'world'}},
661 xml => {text => '<hello>world</hello>'},
662 any => {data => '', status => 204}
663 );
664 };
665
666 app->start;
667
668 MIME type mappings can be extended or changed easily with "types" in
669 Mojolicious.
670
671 app->types->type(rdf => 'application/rdf+xml');
672
673 Static files
674 Similar to templates, but with only a single file extension and
675 optional Base64 encoding, static files can be inlined in the "DATA"
676 section and are served automatically.
677
678 use Mojolicious::Lite;
679
680 app->start;
681 __DATA__
682
683 @@ something.js
684 alert('hello!');
685
686 @@ test.txt (base64)
687 dGVzdCAxMjMKbGFsYWxh
688
689 External static files are not limited to a single file extension and
690 will be served automatically from a "public" directory if it exists.
691
692 $ mkdir public
693 $ mv something.js public/something.js
694 $ mv mojolicious.tar.gz public/mojolicious.tar.gz
695
696 Both have a higher precedence than routes for "GET" and "HEAD"
697 requests. Content negotiation with "Range", "If-None-Match" and
698 "If-Modified-Since" headers is supported as well and can be tested very
699 easily with Mojolicious::Command::get.
700
701 $ ./myapp.pl get /something.js -v -H 'Range: bytes=2-4'
702
703 External templates
704 External templates will be searched by the renderer in a "templates"
705 directory if it exists.
706
707 $ mkdir -p templates/foo
708 $ echo 'Hello World!' > templates/foo/bar.html.ep
709
710 They have a higher precedence than templates in the "DATA" section.
711
712 use Mojolicious::Lite;
713
714 # Render template "templates/foo/bar.html.ep"
715 any '/external' => sub {
716 my $c = shift;
717 $c->render(template => 'foo/bar');
718 };
719
720 app->start;
721
722 Home
723 You can use "home" in Mojolicious to interact with the directory your
724 application considers its home. This is the directory it will search
725 for "public" and "templates" directories, but you can use it to store
726 all sorts of application specific data.
727
728 $ mkdir cache
729 $ echo 'Hello World!' > cache/hello.txt
730
731 There are many useful methods Mojo::Home inherits from Mojo::File, like
732 "child" in Mojo::File and "slurp" in Mojo::File, that will help you
733 keep your application portable across many different operating systems.
734
735 use Mojolicious::Lite;
736
737 # Load message into memory
738 my $hello = app->home->child('cache', 'hello.txt')->slurp;
739
740 # Display message
741 get '/' => sub {
742 my $c = shift;
743 $c->render(text => $hello);
744 };
745
746 You can also introspect your application from the command line with
747 Mojolicious::Command::eval.
748
749 $ ./myapp.pl eval -v 'app->home'
750
751 Conditions
752 Conditions such as "agent" and "host" from
753 Mojolicious::Plugin::HeaderCondition allow even more powerful route
754 constructs.
755
756 use Mojolicious::Lite;
757
758 # Firefox
759 get '/foo' => (agent => qr/Firefox/) => sub {
760 my $c = shift;
761 $c->render(text => 'Congratulations, you are using a cool browser.');
762 };
763
764 # Internet Explorer
765 get '/foo' => (agent => qr/Internet Explorer/) => sub {
766 my $c = shift;
767 $c->render(text => 'Dude, you really need to upgrade to Firefox.');
768 };
769
770 # http://mojolicious.org/bar
771 get '/bar' => (host => 'mojolicious.org') => sub {
772 my $c = shift;
773 $c->render(text => 'Hello Mojolicious.');
774 };
775
776 app->start;
777
778 Sessions
779 Cookie-based sessions just work out of the box, as soon as you start
780 using them through the helper "session" in
781 Mojolicious::Plugin::DefaultHelpers. Just be aware that all session
782 data gets serialized with Mojo::JSON and stored client-side, with a
783 cryptographic signature to prevent tampering.
784
785 use Mojolicious::Lite;
786
787 # Access session data in action and template
788 get '/counter' => sub {
789 my $c = shift;
790 $c->session->{counter}++;
791 };
792
793 app->start;
794 __DATA__
795
796 @@ counter.html.ep
797 Counter: <%= session 'counter' %>
798
799 Note that you should use custom "secrets" in Mojolicious to make signed
800 cookies really tamper resistant.
801
802 app->secrets(['My secret passphrase here']);
803
804 File uploads
805 All files uploaded via "multipart/form-data" request are automatically
806 available as Mojo::Upload objects from "param" in
807 Mojolicious::Controller. And you don't have to worry about memory
808 usage, because all files above 250KiB will be automatically streamed
809 into a temporary file. To build HTML forms more efficiently, you can
810 also use tag helpers like "form_for" in
811 Mojolicious::Plugin::TagHelpers.
812
813 use Mojolicious::Lite;
814
815 # Upload form in DATA section
816 get '/' => 'form';
817
818 # Multipart upload handler
819 post '/upload' => sub {
820 my $c = shift;
821
822 # Check file size
823 return $c->render(text => 'File is too big.', status => 200)
824 if $c->req->is_limit_exceeded;
825
826 # Process uploaded file
827 return $c->redirect_to('form') unless my $example = $c->param('example');
828 my $size = $example->size;
829 my $name = $example->filename;
830 $c->render(text => "Thanks for uploading $size byte file $name.");
831 };
832
833 app->start;
834 __DATA__
835
836 @@ form.html.ep
837 <!DOCTYPE html>
838 <html>
839 <head><title>Upload</title></head>
840 <body>
841 %= form_for upload => (enctype => 'multipart/form-data') => begin
842 %= file_field 'example'
843 %= submit_button 'Upload'
844 % end
845 </body>
846 </html>
847
848 To protect you from excessively large files there is also a limit of
849 16MiB by default, which you can tweak with the attribute
850 "max_request_size" in Mojolicious.
851
852 # Increase limit to 1GiB
853 app->max_request_size(1073741824);
854
855 User agent
856 With Mojo::UserAgent, which is available through the helper "ua" in
857 Mojolicious::Plugin::DefaultHelpers, there's a full featured HTTP and
858 WebSocket user agent built right in. Especially in combination with
859 Mojo::JSON and Mojo::DOM this can be a very powerful tool.
860
861 use Mojolicious::Lite;
862
863 # Blocking
864 get '/headers' => sub {
865 my $c = shift;
866 my $url = $c->param('url') || 'https://mojolicious.org';
867 my $dom = $c->ua->get($url)->result->dom;
868 $c->render(json => $dom->find('h1, h2, h3')->map('text')->to_array);
869 };
870
871 # Non-blocking
872 get '/title' => sub {
873 my $c = shift;
874 $c->ua->get('mojolicious.org' => sub {
875 my ($ua, $tx) = @_;
876 $c->render(data => $tx->result->dom->at('title')->text);
877 });
878 };
879
880 # Concurrent non-blocking
881 get '/titles' => sub {
882 my $c = shift;
883 my $mojo = $c->ua->get_p('https://mojolicious.org');
884 my $cpan = $c->ua->get_p('https://metacpan.org');
885 Mojo::Promise->all($mojo, $cpan)->then(sub {
886 my ($mojo, $cpan) = @_;
887 $c->render(json => {
888 mojo => $mojo->[0]->result->dom->at('title')->text,
889 cpan => $cpan->[0]->result->dom->at('title')->text
890 });
891 })->wait;
892 };
893
894 app->start;
895
896 For more information about the user agent see also "USER AGENT" in
897 Mojolicious::Guides::Cookbook.
898
899 WebSockets
900 WebSocket applications have never been this simple before. Just receive
901 messages by subscribing to events such as "json" in
902 Mojo::Transaction::WebSocket with "on" in Mojolicious::Controller and
903 return them with "send" in Mojolicious::Controller.
904
905 use Mojolicious::Lite;
906
907 websocket '/echo' => sub {
908 my $c = shift;
909 $c->on(json => sub {
910 my ($c, $hash) = @_;
911 $hash->{msg} = "echo: $hash->{msg}";
912 $c->send({json => $hash});
913 });
914 };
915
916 get '/' => 'index';
917
918 app->start;
919 __DATA__
920
921 @@ index.html.ep
922 <!DOCTYPE html>
923 <html>
924 <head>
925 <title>Echo</title>
926 <script>
927 var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
928 ws.onmessage = function (event) {
929 document.body.innerHTML += JSON.parse(event.data).msg;
930 };
931 ws.onopen = function (event) {
932 ws.send(JSON.stringify({msg: 'I ♥ Mojolicious!'}));
933 };
934 </script>
935 </head>
936 </html>
937
938 For more information about real-time web features see also "REAL-TIME
939 WEB" in Mojolicious::Guides::Cookbook.
940
941 Mode
942 You can use the Mojo::Log object from "log" in Mojolicious to portably
943 collect debug messages and automatically disable them later in a
944 production setup by changing the Mojolicious operating mode, which can
945 also be retrieved from the attribute "mode" in Mojolicious.
946
947 use Mojolicious::Lite;
948
949 # Prepare mode specific message during startup
950 my $msg = app->mode eq 'development' ? 'Development!' : 'Something else!';
951
952 get '/' => sub {
953 my $c = shift;
954 $c->app->log->debug('Rendering mode specific message');
955 $c->render(text => $msg);
956 };
957
958 app->log->debug('Starting application');
959 app->start;
960
961 The default operating mode will usually be "development" and can be
962 changed with command line options or the "MOJO_MODE" and "PLACK_ENV"
963 environment variables. A mode other than "development" will raise the
964 log level from "debug" to "info".
965
966 $ ./myapp.pl daemon -m production
967
968 All messages will be written to "STDERR" or a "log/$mode.log" file if a
969 "log" directory exists.
970
971 $ mkdir log
972
973 Mode changes also affect a few other aspects of the framework, such as
974 the built-in "exception" and "not_found" pages. Once you switch modes
975 from "development" to "production", no sensitive information will be
976 revealed on those pages anymore.
977
978 Testing
979 Testing your application is as easy as creating a "t" directory and
980 filling it with normal Perl tests like "t/basic.t", which can be a lot
981 of fun thanks to Test::Mojo.
982
983 use Test::More;
984 use Mojo::File 'curfile';
985 use Test::Mojo;
986
987 # Portably point to "../myapp.pl"
988 my $script = curfile->dirname->sibling('myapp.pl');
989
990 my $t = Test::Mojo->new($script);
991 $t->get_ok('/')->status_is(200)->content_like(qr/Funky/);
992
993 done_testing();
994
995 Just run your tests with prove.
996
997 $ prove -l -v
998 $ prove -l -v t/basic.t
999
1001 You can continue with Mojolicious::Guides now or take a look at the
1002 Mojolicious wiki <http://github.com/mojolicious/mojo/wiki>, which
1003 contains a lot more documentation and examples by many different
1004 authors.
1005
1007 If you have any questions the documentation might not yet answer, don't
1008 hesitate to ask on the mailing list
1009 <http://groups.google.com/group/mojolicious> or the official IRC
1010 channel "#mojo" on "irc.freenode.net" (chat now!
1011 <https://kiwiirc.com/nextclient/#irc://irc.freenode.net/mojo?nick=guest-?>).
1012
1013
1014
1015perl v5.30.1 2020-01-30 Mojolicious::Guides::Tutorial(3)