The Advisory Boar

By Abhijit Menon-Sen <>

Mojolicious: getting rid of waypoints

I have been less active than usual for several weeks due to poor health, and it came as a surprise to discover that Mojolicious 2.82 deprecated waypoints, a feature that I used in more than one app. I couldn't find any explanation of the reasons for the change (the commit message says only "deprecated waypoint support in Mojolicious"), hence this note.

Waypoints were earlier considered for removal because they were poorly understood, but were retained because sensible uses of them were found in the wild at the time. This time, they were deprecated because their special-case handling was in conflict with providing more control over format detection, and because their lack could be worked around easily.

Unfortunately, subsequent changes (after the deprecation) seem to have broken waypoints, which I discovered when going from 2.6x to 2.9x in a single step. I chose to get rid of my waypoints rather than investigate the problem more closely (because I suspected that the "fix" would break other things).

It's easy to rewrite code that uses waypoints. Here's an example from one of my apps:

$internal->waypoint('/logs')->to('misc#logs')
    ->get('/tail')->to('misc#tail');

The above code can be written as:

my $logs = $internal->route('/logs');
$logs->get('/')->to('misc#logs');
$logs->get('/tail')->to('misc#log_tail');

Depending on the situation, other alternatives may be available. For example, you could just as easily declare one route for /logs, and another for /logs/tail.

It's an easy workaround, with only minor disadvantages. If you use the nested routes, you need a new temporary variable ($logs). If you declare separate routes, you have to repeat the prefix unnecessarily. But that's a small price to pay.

I was worried about the breakage at first, but Sebastian points out that nobody else has complained in the month since the change. Given that few people ever understood or used waypoints, perhaps nobody was affected.

Mojolicious and HTTP streaming

Here's an example of how to implement "tail -f" functionality in a Mojolicious application. It's not difficult, but it's not obvious how to put the many moving parts together.

Read more…

Serving static files with Mojolicious

Here's how I configured my Mojolicious application to set an appropriate expiry date based on the content-type of any static resources it serves.

Read more…

Mojolicious and DB handles

"But… what about the model?" is a very common question from newcomers to Mojolicious, and not everyone is happy with the simple answer: "You can do whatever you want, Mojolicious doesn't care". What this really means (especially for people coming from Catalyst) is that you can keep using your DBIx::Class model with "use My::Model", or use whatever other method you prefer to interact with your database.

I don't get along with DBIx::Class myself, but enough people ask about using straight DBI that I felt it worthwhile to share the way I do it. There's not much to it: my code boils down to calling DBI->connect and keeping the handle for later use. Mojo's package attributes with delayed initialisation make it easy to do this.

I put the following code in my startup function (i.e., sub startup in lib/MyApp.pm):

sub startup {
    my $app = shift;

    # …read configuration and do other stuff…

    (ref $app)->attr(
        db => sub {
            DBI->connect("dbi:Pg:database=$db", $user, $pass)
        }
    );
}

This adds a "db" attribute to my application class (represented here by the $app object), which can be accessed as "$app->db". The first time it is accessed, the function I provided is called, and its return value is cached and returned as the value of the attribute thereafter. In this case, my sub is a closure that uses the $db, $user, and $pass variables read from the configuration file, and connects to a Postgres database using DBI/DBD::Pg, but those are just details. You could do the same thing with DBIx::Connector or a similar module.

(Aside: don't just create a database handle in startup. It will break if you run your app under a preforking server like Hypnotoad. Your startup will be called once before forking, but the DBI handle can't be shared between the server processes. The delayed attribute initialisation is perfect in this case, because each server will have its own handle.)

With this code, the database connection is created only the first time it is needed, and it is kept around as long as the application is alive (how long that is depends on which server your application runs under). Controllers can just do "$self->app->db" to fetch a db handle at any time without worrying about any of this.

Marcus Ramberg pointed out on IRC that I could also use "$app->helper(db => sub {...})" to make the handle accessible from controllers (as "$self->db") and from epl templates (as "db"). I don't need to do that for database handles, but someone else might find it useful.

(This is an old post that I forgot to submit when it was written.)

Mojolicious session cookies

Mojolicious comes with easy-to-use cryptographically signed session cookies out of the box.

Read more…

Exceptions during Mojolicious testing

I've been using Test::Mojo to write tests for my Mojolicious applications, and it's great.

my $t = Test::Mojo->new(app => "Wigeon");
$t->post_form_ok('/login', {__login => $login, __passwd => $password})
    ->status_is(200)
    ->content_type_is("text/html")
    ->content_like(qr/Welcome/);
$t->get_ok('/users-only')
    ->status_is(200)
    ->content_type_is("text/html")
    ->text_is('html body p', 'First paragraph');

When something breaks (i.e., an unexpected 404 or 500 response), however, Mojolicious renders the built-in not_found or exception template, and I see the whole HTML/CSS page in my test results. Usually many times, as one failure precipitates others.

I fixed this by loading Mojolicious::Controller early in my application class and overriding its render_not_found and render_exception methods.

use Mojolicious::Controller;

...

{
    no warnings 'redefine';
    package Mojolicious::Controller;

    sub render_not_found {
        my $self = shift;
        $self->render(
            status => 404, format => 'txt',
            text => "Not found: " . $self->req->url->path
        );
    }

    sub render_exception {
        my ($self, $e) = @_;
        return if $self->stash->{'mojo.exception'};
        $self->render(
            status => 500, format => 'txt',
            handler => undef, layout => undef, extends => undef,
            text => Mojo::Exception->new($e)->message,
            "mojo.exception" => 1
        );
    }
}

Soon after I grumbled about having to do this on IRC, Sebastian Riedel added experimental support for mode-specific exception templates. One can now create stripped-down not_found.testing and exception.testing templates, and they will be automatically detected and used.

Returning text/plain responses in Mojolicious

The Mojolicious documentation gave me the impression that the way to send a text/plain response was just:

$self->render_text("Plain text content");

I wrote a content_type_is test for such a response the other day, and its failure made me notice that the type was actually text/html. I asked about it on #mojo, and learned that render_text is just the opposite of render_data, and the function name has nothing to do with the content type of the response.

The incorrect examples at the very beginning of the rendering guide have been fixed, and the description of render_text has been changed. It used to say "Render the given content as plain text, note that text will be encoded" and now it says "Render the given content as Perl characters, which will be encoded to bytes".

Given the misleading examples present earlier, I wish the description had also said something like "The default content type of the response is text/html, and this function does not change it unless you specify a different format". Anyway, the correct way to send a text/plain response is to specify the format explicitly as text:

$self->render_text("Plain text content", format => 'txt');

Mojolicious and X-Powered-By

A few days ago, I discovered that Mojolicious::Plugin::PoweredBy is enabled by default, and adds an "X-Powered-By: Mojolicious (Perl)" header to all HTTP responses. Although I can change the value of the header, there is no way to suppress it (e.g. by setting the value to undef). Since X-headers are meant for private experiments, and should not be exposed to the Internet (never mind how many people do it anyway), I thought this was poor behaviour.

The solution was to bundle an empty PoweredBy plugin with my app:

package Mojolicious::Plugin::PoweredBy;

use base 'Mojolicious::Plugin';

sub register {
}

1;

I brought this up in #mojo on IRC, suggesting tactfully that the plugin should either not be enabled by default, or be easier to disable (when what I really wanted was to suggest it be removed entirely). The Mojo author disagreed, and said that nobody would enable it if it wasn't enabled by default (which is quite true, and to me suggests that it should not exist at all), and that it was "advertising" for Mojo.

It may be advertising, but I'm not sure it sends the right message.