routesperlmojolicious-lite

How to declare a route path with an optional placeholder in Mojolicious::Lite?


I need to declare a route that could treat two kinds of requests:

GET /api/vote/:value 
GET /api/vote/:value/:date

The way I treat the data is not that different in both cases, so I would like them to be defined in the same place.

What I did for now is that I used a wilcard placeholder :

use Mojolicious::Lite -signatures;
use DateTime;

get '/vote/*values' => sub ($c) {
    my $datetime = DateTime->now;
    # Then we process the route parameters
    my @params = split('/', $c->param('values'), 2);
     # Optional if no date provided in the route
    if ( defined $params[1] ) {
        my @dates_params = split('-', $params[1], 3);
        my %datetime = (
            year  => $dates_params[0],
            month => $dates_params[1],
            day   => $dates_params[2],
        );
        $datetime =  DateTime->new(%datetime);
    }
    my %vote = (
        value => $params[0],
        datetime => $datetime,
    );
    # ... More code here
    $c->render(json => $data);
}

It works fine this way, but I don't feel very comfortable with this solution because it is not very explicit nor readable, and I know other frameworks allows the possibility of doing something similar in a way that seems more satisfactory.

E.g., the following, using the Express.js routing capabilities, allows to reach /vote/123 and /vote/123/2019-01-13 in only one route declaration.

router.get('/vote/:value/:date*?', function(req, res, next) {
    // Processing here...
    res.json(// etc.);
});

Note: this example comes from this SO question.

After trying various possibilities with Mojolicious::Lite, I found out regular expressions in Mojolicious routes are not a thing and the best solution I found was the wildcard placeholder. Am I missing something?


Solution

  • % mojo generate lite_app optional
    ...
    % $EDITOR optional
    

    One could mark the date as optional by giving it a default value of undef:

    #!/usr/bin/env perl
    use Mojolicious::Lite -signatures;
    
    get '/vote/:value/*when' => { when => undef } => sub ($c) {
        my $value = $c->stash('value');
        my $when  = $c->stash('when');
        $c->render(
            format => 'txt',
            text   => $value . ' ' . ( defined $when ? $when : 'nope' ) . "\n"
        );
    };
    
    app->start;
    

    which then allows with-or-without the date queries:

    % ./optional get /vote/42 2>/dev/null
    42 nope
    % ./optional get /vote/42/2020/01/07 2>/dev/null
    42 2020/01/07
    %