Intro to Mojolicious
Starting from scratch: Mojolicious::Lite
- Everything in one file (if you want)
- Great for big or small projects (can be
grown
into full-blown Mojolicious app later)
- You should probably use Perl 5.26+
Learning the basics
Routes
# The main landing page; index.html
get '/' => sub {
my ($c) = @_;
my $count = format_number time; # Grab epoch and add commas
my $fortune = `/usr/games/fortune` || `fortune` || "huh??\n";
$c->stash(
count => $count,
fortune => $fortune
);
$c->render();
} => 'index';
- Route name can be derived from path (specified as 'index 'here)
stash()
is used to pass variables from controller to view
(i.e. our templates)
Minimal routes
get '/about';
- This would render a template named
about.html.ep
Placeholders
get '/:route';
- I wanted to (mostly) adhere to the D.R.Y. (don't repeat yourself) principle
- Got 500s rather than 404s for requesting routes with no templates (bad)
Restrictive placeholder
# Default route
get '/:route' => [route => [qw{die me news portal}]] => sub {
my ($c) = @_;
$c->render(template => $c->stash('route'));
};
- Now renders a 404 for routes with no templates (success)
- The requested route is available via
stash()
Sessions
my ($c) = @_;
my $sessionLife = 604800;
if ($c->cookie('banner') eq 'seen') {
# Set session for a week
$c->session(expiration => $sessionLife, banner => 'seen');
# Kill plain-text cookie
$c->cookie(banner => 'seen', {expires => 1});
}
# Pass in the expiration for plain-text cookie
$c->stash(sessionLife => $sessionLife);
session()
is the way to go for session
cookie
cookie()
can access plain-text
cookies
- We should generate a secret passphrase; can be set with
app->secrets()
or the Config
plugin to
make our life easier. Speaking of plugins...
Plugins
plugin 'Config';
# CGI scripts
plugin CGI => ['/cgi-bin/guest.cgi' => './cgi-bin/guest_mm.cgi'];
plugin CGI => ['/cgi-bin/whoami.cgi' => './cgi-bin/whoami.cgi' ];
- Extensions that help with code sharing and organization
- I'm using
Mojolicious::Plugins::CGI
to keep my CGI scripts kickin'
- There's all kinds on
CPAN
- No need to
use
these in Mojolicious::Lite, plugin
keyword takes
care of that for us
Config plugin
- Good for keeping
secrets
(amongst other things)
- Whoopsie,
had to rotate this one out
Under
# Handle the session
under sub {
my ($c) = @_;
my $sessionLife = 604800;
if ($c->cookie('banner') eq 'seen') {
# Set session for a week
$c->session(expiration => $sessionLife, banner => 'seen');
# Kill plain-text cookie
$c->cookie(banner => 'seen', {expires => 1});
}
# Pass in the expiration for plain-text cookie
$c->stash(sessionLife => $sessionLife);
1;
};
- Great for code shared between multiple routes
- Give it a path; defaults to
/
- Must return true in order for routes "under" its path to be evaluated
Another good use for under
# /foo
under '/foo';
# /foo/bar
get '/bar' => {text => 'foo bar'};
# /foo/baz
get '/baz' => {text => 'foo baz'};
# / (reset)
under '/' => {msg => 'whatever'};
# /bar
get '/bar' => {inline => '<%= $msg %> works'};
- Used here as a prefix for multiple routes (borrowed example from
docs)
Templates
% title 'Swagg::Net ULA Tool';
% layout 'swagg-iframe';
<% if ($ula6) { %>
<p>Your IPv6 ULA prefix is <%= $ula6 %>.</p>
<% } else { %>
<form action="/ula6" method="get">
MAC address:<br>
<input type="text" name="macaddr"><br>
<br>
<input type="submit" value="Submit">
</form>
<% } %>
- HTML with embedded
Perl
(code and variables)
- I decided to generally keep as much as HTML/CSS as possible for now
title
specifies the <title>
element in the layout
; speaking of
layouts...
Layouts
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= title %></title>
<link rel="stylesheet" type="text/css" href="/css/swagg.css">
</head>
<body>
<%= content %>
</body>
</html>
- Much easier for maintaining hyperlinks, page navigation, etc
- Still embedded Perl here!
Layout + sessions = my banner
<% unless (session('banner') eq 'seen') { %>
<!-- Soundtrack -->
<!-- <embed> doesn't work anymore SAD
<embed src="/misc/Smashmouth_-_All_Star.mid" width="100%" height="60">-->
<audio id="soundtrack" src="/Music/Smashmouth-All-Star.mp3" preload="auto">
</audio>
<!-- "GDPR" banner -->
<div id="gdpr">
<b>Notice:</b> This site uses MIDI technology instead of tracking cookies.
<img alt="a compact disc playing music" src="/Pictures/Music_CD.gif"
style="vertical-align: bottom"><br>
<br>
<button class="win95button" onclick="playIt();"><u>O</u>K</button>
<button class="win95button" onclick="closeIt();"><u>C</u>ancel</button>
</div>
<script>
function closeIt() {
document.getElementById("gdpr").style.display = "none";
document.cookie = "banner=seen; <%= $sessionLife %>;";
}
function playIt() {
document.getElementById("soundtrack").play();
closeIt();
}
</script>
<% } %>
- Was using all JS (jquery) for this
- This saves bandwidth in comparison
TODO
- cpanfile
should lighten up the
Dockerfile
some
- AssetPack
plugin
looks interesting
- Use Hypnotoad in prod
- Implement Perl module as "model"; get rid of much of the controller
code (replaced with subroutines from module)
- Implement DB for stuff like visitor counter and guestbook (will need
our model/Perl module)
Goodbye
# Delete whole session by setting an expiration date in the past
$c->session(expires => 1);