In the second post of the series we look into the bascic mechanisms of routing and rendering within Mojolicous and create the first requests of our app.
Mojolicious Series
- Part 01 - Introduction
- Part 02 - Routing and Rendering
- Part 03 - Forms, Logins
- Part 04 - Database schemas with DBIx::Class
- Part 05 - DBIx::Class Integration in Mojolicious
- Part 06 - Schema Versioning with DBIx::Class::Migration
- Part 07 - Relationships with DBIx::Class
Our first Route: Index
Let's define our first route. Open the lib/moblo.pm
module.
In Mojolicious we create and manipulate routes using the router object
$self->routes
. Routes are mappings from incoming request URLs (or
rather, patterns) to some entity returning a response.
The first route we create is to render an index page. (If you used the
mojo generator from the last post, delete the route to
example/welcome
.)
1
2
3
4
5
6
7
8
9
10
11
sub startup {
my $self = shift;
# .. snip ..
# Router
my $r = $self->routes;
# GET / -> Main::index()
$r->get('/')->to(template => 'main/index');
}
As the index page doesn't require any additional data, we can directly
render a template in the route definition. Compare this to the generated
welcome route. It routed to the controller lib/moblo/Example.pm
and
the action welcome
within. It called render
with a stash variable
named msg that was set from the controller.
To demonstrate the flexibility of routes, we can also change to route to use a controller to manually render data.
1
$r->get('/')->to('main#index');
This ties the returned content from the method index
of the controller
lib/moblo/Main.pm
to the URL /
. The Controller#action shortcut
also has an equal alternative
->to(controller => 'Main', action => 'index')
. Use
whichever you prefer.
Let's create the controller counterpart. Create the controller file at
lib/moblo/Main.pm
. Every controller derives from the class
Mojolicious::Controller
. Mojolicious provides a shortcut
for this definition through
Mojo::Base. Thus, every
controller module looks like this, with only the package defintion
changing:
1
2
3
4
5
6
package Moblo::Main;
use Mojo::Base 'Mojolicious::Controller';
# Actions/Methods go here
1;
Now, the last and most important part is still missing: The action definition. Recall that actions are simply methods in the controller which return data to render.
In the generated welcome action, we already saw the render function to render a template with an additional variable stored in the stash.
1
2
3
4
sub index {
my $self = shift;
$self->render('main/index');
}
This action forces rendering the template templates/main/index
and
returns an error if it's missing.
By the way: Unless you manually render data yourself, Mojolicious will
look for a template under templates/<controller>/<action>
and use that
automatically. Thus, the following action definitions are all equal
within the controller Moblo::Main, given that the template file exists.
1
2
3
sub index {}
sub index { shift->render; }
sub index { shift->render('main/index'); }
The index template
So, now the request to /
will return the content to the template
templates/main/index
. Let's bring it to life.
Mojolicious provides a templating engine ep for any kind of document.
Edit templates/main/index.html.ep
.
1
2
3
4
<h2>Moblo — Blog Index</h2>
<p>Welcome to Moblo!</p>
<h3>Post Index</h3>
<p>None! :/</p>
Well, that's rather boring. Let's at least extend it with a link to a login page to gain access to the actual user management page.
Similar to Ruby's erb, the templating engine uses special tags <%= %>
, %=
for running and printing the output, as well as %
and <% %>
for running regular perl code within a template.
1
2
3
4
5
6
7
8
<h2>Moblo — Blog Index</h2>
<p>Welcome to Moblo!</p>
<h3>Post Index</h3>
<p>None! :/</p>
<hr>
<p>
%= link_to Login => 'login_form'
</p>
Finally, templated code!
The used function
link_to
is a helper method that is exposed within the template file. Mojolicious
provides a plethora of these helper functions to be used within the ep
template engine (basically, you can execute any perl code within). The
two built-in sets are
DefaultHelpers,
which provide access to internal data (stash, GET/POST data, etc.) and
TagHelpers
to create HTML tags. Later on, we will also create and use more advanced
methods in the templates, such as content blocks, includes or form
validation.
Layouts
You may have noticed that the above template is no complete HTML
document. It's because I want to extract the outer body of the app into
a separate file. With mojolicious, that's called a layout
.
The layout is really just a special template which uses the helper
method content
, which is replaced with the rendered
template file.
You can either set the layout manually in the call to
$self->render
like so:
1
$self->render('main/index', layout => 'base');
This will wrap the layout templates/layouts/base.html.ep around the contents of the index template. If you do not specify the layout, Mojolicious will try to use the default layout file located at templates/layouts/default.html.ep. By the way: You can override the default setting in your main application module (lib/moblo.pm in our case):
1
2
3
4
5
6
7
8
9
10
11
12
sub startup {
my $self = shift;
# Default layout
$self->defaults(layout => 'base');
# Router
my $r = $self->routes;
# ...
}
The layout file itself is just another template, which you can fill however you like. The following template shows the default layout for reference:
1
2
3
4
5
<!DOCTYPE html>
<html>
<head><title><%= title %></title></head>
<body><%= content %></body>
</html>
That's it for the introduction to routing and templates. In the next post, we will bring the login route to life.
Code at GitHub
You can browse the app at the state of this second post on Github.