perlmojoliciousplack

how to pass arguments from an plack app to an mojolicious app enabled in builder?


given the example plack app using lots of middleware components and an mojolicious app enabled in builder (see below), how can i pass parameters from the app.psgi to Mojolicious without using the ugly %ENV hack shown? of cause passing an config is just an example, this could be any scalar/object.

app.psgi

use Plack::Builder;

$ENV{CONFIG} = {...};

builder {
    ...
    Mojolicious::Commands->start_app('MyApp');
};

MyApp.pm

package MyApp;

use Mojo::Base 'Mojolicious';

sub startup {

    my $self = shift;
    my $r = $self->routes;

    $self->config( $ENV{CONFIG} );

    $r->route('/')->to('home#');        
}

Solution

  • This is an interesting question and is most easily tackled by looking at the source. In your example you rightly use

    Mojolicious::Commands->start_app('MyApp');
    

    Looking at the source shows that start_app is a rather simple wrapper:

    sub start_app {
      my $self = shift;
      return Mojo::Server->new->build_app(shift)->start(@_);
    }
    

    It turns out that build_app is as well:

    sub build_app {
      my ($self, $app) = @_;
      local $ENV{MOJO_EXE};
      return $app->new unless my $e = Mojo::Loader->new->load($app);
      die ref $e ? $e : qq{Couldn't find application class "$app".\n};
    }
    

    returning a new instance of your app's class. The Mojolicious class's new function is more involved, but in the end, it just calls the familiar startup method and returns the instance.

    This means that you cannot easily pass arguments to the startup method from your middleware wrapper, used in the standard way. I can think of two mechanisms to accomplish what you want to do: 1) write your own build_app function to replace the server's method but which passes arguments to $app->new (which would get passed to startup in turn) or 2) write your own start_app function which could call another startup-like function.

    # in MyApp.pm
    
    sub startup {
      ... # as before
    }
    
    sub after_startup {
      ... # your new code here,
          # or even most of what was in `startup` before
    }
    

    and

    # app.psgi
    
    builder {
      ...
      my $app = Mojo::Server->new->build_app(shift);
      $app->after_startup(@your_args_here);
      $app->start(@_);
    }