1Mojolicious::Guides::GrUosweirngC(o3n)tributed Perl DocuMmoejnotlaitciioonus::Guides::Growing(3)
2
3
4

NAME

6       Mojolicious::Guides::Growing - Growing Mojolicious applications
7

OVERVIEW

9       This document explains the process of starting a Mojolicious::Lite
10       prototype from scratch and growing it into a well-structured
11       Mojolicious application.
12

CONCEPTS

14       Essentials every Mojolicious developer should know.
15
16   Model View Controller
17       MVC is a software architectural pattern for graphical user interface
18       programming originating in Smalltalk-80, that separates application
19       logic, presentation and input.
20
21                  +------------+    +-------+    +------+
22         Input -> | Controller | -> | Model | -> | View | -> Output
23                  +------------+    +-------+    +------+
24
25       A slightly modified version of the pattern moving some application
26       logic into the controller is the foundation of pretty much every web
27       framework these days, including Mojolicious.
28
29                     +----------------+     +-------+
30         Request  -> |                | <-> | Model |
31                     |                |     +-------+
32                     |   Controller   |
33                     |                |     +-------+
34         Response <- |                | <-> | View  |
35                     +----------------+     +-------+
36
37       The controller receives a request from a user, passes incoming data to
38       the model and retrieves data from it, which then gets turned into an
39       actual response by the view. But note that this pattern is just a
40       guideline that most of the time results in cleaner more maintainable
41       code, not a rule that should be followed at all costs.
42
43   REpresentational State Transfer
44       REST is a software architectural style for distributed hypermedia
45       systems such as the web. While it can be applied to many protocols it
46       is most commonly used with HTTP these days. In REST terms, when you are
47       opening a URL like "http://mojolicious.org/foo" with your browser, you
48       are basically asking the web server for the HTML representation of the
49       "http://mojolicious.org/foo" resource.
50
51         +--------+                                  +--------+
52         |        | -> http://mojolicious.org/foo -> |        |
53         | Client |                                  | Server |
54         |        | <-  <html>Mojo rocks!</html>  <- |        |
55         +--------+                                  +--------+
56
57       The fundamental idea here is that all resources are uniquely
58       addressable with URLs and every resource can have different
59       representations such as HTML, RSS or JSON. User interface concerns are
60       separated from data storage concerns and all session state is kept
61       client-side.
62
63         +---------+                        +------------+
64         |         | ->    PUT /foo      -> |            |
65         |         | ->    Hello World!  -> |            |
66         |         |                        |            |
67         |         | <-    201 CREATED   <- |            |
68         |         |                        |            |
69         |         | ->    GET /foo      -> |            |
70         | Browser |                        | Web Server |
71         |         | <-    200 OK        <- |            |
72         |         | <-    Hello World!  <- |            |
73         |         |                        |            |
74         |         | ->    DELETE /foo   -> |            |
75         |         |                        |            |
76         |         | <-    200 OK        <- |            |
77         +---------+                        +------------+
78
79       While HTTP methods such as "PUT", "GET" and "DELETE" are not directly
80       part of REST they go very well with it and are commonly used to
81       manipulate resources.
82
83   Sessions
84       HTTP was designed as a stateless protocol, web servers don't know
85       anything about previous requests, which makes user-friendly login
86       systems very tricky. Sessions solve this problem by allowing web
87       applications to keep stateful information across several HTTP requests.
88
89         GET /login?user=sebastian&pass=s3cret HTTP/1.1
90         Host: mojolicious.org
91
92         HTTP/1.1 200 OK
93         Set-Cookie: sessionid=987654321
94         Content-Length: 10
95         Hello sebastian.
96
97         GET /protected HTTP/1.1
98         Host: mojolicious.org
99         Cookie: sessionid=987654321
100
101         HTTP/1.1 200 OK
102         Set-Cookie: sessionid=987654321
103         Content-Length: 16
104         Hello again sebastian.
105
106       Traditionally all session data was stored on the server-side and only
107       session ids were exchanged between browser and web server in the form
108       of cookies.
109
110         Set-Cookie: session=hmac-sha1(base64(json($session)))
111
112       In Mojolicious however we are taking this concept one step further by
113       storing everything JSON serialized and Base64 encoded in HMAC-SHA1
114       signed cookies, which is more compatible with the REST philosophy and
115       reduces infrastructure requirements.
116
117   Test-Driven Development
118       TDD is a software development process where the developer starts
119       writing failing test cases that define the desired functionality and
120       then moves on to producing code that passes these tests. There are many
121       advantages such as always having good test coverage and code being
122       designed for testability, which will in turn often prevent future
123       changes from breaking old code. Much of Mojolicious was developed using
124       TDD.
125

PROTOTYPE

127       One of the main differences between Mojolicious and other web
128       frameworks is that it also includes Mojolicious::Lite, a micro web
129       framework optimized for rapid prototyping.
130
131   Differences
132       You likely know the feeling, you've got a really cool idea and want to
133       try it as quickly as possible, that's exactly why Mojolicious::Lite
134       applications don't need more than a single file.
135
136         myapp.pl   # Templates and even static files can be inlined
137
138       Full Mojolicious applications on the other hand are much closer to a
139       well organized CPAN distribution to maximize maintainability.
140
141         myapp                      # Application directory
142         |- script                  # Script directory
143         |  +- my_app               # Application script
144         |- lib                     # Library directory
145         |  |- MyApp.pm             # Application class
146         |  +- MyApp                # Application namespace
147         |     +- Controller        # Controller namespace
148         |        +- Example.pm     # Controller class
149         |- my_app.yml              # Configuration file
150         |- t                       # Test directory
151         |  +- basic.t              # Random test
152         |- log                     # Log directory
153         |  +- development.log      # Development mode log file
154         |- public                  # Static file directory (served automatically)
155         |  +- index.html           # Static HTML file
156         +- templates               # Template directory
157            |- layouts              # Template directory for layouts
158            |  +- default.html.ep   # Layout template
159            +- example              # Template directory for "Example" controller
160               +- welcome.html.ep   # Template for "welcome" action
161
162       Both application skeletons can be automatically generated with the
163       commands Mojolicious::Command::Author::generate::lite_app and
164       Mojolicious::Command::Author::generate::app.
165
166         $ mojo generate lite-app myapp.pl
167         $ mojo generate app MyApp
168
169       Feature-wise both are almost equal, the only real differences are
170       organizational, so each one can be gradually transformed into the
171       other.
172
173   Foundation
174       We start our new application with a single executable Perl script.
175
176         $ mkdir myapp
177         $ cd myapp
178         $ touch myapp.pl
179         $ chmod 744 myapp.pl
180
181       This will be the foundation for our login manager example application.
182
183         #!/usr/bin/env perl
184         use Mojolicious::Lite -signatures;
185
186         get '/' => sub ($c) {
187           $c->render(text => 'Hello World!');
188         };
189
190         app->start;
191
192       The built-in development web server makes working on your application a
193       lot of fun thanks to automatic reloading.
194
195         $ morbo ./myapp.pl
196         Web application available at http://127.0.0.1:3000
197
198       Just save your changes and they will be automatically in effect the
199       next time you refresh your browser.
200
201   A bird's-eye view
202       It all starts with an HTTP request like this, sent by your browser.
203
204         GET / HTTP/1.1
205         Host: localhost:3000
206
207       Once the request has been received by the web server through the event
208       loop, it will be passed on to Mojolicious, where it will be handled in
209       a few simple steps.
210
211       1.
212         Check if a static file exists that would meet the requirements.
213
214       2.
215         Try to find a route that would meet the requirements.
216
217       3.
218         Dispatch the request to this route, usually reaching one or more
219         actions.
220
221       4.
222         Process the request, maybe generating a response with the renderer.
223
224       5.
225         Return control to the web server, and if no response has been
226         generated yet, wait for a non-blocking operation to do so through the
227         event loop.
228
229       With our application the router would have found an action in step 2,
230       and rendered some text in step 4, resulting in an HTTP response like
231       this being sent back to the browser.
232
233         HTTP/1.1 200 OK
234         Content-Length: 12
235         Hello World!
236
237   Model
238       In Mojolicious we consider web applications simple frontends for
239       existing business logic, that means Mojolicious is by design entirely
240       model layer agnostic and you just use whatever Perl modules you like
241       most.
242
243         $ mkdir -p lib/MyApp/Model
244         $ touch lib/MyApp/Model/Users.pm
245         $ chmod 644 lib/MyApp/Model/Users.pm
246
247       Our login manager will simply use a plain old Perl module abstracting
248       away all logic related to matching usernames and passwords. The name
249       "MyApp::Model::Users" is an arbitrary choice, and is simply used to
250       make the separation of concerns more visible.
251
252         package MyApp::Model::Users;
253
254         use strict;
255         use warnings;
256         use experimental qw(signatures);
257
258         use Mojo::Util qw(secure_compare);
259
260         my $USERS = {
261           joel      => 'las3rs',
262           marcus    => 'lulz',
263           sebastian => 'secr3t'
264         };
265
266         sub new ($class) { bless {}, $class }
267
268         sub check ($self, $user, $pass) {
269
270           # Success
271           return 1 if $USERS->{$user} && secure_compare $USERS->{$user}, $pass;
272
273           # Fail
274           return undef;
275         }
276
277         1;
278
279       A simple helper can be registered with the function "helper" in
280       Mojolicious::Lite to make our model available to all actions and
281       templates.
282
283         #!/usr/bin/env perl
284         use Mojolicious::Lite -signatures;
285
286         use lib qw(lib);
287         use MyApp::Model::Users;
288
289         # Helper to lazy initialize and store our model object
290         helper users => sub { state $users = MyApp::Model::Users->new };
291
292         # /?user=sebastian&pass=secr3t
293         any '/' => sub ($c) {
294
295           # Query parameters
296           my $user = $c->param('user') || '';
297           my $pass = $c->param('pass') || '';
298
299           # Check password
300           return $c->render(text => "Welcome $user.") if $c->users->check($user, $pass);
301
302           # Failed
303           $c->render(text => 'Wrong username or password.');
304         };
305
306         app->start;
307
308       The method "param" in Mojolicious::Controller is used to access query
309       parameters, "POST" parameters, file uploads and route placeholders, all
310       at once.
311
312   Testing
313       In Mojolicious we take testing very serious and try to make it a
314       pleasant experience.
315
316         $ mkdir t
317         $ touch t/login.t
318         $ chmod 644 t/login.t
319
320       Test::Mojo is a scriptable HTTP user agent designed specifically for
321       testing, with many fun state of the art features such as CSS selectors
322       based on Mojo::DOM.
323
324         use Test::More;
325         use Test::Mojo;
326
327         # Include application
328         use Mojo::File qw(curfile);
329         require(curfile->dirname->sibling('myapp.pl'));
330
331         # Allow 302 redirect responses
332         my $t = Test::Mojo->new;
333         $t->ua->max_redirects(1);
334
335         # Test if the HTML login form exists
336         $t->get_ok('/')
337           ->status_is(200)
338           ->element_exists('form input[name="user"]')
339           ->element_exists('form input[name="pass"]')
340           ->element_exists('form input[type="submit"]');
341
342         # Test login with valid credentials
343         $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
344           ->status_is(200)
345           ->text_like('html body' => qr/Welcome sebastian/);
346
347         # Test accessing a protected page
348         $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
349
350         # Test if HTML login form shows up again after logout
351         $t->get_ok('/logout')
352           ->status_is(200)
353           ->element_exists('form input[name="user"]')
354           ->element_exists('form input[name="pass"]')
355           ->element_exists('form input[type="submit"]');
356
357         done_testing();
358
359       Your application won't pass these tests, but from now on you can use
360       them to check your progress.
361
362         $ prove -l
363         $ prove -l t/login.t
364         $ prove -l -v t/login.t
365
366       Or perform quick requests right from the command line with
367       Mojolicious::Command::get.
368
369         $ ./myapp.pl get /
370         Wrong username or password.
371
372         $ ./myapp.pl get -v '/?user=sebastian&pass=secr3t'
373         GET /?user=sebastian&pass=secr3t HTTP/1.1
374         User-Agent: Mojolicious (Perl)
375         Accept-Encoding: gzip
376         Content-Length: 0
377         Host: localhost:59472
378
379         HTTP/1.1 200 OK
380         Date: Sun, 18 Jul 2010 13:09:58 GMT
381         Server: Mojolicious (Perl)
382         Content-Length: 12
383         Content-Type: text/plain
384
385         Welcome sebastian.
386
387   State keeping
388       Sessions in Mojolicious pretty much just work out of the box once you
389       start using the method "session" in Mojolicious::Controller, there is
390       no setup required, but we suggest setting a more secure passphrase with
391       "secrets" in Mojolicious.
392
393         $app->secrets(['Mojolicious rocks']);
394
395       This passphrase is used by the HMAC-SHA1 algorithm to make signed
396       cookies tamper resistant and can be changed at any time to invalidate
397       all existing sessions.
398
399         $c->session(user => 'sebastian');
400         my $user = $c->session('user');
401
402       By default all sessions expire after one hour, for more control you can
403       use the "expiration" session value to set an expiration date in seconds
404       from now.
405
406         $c->session(expiration => 3600);
407
408       And the whole session can be deleted by using the "expires" session
409       value to set an absolute expiration date in the past.
410
411         $c->session(expires => 1);
412
413       For data that should only be visible on the next request, like a
414       confirmation message after a 302 redirect performed with "redirect_to"
415       in Mojolicious::Plugin::DefaultHelpers, you can use the flash,
416       accessible through "flash" in Mojolicious::Plugin::DefaultHelpers.
417
418         $c->flash(message => 'Everything is fine.');
419         $c->redirect_to('goodbye');
420
421       Just remember that all session data gets serialized with Mojo::JSON and
422       stored in HMAC-SHA1 signed cookies, which usually have a 4096 byte
423       (4KiB) limit, depending on browser.
424
425   Final prototype
426       A final "myapp.pl" prototype passing all of the tests above could look
427       like this.
428
429         #!/usr/bin/env perl
430         use Mojolicious::Lite -signatures;
431
432         use lib qw(lib);
433         use MyApp::Model::Users;
434
435         # Make signed cookies tamper resistant
436         app->secrets(['Mojolicious rocks']);
437
438         helper users => sub { state $users = MyApp::Model::Users->new };
439
440         # Main login action
441         any '/' => sub ($c) {
442
443           # Query or POST parameters
444           my $user = $c->param('user') || '';
445           my $pass = $c->param('pass') || '';
446
447           # Check password and render "index.html.ep" if necessary
448           return $c->render unless $c->users->check($user, $pass);
449
450           # Store username in session
451           $c->session(user => $user);
452
453           # Store a friendly message for the next page in flash
454           $c->flash(message => 'Thanks for logging in.');
455
456           # Redirect to protected page with a 302 response
457           $c->redirect_to('protected');
458         } => 'index';
459
460         # Make sure user is logged in for actions in this group
461         group {
462           under sub ($c) {
463
464             # Redirect to main page with a 302 response if user is not logged in
465             return 1 if $c->session('user');
466             $c->redirect_to('index');
467             return undef;
468           };
469
470           # A protected page auto rendering "protected.html.ep"
471           get '/protected';
472         };
473
474         # Logout action
475         get '/logout' => sub ($c) {
476
477           # Expire and in turn clear session automatically
478           $c->session(expires => 1);
479
480           # Redirect to main page with a 302 response
481           $c->redirect_to('index');
482         };
483
484         app->start;
485         __DATA__
486
487         @@ index.html.ep
488         % layout 'default';
489         %= form_for index => begin
490           % if (param 'user') {
491             <b>Wrong name or password, please try again.</b><br>
492           % }
493           Name:<br>
494           %= text_field 'user'
495           <br>Password:<br>
496           %= password_field 'pass'
497           <br>
498           %= submit_button 'Login'
499         % end
500
501         @@ protected.html.ep
502         % layout 'default';
503         % if (my $msg = flash 'message') {
504           <b><%= $msg %></b><br>
505         % }
506         Welcome <%= session 'user' %>.<br>
507         %= link_to Logout => 'logout'
508
509         @@ layouts/default.html.ep
510         <!DOCTYPE html>
511         <html>
512           <head><title>Login Manager</title></head>
513           <body><%= content %></body>
514         </html>
515
516       And the directory structure should be looking like this now.
517
518         myapp
519         |- myapp.pl
520         |- lib
521         |  +- MyApp
522         |     +- Model
523         |        +- Users.pm
524         +- t
525            +- login.t
526
527       Our templates are using quite a few features of the renderer,
528       Mojolicious::Guides::Rendering explains them all in great detail.
529

WELL-STRUCTURED APPLICATION

531       Due to the flexibility of Mojolicious there are many variations of the
532       actual growing process, but this should give you a good overview of the
533       possibilities.
534
535   Inflating templates
536       All templates and static files inlined in the "DATA" section can be
537       automatically turned into separate files in the "templates" and
538       "public" directories with the command
539       Mojolicious::Command::Author::inflate.
540
541         $ ./myapp.pl inflate
542
543       Those directories have a higher precedence, so inflating can also be a
544       great way to allow your users to customize their applications.
545
546   Simplified application class
547       This is the heart of every full Mojolicious application and always gets
548       instantiated during server startup.
549
550         $ touch lib/MyApp.pm
551         $ chmod 644 lib/MyApp.pm
552
553       We will start by extracting all actions from "myapp.pl" and turn them
554       into simplified hybrid routes in the Mojolicious::Routes router, none
555       of the actual action code needs to be changed.
556
557         package MyApp;
558         use Mojo::Base 'Mojolicious', -signatures;
559
560         use MyApp::Model::Users;
561
562         sub startup ($self) {
563
564           $self->secrets(['Mojolicious rocks']);
565           $self->helper(users => sub { state $users = MyApp::Model::Users->new });
566
567           my $r = $self->routes;
568
569           $r->any('/' => sub ($c) {
570
571             my $user = $c->param('user') || '';
572             my $pass = $c->param('pass') || '';
573             return $c->render unless $c->users->check($user, $pass);
574
575             $c->session(user => $user);
576             $c->flash(message => 'Thanks for logging in.');
577             $c->redirect_to('protected');
578           } => 'index');
579
580           my $logged_in = $r->under(sub ($c) {
581             return 1 if $c->session('user');
582             $c->redirect_to('index');
583             return undef;
584           });
585           $logged_in->get('/protected');
586
587           $r->get('/logout' => sub ($c) {
588             $c->session(expires => 1);
589             $c->redirect_to('index');
590           });
591         }
592
593         1;
594
595       The "startup" method gets called right after instantiation and is the
596       place where the whole application gets set up.  Since full Mojolicious
597       applications can use nested routes they have no need for "group"
598       blocks.
599
600   Simplified application script
601       "myapp.pl" itself can now be turned into a simplified application
602       script to allow running tests again.
603
604         #!/usr/bin/env perl
605
606         use Mojo::Base -strict;
607         use lib qw(lib);
608         use Mojolicious::Commands;
609
610         # Start command line interface for application
611         Mojolicious::Commands->start_app('MyApp');
612
613       And the directory structure of our hybrid application should be looking
614       like this.
615
616         myapp
617         |- myapp.pl
618         |- lib
619         |  |- MyApp.pm
620         |  +- MyApp
621         |     +- Model
622         |        +- Users.pm
623         |- t
624         |  +- login.t
625         +- templates
626            |- layouts
627            |  +- default.html.ep
628            |- index.html.ep
629            +- protected.html.ep
630
631   Controller class
632       Hybrid routes are a nice intermediate step, but to maximize
633       maintainability it makes sense to split our action code from its
634       routing information.
635
636         $ mkdir lib/MyApp/Controller
637         $ touch lib/MyApp/Controller/Login.pm
638         $ chmod 644 lib/MyApp/Controller/Login.pm
639
640       Once again the actual action code does not need to change, we just
641       rename $c to $self since the controller is now the invocant.
642
643         package MyApp::Controller::Login;
644         use Mojo::Base 'Mojolicious::Controller', -signatures;
645
646         sub index ($self) {
647           my $user = $self->param('user') || '';
648           my $pass = $self->param('pass') || '';
649           return $self->render unless $self->users->check($user, $pass);
650
651           $self->session(user => $user);
652           $self->flash(message => 'Thanks for logging in.');
653           $self->redirect_to('protected');
654         }
655
656         sub logged_in ($self) {
657           return 1 if $self->session('user');
658           $self->redirect_to('index');
659           return undef;
660         }
661
662         sub logout ($self) {
663           $self->session(expires => 1);
664           $self->redirect_to('index');
665         }
666
667         1;
668
669       All Mojolicious::Controller controllers are plain old Perl classes and
670       get instantiated on demand.
671
672   Application class
673       The application class "lib/MyApp.pm" can now be reduced to model and
674       routing information.
675
676         package MyApp;
677         use Mojo::Base 'Mojolicious', -signatures;
678
679         use MyApp::Model::Users;
680
681         sub startup ($self) {
682
683           $self->secrets(['Mojolicious rocks']);
684           $self->helper(users => sub { state $users = MyApp::Model::Users->new });
685
686           my $r = $self->routes;
687           $r->any('/')->to('login#index')->name('index');
688
689           my $logged_in = $r->under('/')->to('login#logged_in');
690           $logged_in->get('/protected')->to('login#protected');
691
692           $r->get('/logout')->to('login#logout');
693         }
694
695         1;
696
697       The router allows many different route variations,
698       Mojolicious::Guides::Routing explains them all in great detail.
699
700   Templates
701       Templates are our views, and usually bound to controllers, so they need
702       to be moved into the appropriate directories.
703
704         $ mkdir templates/login
705         $ mv templates/index.html.ep templates/login/index.html.ep
706         $ mv templates/protected.html.ep templates/login/protected.html.ep
707
708   Script
709       Finally "myapp.pl" can be moved into a "script" directory and renamed
710       to "my_app" to follow the CPAN standard.
711
712         $ mkdir script
713         $ mv myapp.pl script/my_app
714
715       Just a few small details change, instead of a relative path to lib we
716       now use Mojo::File to get an absolute path, allowing us to start the
717       application from outside its home directory.
718
719         #!/usr/bin/env perl
720
721         use strict;
722         use warnings;
723
724         use Mojo::File qw(curfile);
725         use lib curfile->dirname->sibling('lib')->to_string;
726         use Mojolicious::Commands;
727
728         # Start command line interface for application
729         Mojolicious::Commands->start_app('MyApp');
730
731   Simplified tests
732       Full Mojolicious applications are a little easier to test, so
733       "t/login.t" can be simplified.
734
735         use Mojo::Base -strict;
736
737         use Test::More;
738         use Test::Mojo;
739
740         my $t = Test::Mojo->new('MyApp');
741         $t->ua->max_redirects(1);
742
743         subtest 'Test login workflow' => sub {
744           $t->get_ok('/')
745             ->status_is(200)
746             ->element_exists('form input[name="user"]')
747             ->element_exists('form input[name="pass"]')
748             ->element_exists('form input[type="submit"]');
749
750           $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
751             ->status_is(200)
752             ->text_like('html body' => qr/Welcome sebastian/);
753
754           $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
755
756           $t->get_ok('/logout')
757             ->status_is(200)
758             ->element_exists('form input[name="user"]')
759             ->element_exists('form input[name="pass"]')
760             ->element_exists('form input[type="submit"]');
761         };
762
763         done_testing();
764
765       And our final directory structure should be looking like this.
766
767         myapp
768         |- script
769         |  +- my_app
770         |- lib
771         |  |- MyApp.pm
772         |  +- MyApp
773         |     |- Controller
774         |     |  +- Login.pm
775         |     +- Model
776         |        +- Users.pm
777         |- t
778         |  +- login.t
779         +- templates
780            |- layouts
781            |  +- default.html.ep
782            +- login
783               |- index.html.ep
784               +- protected.html.ep
785
786       Test-driven development takes a little getting used to, but can be a
787       very powerful tool.
788

MORE

790       You can continue with Mojolicious::Guides now or take a look at the
791       Mojolicious wiki <https://github.com/mojolicious/mojo/wiki>, which
792       contains a lot more documentation and examples by many different
793       authors.
794

SUPPORT

796       If you have any questions the documentation might not yet answer, don't
797       hesitate to ask in the Forum <https://forum.mojolicious.org> or the
798       official IRC channel "#mojo" on "chat.freenode.net" (chat now!
799       <https://webchat.freenode.net/#mojo>).
800
801
802
803perl v5.32.1                      2021-02-07   Mojolicious::Guides::Growing(3)
Impressum