The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Mojolicious::Lite - Micro Web Framework

SYNOPSIS

# Using Mojolicious::Lite will enable "strict" and "warnings"
use Mojolicious::Lite;

# Route with placeholder
get '/:foo' => sub {
    my $self = shift;
    $self->render_text('Yea baby!');
};

# Start the Mojolicious command system
shagadelic;

DESCRIPTION

Mojolicous::Lite is a micro web framework built around Mojolicious.

A minimal application looks like this.

#!/usr/bin/env perl

use Mojolicious::Lite;

get '/' => sub {
    my $self = shift;
    $self->render_text('Yea baby!');
};

shagadelic;

There is also a helper command to generate a small example application.

% mojolicious generate lite_app

All the normal Mojolicious command options are available from the command line.

% ./myapp.pl daemon
Server available at http://127.0.0.1:3000.

% ./myapp.pl daemon 8080
Server available at http://127.0.0.1:8080.

% ./myapp.pl daemon_prefork
Server available at http://127.0.0.1:3000.

% ./myapp.pl cgi
...CGI output...

% ./myapp.pl fastcgi
...Blocking FastCGI main loop...

The shagadelic call that starts the Mojolicious command system can be customized to override normal @ARGV use.

shagadelic('cgi');

Routes are basically just fancy paths that can contain different kinds of placeholders.

# /foo
get '/foo' => sub {
    my $self = shift;
    $self->render_text('Yea baby!');
};

All routes can have a name associated with them, this allows automatic template detection and back referencing with url_for. Names are always the last argument.

# /
get '/' => 'index';

# /foo
get '/foo' => 'foo';

# /bar
get '/bar' => sub {
    my $self = shift;
    $self->render_text('Hi!')
} => 'bar';

__DATA__

@@ index.html.ep
<a href="<%= url_for 'foo' %>">Foo</a>.
<a href="<%= url_for 'bar' %>">Bar</a>.

@@ foo.html.ep
<a href="<%= url_for 'index' %>">Home</a>.

Templates can have layouts.

# GET /with_layout
get '/with_layout' => sub {
    my $self = shift;
    $self->render('with_layout', layout => 'green');
};

__DATA__

@@ with_layout.html.ep
We've got content!

@@ layouts/green.html.ep
<!doctype html><html>
    <head><title>Green!</title></head>
    <body><%= content %></body>
</html>

Templates can also extend each other.

# GET /
get '/' => 'first';

# GET /second
get '/second' => 'second';

__DATA__

@@ first.html.ep
% extends 'second';
%{ content header =>
    <title>Howdy!</title>
%}
First!

@@ second.html.ep
% layout 'third';
%{ content header =>
    <title>Welcome!</title>
%}
Second!

@@ layouts/third.html.ep
<!doctype html><html>
    <head>
        <%{= content header => %>
            <title>Lame default title...</title>
        <%}%>
    </head>
    <body><%= content %></body>
</html>

Route placeholders allow capturing parts of a request path until a / or . separator occurs, results will be stored by name in the stash.

# /foo/*
get '/foo/:bar' => sub {
    my $self = shift;
    my $bar  = $self->stash('bar');
    $self->render_text("Our :bar placeholder matched $bar");
};

# /*something/foo
get '/(:bar)something/foo' => sub {
    my $self = shift;
    my $bar  = $self->stash('bar');
    $self->render_text("Our :bar placeholder matched $bar");
};

Relaxed placeholders allow matching of everything until a / occurs.

# GET /hello/*
get '/hello/(.you)' => sub {
    shift->render('groovy');
};

__DATA__

@@ groovy.html.ep
Your name is <%= $you %>.

Wildcard placeholders allow matching absolutely everything, including / and ..

# /hello/*
get '/hello/(*you)' => sub {
    shift->render('groovy');
};

__DATA__

@@ groovy.html.ep
Your name is <%= $you %>.

Routes can be restricted to specific request methods.

# GET /bye
get '/bye' => sub { shift->render_text('Bye!') };

# POST /bye
post '/bye' => sub { shift->render_text('Bye!') };

# GET|POST|DELETE /bye
any [qw/get post delete/] => '/bye' => sub {
    shift->render_text('Bye!');
};

# /baz
any '/baz' => sub {
    my $self   = shift;
    my $method = $self->req->method;
    $self->render_text("You called /baz with $method");
};

All placeholders get compiled to a regex internally, with regex constraints this process can be easily customized.

# /*
any '/:bar' => [bar => qr/\d+/] => sub {
    my $self = shift;
    my $bar  = $self->stash('bar');
    $self->render_text("Our :bar placeholder matched $bar");
};

Routes allow default values to make placeholders optional.

# /hello/*
get '/hello/:name' => {name => 'Sebastian'} => sub {
    my $self = shift;
    $self->render('groovy', format => 'txt');
};

__DATA__

@@ groovy.txt.ep
My name is <%= $name %>.

All those features can be easily used together.

# /everything/*?name=*
get '/everything/:stuff' => [stuff => qr/\d+/] => {stuff => 23} => sub {
    shift->render('welcome');
};

__DATA__

@@ welcome.html.ep
Stuff is <%= $stuff %>.
Query param name is <%= param 'name' %>.

Here's a fully functional example for a html form handling application using multiple features at once.

#!/usr/bin/env perl

use Mojolicious::Lite;

get '/' => 'index';

post '/form' => sub {
    my $self = shift;

    my $groovy = $self->param('groovy') || 'Austin Powers';
    $groovy =~ s/[^\w\s]+//g;

    $self->render(
        template => 'welcome',
        layout   => 'funky',
        groovy   => $groovy
    );
} => 'form';

shagadelic;
__DATA__

@@ index.html.ep
% layout 'funky');
Who is groovy?
<form action="<%= url_for 'form' %>" method="POST">
    <input type="text" name="groovy" />
    <input type="submit" value="Woosh!">
</form>

@@ welcome.html.ep
<%= $groovy %> is groovy!
<%= include 'menu' %>

@@ menu.html.ep
<a href="<%= url_for 'index' %>">Try again</a>

@@ layouts/funky.html.ep
<!doctype html><html>
    <head><title>Funky!</title></head>
    <body><%= content %>
    </body>
</html>

Ladders can be used for authentication and to share code between multiple routes. All routes following a ladder are only evaluated if the ladder returns a true value.

use Mojolicious::Lite;

# Authenticate based on name parameter
ladder sub {
    my $self = shift;

    # Authenticated
    my $name = $self->param('name') || '';
    return 1 if $name eq 'Bender';

    # Not authenticated
    $self->render('denied');
    return;
};

# GET / (with ladder authentication)
get '/' => 'index';

shagadelic;
__DATA__;

@@ denied.html.ep
You are not Bender, permission denied!

@@ index.html.ep
Hi Bender!

Conditions such as agent allow even more powerful route constructs.

# /foo
get '/foo' => (agent => qr/Firefox/) => sub {
    shift->render_text('Congratulations, you are using a cool browser!');
}

# /foo
get '/foo' => (agent => qr/Internet Explorer/) => sub {
    shift->render_text('Dude, you really need to upgrade to Firefox!');
}

Formats can be automatically detected by looking at file extensions.

# /detection.html
# /detection.txt
get '/detection' => sub {
    my $self = shift;
    $self->render('detected');
};

__DATA__

@@ detected.html.ep
<!doctype html><html>
    <head><title>Detected!</title></head>
    <body>HTML was detected.</body>
</html>

@@ detected.txt.ep
TXT was detected.

External templates will be searched by the renderer in a templates directory.

# /external
any '/external' => sub {
    my $self = shift;

    # templates/foo/bar.html.ep
    $self->render('foo/bar');
};

Static files will be automatically served from the public directory if it exists.

% mkdir public
% mv something.js public/something.js

Testing your application is as easy as creating a t directory and filling it with normal Perl unit tests like t/funky.t.

use Test::More tests => 3;
use Test::Mojo;

use FindBin;
require "$FindBin::Bin/../myapp.pl";

my $t = Test::Mojo->new;
$t->get_ok('/')->status_is(200)->content_like(qr/Funky!/);

Run all unit tests with the test command.

% ./myapp.pl test

To disable debug messages later in a production setup you can change the Mojolicious mode, default will be development.

% MOJO_MODE=production ./myapp.pl

Log messages will be automatically written to a log/$mode.log file if a log directory exists.

% mkdir log

For more control the Mojolicious instance can be accessed directly.

app->log->level('error');
app->routes->route('/foo/:bar')->via('get')->to(callback => sub {
    my $self = shift;
    $self->render_text('Hello Mojo!');
});

In case a lite app needs to grow, lite and real Mojolicous applications can be easily mixed to make the transition process very smooth.

package MyApp::Foo;
use base 'Mojolicious::Controller';

sub index { shift->render_text('It works!') }

package main;
use Mojolicious::Lite;

get '/bar' => sub { shift->render_text('This too!') };

app->routes->namespace('MyApp');
app->routes->route('/foo/:action')->via('get')
  ->to(controller => 'foo', action => index);

shagadelic;

ATTRIBUTES

Mojolicious::Lite inherits all attributes from Mojolicious.

METHODS

Mojolicious::Lite inherits all methods from Mojolicious and implements the following new ones.

new

my $mojo = Mojolicious::Lite->new;