Search code examples
perlplack

Plack::Builder - last line isn't using mount - error message


Having the next simple Plack app:

use strict;
use warnings;
use Plack::Builder;

my $app = sub { 
        return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] ]; 
};

builder {
    foreach my $act ( qw( /some/aa /another/bb  / ) ) {
        mount $act => $app;
    }
};

return's error:

WARNING: You used mount() in a builder block, but the last line (app) isn't using mount().
WARNING: This causes all mount() mappings to be ignored.
 at /private/tmp/test.psgi line 13.
Error while loading /private/tmp/test.psgi: to_app() is called without mount(). No application to build. at /private/tmp/test.psgi line 13.

but the next builder block is OK.

builder {
    foreach my $act ( qw( /some/aa /another/bb  / ) ) {
        mount $act => $app;
    }
    mount "/" => $app;
};

I understand than the Plack::Builder manual says

NOTE: Once you use mount in your builder code, you have to use mount for all the paths, including the root path (/).

But in the for loop i have the / mount as last one: qw( /some/aa /another/bb / ), so something is here behind the scene.

Can anybody explain, please?


Solution

  • A look at the source code help to understand what is going on:

    sub builder(&) {
        my $block = shift;
        ...
        my $app = $block->();
    
        if ($mount_is_called) {
            if ($app ne $urlmap) {
                Carp::carp("WARNING: You used mount() in a builder block,
    

    So, builder is just a subroutine and its argument is a code block. That code block is evaluated and the result ends up in $app. With your code, however, the result of the evaluation is the empty string which results from the terminating foreach loop:

    $ perl -MData::Dumper -e 'sub test{ for("a", "b"){ $_ } }; print Dumper(test())'
    $VAR1 = '';
    

    Since mount foo => $bar is "just" syntactic sugar that even gets hard to read in cases like yours, I suggest you move a tiny step towards the bare metal, skip the syntactic sugar and use Plack::App::URLMap directly:

    use strict;
    use warnings;
    use Plack::App::URLMap;
    
    my $app = sub { 
        return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] ]; 
    };
    
    my $map = Plack::App::URLMap->new;
    
    foreach my $act ( qw( /some/aa /another/bb  / ) ) {
        $map->mount( $act => $app );
    }
    
    $map->to_app;