The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Forward::Routes provides the ability to automatically detect a URLs format and route depending on this format.

By default, format detection is turned off, so using a placeholder, you get full file names back:

    my $routes = Forward::Routes->new;
    $routes->add_route('hello/:file_name');

    # pass method and path to match method
    $routes->match(get => '/my_first_file.html');
    # $m->[0]->params is {file_name => my_first_file.html}

    $routes->match(get => '/my_first_file.jpeg');
    # $m->[0]->params is {file_name => my_first_file.jpeg}

Turning on format detection has two effects:
- only paths with the required format match
- the format is then saved in the match object

    my $routes = Forward::Routes->new;
    $routes->add_route('hello/:last_name')->format('html');

    $routes->match(get => '/hello/smith.html');
    # $m->[0]->params is {last_name => 'smith', format => 'html'}

    $routes->match(get => '/hello/foo.jpeg');
    # $m is undef (no match)

Format detection allows to route to different actions in different controllers based on the format.

    my $routes = Forward::Routes->new;
    $routes->add_route('hello/:file_name')->format('html')->to('Foo#bar');
    $routes->add_route('hello/:file_name')->format('xml')->to('My#xml');

    $routes->match(get => '/hello/my_first_file.html');
    # $m->[0]->params is {file_name => my_first_file, format => 'html', controller => 'Foo', action => 'bar'}

    $routes->match(get => '/hello/my_first_file.xml');
    # $m->[0]->params is {file_name => my_first_file, format => 'xml', controller => 'My', action => 'xml'}

You can also make use of nested routes. All child routes inherit the format constraint of their parent.

    my $routes = Forward::Routes->new;
    my $html_route = $routes->add_route->format('html');

    $html_route->add_route('/foo');
    $html_route->add_route('/baz');

    $m = $root->match(get => 'foo.html');
    # $m->[0]->params is {format => 'html'};

    $m = $root->match(get => 'baz.html');
    # $m->[0]->params is {format => 'html'};

    $m = $root->match(get => '/foo');
    # $m is undef

Or you can define a pattern first and restrict formats later (via nested routes):

    my $routes = Forward::Routes->new;
    my $child = $routes->add_route('/hello');

    $child->add_route->format('html')->to('HTMLController#html_action');
    $child->add_route->format('xml')->to('XMLController#xml_action');

    $m = $root->match(get => '/hello.html');
    # $m->[0]->params is {format => 'html', controller => 'HTMLController', action => 'html_action'};

    $m = $root->match(get => 'hello.xml');
    # $m->[0]->params is {format => 'xml', controller => 'XMLController', action => 'xml_action'};

Or just define a base format and override formats for deeper routes:

    my $routes = Forward::Routes->new;
    my $html_route = $routes->add_route('hello')->format('html');

    $html_route->add_route('/foo');
    $html_route->add_route('/baz')->format('xml');
    my $bar_route = $html_route->add_route('/bar');
        my $bar_jpeg_route = $bar_route->add_route->format('jpeg');
            $bar_jpeg_route->add_route('first');
            $bar_jpeg_route->add_route('second')->format('gif');

    
    $m = $root->match(get => '/hello/foo.html');
    # $m->[0]->params is {format => 'html'};

    $m = $root->match(get => '/hello/baz.html');
    # $m is undef

    $m = $root->match(get => '/hello/baz.xml');
    # $m->[0]->params is {format => 'xml'};

    $m = $root->match(get => '/hello/bar.html');
    # $m is undef

    $m = $root->match(get => '/hello/bar/first.jpeg');
    # $m->[0]->params is {format => 'jpeg'};

    $m = $root->match(get => '/hello/bar/second.gif');
    # $m->[0]->params is {format => 'gif'};



You can also pass empty if you do not want a format beeing accepted:

    my $routes = Forward::Routes->new;
    $routes->add_route('/hello/foo')->format('');
    $routes->add_route('/hello/:file_name')->format('');

    $m = $r->match(get => 'hello/there.html');
    # $m->[0] is undef

    $m = $r->match(get => 'hello/there');
    # $m->[0]->params is {file_name => 'there', format => ''}

    $m = $r->match(get => 'hello/foo.html');
    # $m->[0] is undef

    $m = $r->match(get => 'hello/foo');
    # $m->[0]->params is {format => ''}


Finally, you can also pass more than one format constraint:

    my $routes = Forward::Routes->new;
    $routes->add_route('foo/:bar')->format('html', 'xml');

    $m = $r->match(get => 'foo/there.html');
    # $m->[0]->params is {bar => 'there', format => 'html'}

    $m = $r->match(get => 'foo/there.xml');
    # $m->[0]->params is {bar => 'there', format => 'xml'}

    $m = $r->match(get => 'foo/there.jpeg');
    # $m is undef